Skip to content

Commit 8c598e7

Browse files
committed
Replace opaque type with CStruct0
Add comment line to incomplete types
1 parent 705e165 commit 8c598e7

File tree

14 files changed

+189
-34
lines changed

14 files changed

+189
-34
lines changed

bindgen/Utils.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,20 @@ template <typename T> static inline bool isAliasForType(Type *type) {
126126
return false;
127127
}
128128

129+
/**
130+
* @return true if typedef references opaque type directly or through a
131+
* chain of typedefs.
132+
*/
133+
static inline bool isAliasForOpaqueType(const Type *type) {
134+
assert(type);
135+
auto *typeDef = dynamic_cast<const TypeDef *>(type);
136+
if (typeDef) {
137+
if (!typeDef->getType()) {
138+
return true;
139+
}
140+
return isAliasForOpaqueType(typeDef->getType().get());
141+
}
142+
return false;
143+
}
144+
129145
#endif // UTILS_H

bindgen/defines/DefineFinder.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@ void DefineFinder::MacroUndefined(const clang::Token &macroNameTok,
108108
return;
109109
}
110110
clang::SourceManager &sm = compiler.getSourceManager();
111-
if (!sm.isInMainFile(undef->getLocation())) {
112-
return;
113-
}
114111
if (sm.isWrittenInMainFile(macroNameTok.getLocation()) &&
115112
md.getMacroInfo() && !md.getMacroInfo()->isFunctionLike()) {
116113
std::string macroName = macroNameTok.getIdentifierInfo()->getName();

bindgen/ir/Function.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ bool Function::isLegalScalaNativeFunction() const {
7272
/* Return type and parameters types cannot be array types because array type
7373
* in this case is always represented as a pointer to element type */
7474
if (isAliasForType<Struct>(retType.get()) ||
75-
isAliasForType<Union>(retType.get())) {
75+
isAliasForType<Union>(retType.get()) ||
76+
isAliasForOpaqueType(retType.get())) {
7677
return false;
7778
}
7879
for (const auto &parameter : parameters) {
7980
if (isAliasForType<Struct>(parameter->getType().get()) ||
80-
isAliasForType<Union>(parameter->getType().get())) {
81+
isAliasForType<Union>(parameter->getType().get()) ||
82+
isAliasForOpaqueType(parameter->getType().get())) {
8183
return false;
8284
}
8385
}

bindgen/ir/IR.cpp

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -102,40 +102,61 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) {
102102

103103
std::string objectName = handleReservedWords(ir.objectName);
104104

105-
if (!ir.libObjEmpty()) {
105+
bool isLibObjectEmpty = ir.libObjEmpty();
106+
107+
if (!isLibObjectEmpty) {
106108
if (!ir.linkName.empty()) {
107109
s << "@native.link(\"" << ir.linkName << "\")\n";
108110
}
109111

110112
s << "@native.extern\n"
111113
<< "object " << objectName << " {\n";
114+
}
112115

113-
for (const auto &typeDef : ir.typeDefs) {
114-
if (ir.shouldOutput(typeDef)) {
115-
s << *typeDef;
116-
}
116+
for (const auto &typeDef : ir.typeDefs) {
117+
if (ir.shouldOutput(typeDef)) {
118+
s << *typeDef;
119+
} else if (isAliasForOpaqueType(typeDef.get()) &&
120+
ir.inMainFile(*typeDef)) {
121+
llvm::errs() << "Warning: type alias " + typeDef->getName()
122+
<< " is skipped because it is an unused alias for "
123+
"incomplete type."
124+
<< "\n";
125+
llvm::errs().flush();
117126
}
127+
}
118128

119-
for (const auto &variable : ir.variables) {
129+
for (const auto &variable : ir.variables) {
130+
if (!variable->hasIllegalUsageOfOpaqueType()) {
120131
s << *variable;
132+
} else {
133+
llvm::errs() << "Error: Variable " << variable->getName()
134+
<< " is skipped because it has incomplete type.\n";
121135
}
136+
}
122137

123-
for (const auto &varDefine : ir.varDefines) {
138+
for (const auto &varDefine : ir.varDefines) {
139+
if (!varDefine->hasIllegalUsageOfOpaqueType()) {
124140
s << *varDefine;
141+
} else {
142+
llvm::errs() << "Error: Variable alias " << varDefine->getName()
143+
<< " is skipped because it has incomplete type.\n";
144+
llvm::errs().flush();
125145
}
146+
}
126147

127-
for (const auto &func : ir.functions) {
128-
if (func->isLegalScalaNativeFunction()) {
129-
s << *func;
130-
} else {
131-
llvm::errs()
132-
<< "Warning: Function " << func->getName()
133-
<< " is skipped because Scala Native does not support "
134-
"passing structs and arrays by value.\n";
135-
llvm::errs().flush();
136-
}
148+
for (const auto &func : ir.functions) {
149+
if (!func->isLegalScalaNativeFunction()) {
150+
llvm::errs() << "Warning: Function " << func->getName()
151+
<< " is skipped because Scala Native does not support "
152+
"passing structs and arrays by value.\n";
153+
llvm::errs().flush();
154+
} else {
155+
s << *func;
137156
}
157+
}
138158

159+
if (!isLibObjectEmpty) {
139160
s << "}\n\n";
140161
}
141162

@@ -449,5 +470,18 @@ bool IR::hasOutputtedDeclaration(
449470

450471
template <typename T>
451472
bool IR::shouldOutput(const std::shared_ptr<T> &type) const {
452-
return inMainFile(*type) || isTypeUsed(type, true);
473+
if (isTypeUsed(type, true)) {
474+
return true;
475+
}
476+
if (!inMainFile(*type)) {
477+
/* remove unused types from included files */
478+
return false;
479+
}
480+
auto *typeDef = dynamic_cast<TypeDef *>(type.get());
481+
if (typeDef) {
482+
/* unused typedefs from main file are printed only if they are not
483+
* aliases for an opaque type. */
484+
return !isAliasForOpaqueType(typeDef);
485+
}
486+
return true;
453487
}

bindgen/ir/IR.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,13 @@ class IR {
149149
template <typename T> bool inMainFile(const T &type) const;
150150

151151
/**
152-
* @tparam T Type subclass
153-
* @return true if type is in main file or it is used by declaration from
154-
* main file.
152+
* @tparam T Enum, Struct, Union or TypeDef
153+
* @return true if the type will be printed.
154+
* Following types are not printed:
155+
* - Unused types from included headers
156+
* - Unused typedefs from main header if they reference an opaque
157+
* type (if such typedef is used then true is returned but error
158+
* message is printed when bindings are generated)
155159
*/
156160
template <typename T>
157161
bool shouldOutput(const std::shared_ptr<T> &type) const;

bindgen/ir/TypeDef.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ TypeDef::TypeDef(std::string name, std::shared_ptr<Type> type,
99
location(std::move(location)) {}
1010

1111
llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &typeDef) {
12-
if (!typeDef.getType()) {
13-
llvm::errs() << "Error: type definition for " << typeDef.getName()
14-
<< " was not found.\n";
15-
llvm::errs().flush();
16-
return s;
12+
s << " type " << handleReservedWords(typeDef.name) << " = ";
13+
if (typeDef.type) {
14+
s << typeDef.getType()->str();
15+
} else {
16+
s << "native.CStruct0 // incomplete type";
1717
}
18-
s << " type " + handleReservedWords(typeDef.name) + " = " +
19-
typeDef.getType()->str() + "\n";
18+
s << "\n";
2019
return s;
2120
}
2221

bindgen/ir/VarDefine.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s,
1010
<< varDefine.variable->getType()->str() << " = native.extern\n";
1111
return s;
1212
}
13+
14+
bool VarDefine::hasIllegalUsageOfOpaqueType() const {
15+
return variable->hasIllegalUsageOfOpaqueType();
16+
}

bindgen/ir/VarDefine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class VarDefine : public Define {
1414
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s,
1515
const VarDefine &varDefine);
1616

17+
bool hasIllegalUsageOfOpaqueType() const;
18+
1719
private:
1820
std::shared_ptr<Variable> variable;
1921
};

bindgen/ir/Variable.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "Variable.h"
2+
#include "../Utils.h"
23

34
Variable::Variable(const std::string &name, std::shared_ptr<Type> type)
45
: TypeAndName(name, type) {}
@@ -7,4 +8,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Variable &variable) {
78
s << " val " << variable.getName() << ": " << variable.getType()->str()
89
<< " = native.extern\n";
910
return s;
10-
}
11+
}
12+
13+
bool Variable::hasIllegalUsageOfOpaqueType() const {
14+
return isAliasForOpaqueType(type.get());
15+
}

bindgen/ir/Variable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class Variable : public TypeAndName {
1010

1111
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s,
1212
const Variable &variable);
13+
14+
bool hasIllegalUsageOfOpaqueType() const;
1315
};
1416

1517
#endif // SCALA_NATIVE_BINDGEN_VARIABLE_H

tests/samples/OpaqueTypes.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include "include/OpaqueTypes.h"
2+
13
typedef struct points points;
24

35
struct point;
@@ -21,3 +23,37 @@ struct point {
2123
int x;
2224
int y;
2325
};
26+
27+
struct undefinedStruct;
28+
29+
void usePointerToUndefinedStruct(struct undefinedStruct *);
30+
31+
struct structWithPointerToUndefinedStruct {
32+
struct undefinedStruct *field;
33+
};
34+
35+
union unionWithPointerToUndefinedStruct {
36+
struct undefinedStruct *field;
37+
};
38+
39+
typedef union undefinedUnion undefinedUnion;
40+
41+
typedef undefinedUnion *aliasToPointerOfUndefinedUnion;
42+
43+
aliasToPointerOfUndefinedUnion *fun();
44+
45+
typedef struct undefinedStruct aliasForUndefinedStruct; // okay
46+
47+
aliasForUndefinedStruct *returnPointerToAliasOfUndefinedStruct();
48+
49+
void usePointerToUndefinedIncludedStruct(undefinedIncludedStruct *);
50+
51+
typedef aliasToPointerOfUndefinedUnion (
52+
*functionPointerWithPointerToOpaqueType)(struct undefinedStruct **);
53+
54+
void useUndefinedStruct(
55+
struct undefinedStruct); // removed. Error message is printed
56+
57+
extern struct undefinedStruct removedExtern; // removed
58+
59+
#define removedExternAlias removedExtern // removed

tests/samples/OpaqueTypes.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,27 @@ import scala.scalanative.native._
66
@native.link("bindgentests")
77
@native.extern
88
object OpaqueTypes {
9+
type struct_undefinedIncludedStruct = native.CStruct0 // incomplete type
10+
type undefinedIncludedStruct = struct_undefinedIncludedStruct
911
type struct_points = native.CStruct2[native.Ptr[struct_point], native.Ptr[struct_point]]
1012
type points = struct_points
1113
type struct_point = native.CStruct2[native.CInt, native.CInt]
1214
type union_u = native.CArray[Byte, native.Nat._4]
1315
type u = union_u
16+
type struct_undefinedStruct = native.CStruct0 // incomplete type
17+
type struct_structWithPointerToUndefinedStruct = native.CStruct1[native.Ptr[struct_undefinedStruct]]
18+
type union_unionWithPointerToUndefinedStruct = native.CArray[Byte, native.Nat._8]
19+
type union_undefinedUnion = native.CStruct0 // incomplete type
20+
type undefinedUnion = union_undefinedUnion
21+
type aliasToPointerOfUndefinedUnion = native.Ptr[undefinedUnion]
22+
type aliasForUndefinedStruct = struct_undefinedStruct
23+
type functionPointerWithPointerToOpaqueType = native.CFunctionPtr1[native.Ptr[native.Ptr[struct_undefinedStruct]], native.Ptr[undefinedUnion]]
1424
def move(point: native.Ptr[struct_point], x: native.CInt, y: native.CInt): native.Ptr[struct_point] = native.extern
1525
def processPoints(p: native.Ptr[points]): native.Ptr[union_u] = native.extern
26+
def usePointerToUndefinedStruct(anonymous0: native.Ptr[struct_undefinedStruct]): Unit = native.extern
27+
def fun(): native.Ptr[native.Ptr[undefinedUnion]] = native.extern
28+
def returnPointerToAliasOfUndefinedStruct(): native.Ptr[aliasForUndefinedStruct] = native.extern
29+
def usePointerToUndefinedIncludedStruct(anonymous0: native.Ptr[undefinedIncludedStruct]): Unit = native.extern
1630
}
1731

1832
import OpaqueTypes._
@@ -37,10 +51,22 @@ object OpaqueTypesHelpers {
3751

3852
def struct_point()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point]
3953

54+
implicit class struct_structWithPointerToUndefinedStruct_ops(val p: native.Ptr[struct_structWithPointerToUndefinedStruct]) extends AnyVal {
55+
def field: native.Ptr[struct_undefinedStruct] = !p._1
56+
def field_=(value: native.Ptr[struct_undefinedStruct]): Unit = !p._1 = value
57+
}
58+
59+
def struct_structWithPointerToUndefinedStruct()(implicit z: native.Zone): native.Ptr[struct_structWithPointerToUndefinedStruct] = native.alloc[struct_structWithPointerToUndefinedStruct]
60+
4061
implicit class union_u_pos(val p: native.Ptr[union_u]) extends AnyVal {
4162
def i: native.Ptr[native.CInt] = p.cast[native.Ptr[native.CInt]]
4263
def i_=(value: native.CInt): Unit = !p.cast[native.Ptr[native.CInt]] = value
4364
def f: native.Ptr[native.CFloat] = p.cast[native.Ptr[native.CFloat]]
4465
def f_=(value: native.CFloat): Unit = !p.cast[native.Ptr[native.CFloat]] = value
4566
}
67+
68+
implicit class union_unionWithPointerToUndefinedStruct_pos(val p: native.Ptr[union_unionWithPointerToUndefinedStruct]) extends AnyVal {
69+
def field: native.Ptr[native.Ptr[struct_undefinedStruct]] = p.cast[native.Ptr[native.Ptr[struct_undefinedStruct]]]
70+
def field_=(value: native.Ptr[struct_undefinedStruct]): Unit = !p.cast[native.Ptr[native.Ptr[struct_undefinedStruct]]] = value
71+
}
4672
}

tests/samples/include/OpaqueTypes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
struct s;
2+
3+
extern struct s externVar; // removed. No warning printed
4+
5+
typedef struct undefinedIncludedStruct undefinedIncludedStruct;
6+
7+
void useUndefinedIncludedStruct(undefinedIncludedStruct);

tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,26 @@ class BindgenReportingSpec extends FunSpec {
5858
|Warning: Function returnUnion is skipped because Scala Native does not support passing structs and arrays by value.""".stripMargin
5959
)
6060
}
61+
62+
it("Skips variable with opaque type") {
63+
val bindings =
64+
bindgen(input = """struct undefinedStruct;
65+
|extern struct undefinedStruct removedExtern;
66+
|#define removedExternAlias removedExtern
67+
|""".stripMargin)
68+
assert(
69+
bindings.errs == """Error: Variable removedExtern is skipped because it has incomplete type.
70+
|Error: Variable alias removedExternAlias is skipped because it has incomplete type.""".stripMargin)
71+
72+
}
73+
74+
it("Skips unused alias for opaque type") {
75+
val bindings =
76+
bindgen(input = """union undefinedUnion;
77+
|typedef union undefinedUnion aliasForUndefinedUnion;
78+
|""".stripMargin)
79+
assert(
80+
bindings.errs == "Warning: type alias aliasForUndefinedUnion is skipped because it is an unused alias for incomplete type.")
81+
}
6182
}
6283
}

0 commit comments

Comments
 (0)