From 8c598e7dae36a44f91d8587654a09d976ee55968 Mon Sep 17 00:00:00 2001 From: kornilova-l Date: Tue, 10 Jul 2018 23:13:04 +0300 Subject: [PATCH] Replace opaque type with CStruct0 Add comment line to incomplete types --- bindgen/Utils.h | 16 +++++ bindgen/defines/DefineFinder.cpp | 3 - bindgen/ir/Function.cpp | 6 +- bindgen/ir/IR.cpp | 70 ++++++++++++++----- bindgen/ir/IR.h | 10 ++- bindgen/ir/TypeDef.cpp | 13 ++-- bindgen/ir/VarDefine.cpp | 4 ++ bindgen/ir/VarDefine.h | 2 + bindgen/ir/Variable.cpp | 7 +- bindgen/ir/Variable.h | 2 + tests/samples/OpaqueTypes.h | 36 ++++++++++ tests/samples/OpaqueTypes.scala | 26 +++++++ tests/samples/include/OpaqueTypes.h | 7 ++ .../bindgen/BindgenReportingSpec.scala | 21 ++++++ 14 files changed, 189 insertions(+), 34 deletions(-) create mode 100644 tests/samples/include/OpaqueTypes.h diff --git a/bindgen/Utils.h b/bindgen/Utils.h index b3ae0fa..1a5c1d3 100644 --- a/bindgen/Utils.h +++ b/bindgen/Utils.h @@ -126,4 +126,20 @@ template static inline bool isAliasForType(Type *type) { return false; } +/** + * @return true if typedef references opaque type directly or through a + * chain of typedefs. + */ +static inline bool isAliasForOpaqueType(const Type *type) { + assert(type); + auto *typeDef = dynamic_cast(type); + if (typeDef) { + if (!typeDef->getType()) { + return true; + } + return isAliasForOpaqueType(typeDef->getType().get()); + } + return false; +} + #endif // UTILS_H diff --git a/bindgen/defines/DefineFinder.cpp b/bindgen/defines/DefineFinder.cpp index 94e7241..51db8cb 100644 --- a/bindgen/defines/DefineFinder.cpp +++ b/bindgen/defines/DefineFinder.cpp @@ -108,9 +108,6 @@ void DefineFinder::MacroUndefined(const clang::Token ¯oNameTok, return; } clang::SourceManager &sm = compiler.getSourceManager(); - if (!sm.isInMainFile(undef->getLocation())) { - return; - } if (sm.isWrittenInMainFile(macroNameTok.getLocation()) && md.getMacroInfo() && !md.getMacroInfo()->isFunctionLike()) { std::string macroName = macroNameTok.getIdentifierInfo()->getName(); diff --git a/bindgen/ir/Function.cpp b/bindgen/ir/Function.cpp index 394beee..e84ae7e 100644 --- a/bindgen/ir/Function.cpp +++ b/bindgen/ir/Function.cpp @@ -72,12 +72,14 @@ bool Function::isLegalScalaNativeFunction() const { /* Return type and parameters types cannot be array types because array type * in this case is always represented as a pointer to element type */ if (isAliasForType(retType.get()) || - isAliasForType(retType.get())) { + isAliasForType(retType.get()) || + isAliasForOpaqueType(retType.get())) { return false; } for (const auto ¶meter : parameters) { if (isAliasForType(parameter->getType().get()) || - isAliasForType(parameter->getType().get())) { + isAliasForType(parameter->getType().get()) || + isAliasForOpaqueType(parameter->getType().get())) { return false; } } diff --git a/bindgen/ir/IR.cpp b/bindgen/ir/IR.cpp index 04f5282..3b34069 100644 --- a/bindgen/ir/IR.cpp +++ b/bindgen/ir/IR.cpp @@ -102,40 +102,61 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { std::string objectName = handleReservedWords(ir.objectName); - if (!ir.libObjEmpty()) { + bool isLibObjectEmpty = ir.libObjEmpty(); + + if (!isLibObjectEmpty) { if (!ir.linkName.empty()) { s << "@native.link(\"" << ir.linkName << "\")\n"; } s << "@native.extern\n" << "object " << objectName << " {\n"; + } - for (const auto &typeDef : ir.typeDefs) { - if (ir.shouldOutput(typeDef)) { - s << *typeDef; - } + for (const auto &typeDef : ir.typeDefs) { + if (ir.shouldOutput(typeDef)) { + s << *typeDef; + } else if (isAliasForOpaqueType(typeDef.get()) && + ir.inMainFile(*typeDef)) { + llvm::errs() << "Warning: type alias " + typeDef->getName() + << " is skipped because it is an unused alias for " + "incomplete type." + << "\n"; + llvm::errs().flush(); } + } - for (const auto &variable : ir.variables) { + for (const auto &variable : ir.variables) { + if (!variable->hasIllegalUsageOfOpaqueType()) { s << *variable; + } else { + llvm::errs() << "Error: Variable " << variable->getName() + << " is skipped because it has incomplete type.\n"; } + } - for (const auto &varDefine : ir.varDefines) { + for (const auto &varDefine : ir.varDefines) { + if (!varDefine->hasIllegalUsageOfOpaqueType()) { s << *varDefine; + } else { + llvm::errs() << "Error: Variable alias " << varDefine->getName() + << " is skipped because it has incomplete type.\n"; + llvm::errs().flush(); } + } - for (const auto &func : ir.functions) { - if (func->isLegalScalaNativeFunction()) { - s << *func; - } else { - llvm::errs() - << "Warning: Function " << func->getName() - << " is skipped because Scala Native does not support " - "passing structs and arrays by value.\n"; - llvm::errs().flush(); - } + for (const auto &func : ir.functions) { + if (!func->isLegalScalaNativeFunction()) { + llvm::errs() << "Warning: Function " << func->getName() + << " is skipped because Scala Native does not support " + "passing structs and arrays by value.\n"; + llvm::errs().flush(); + } else { + s << *func; } + } + if (!isLibObjectEmpty) { s << "}\n\n"; } @@ -449,5 +470,18 @@ bool IR::hasOutputtedDeclaration( template bool IR::shouldOutput(const std::shared_ptr &type) const { - return inMainFile(*type) || isTypeUsed(type, true); + if (isTypeUsed(type, true)) { + return true; + } + if (!inMainFile(*type)) { + /* remove unused types from included files */ + return false; + } + auto *typeDef = dynamic_cast(type.get()); + if (typeDef) { + /* unused typedefs from main file are printed only if they are not + * aliases for an opaque type. */ + return !isAliasForOpaqueType(typeDef); + } + return true; } diff --git a/bindgen/ir/IR.h b/bindgen/ir/IR.h index da0935c..38cf5c6 100644 --- a/bindgen/ir/IR.h +++ b/bindgen/ir/IR.h @@ -149,9 +149,13 @@ class IR { template bool inMainFile(const T &type) const; /** - * @tparam T Type subclass - * @return true if type is in main file or it is used by declaration from - * main file. + * @tparam T Enum, Struct, Union or TypeDef + * @return true if the type will be printed. + * Following types are not printed: + * - Unused types from included headers + * - Unused typedefs from main header if they reference an opaque + * type (if such typedef is used then true is returned but error + * message is printed when bindings are generated) */ template bool shouldOutput(const std::shared_ptr &type) const; diff --git a/bindgen/ir/TypeDef.cpp b/bindgen/ir/TypeDef.cpp index 6bbafe8..91edaa7 100644 --- a/bindgen/ir/TypeDef.cpp +++ b/bindgen/ir/TypeDef.cpp @@ -9,14 +9,13 @@ TypeDef::TypeDef(std::string name, std::shared_ptr type, location(std::move(location)) {} llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &typeDef) { - if (!typeDef.getType()) { - llvm::errs() << "Error: type definition for " << typeDef.getName() - << " was not found.\n"; - llvm::errs().flush(); - return s; + s << " type " << handleReservedWords(typeDef.name) << " = "; + if (typeDef.type) { + s << typeDef.getType()->str(); + } else { + s << "native.CStruct0 // incomplete type"; } - s << " type " + handleReservedWords(typeDef.name) + " = " + - typeDef.getType()->str() + "\n"; + s << "\n"; return s; } diff --git a/bindgen/ir/VarDefine.cpp b/bindgen/ir/VarDefine.cpp index c9f5719..807cb78 100644 --- a/bindgen/ir/VarDefine.cpp +++ b/bindgen/ir/VarDefine.cpp @@ -10,3 +10,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, << varDefine.variable->getType()->str() << " = native.extern\n"; return s; } + +bool VarDefine::hasIllegalUsageOfOpaqueType() const { + return variable->hasIllegalUsageOfOpaqueType(); +} diff --git a/bindgen/ir/VarDefine.h b/bindgen/ir/VarDefine.h index f988dcc..2c736a4 100644 --- a/bindgen/ir/VarDefine.h +++ b/bindgen/ir/VarDefine.h @@ -14,6 +14,8 @@ class VarDefine : public Define { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const VarDefine &varDefine); + bool hasIllegalUsageOfOpaqueType() const; + private: std::shared_ptr variable; }; diff --git a/bindgen/ir/Variable.cpp b/bindgen/ir/Variable.cpp index 22c01b8..16433d1 100644 --- a/bindgen/ir/Variable.cpp +++ b/bindgen/ir/Variable.cpp @@ -1,4 +1,5 @@ #include "Variable.h" +#include "../Utils.h" Variable::Variable(const std::string &name, std::shared_ptr type) : TypeAndName(name, type) {} @@ -7,4 +8,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Variable &variable) { s << " val " << variable.getName() << ": " << variable.getType()->str() << " = native.extern\n"; return s; -} \ No newline at end of file +} + +bool Variable::hasIllegalUsageOfOpaqueType() const { + return isAliasForOpaqueType(type.get()); +} diff --git a/bindgen/ir/Variable.h b/bindgen/ir/Variable.h index 70071c7..54ced06 100644 --- a/bindgen/ir/Variable.h +++ b/bindgen/ir/Variable.h @@ -10,6 +10,8 @@ class Variable : public TypeAndName { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Variable &variable); + + bool hasIllegalUsageOfOpaqueType() const; }; #endif // SCALA_NATIVE_BINDGEN_VARIABLE_H diff --git a/tests/samples/OpaqueTypes.h b/tests/samples/OpaqueTypes.h index 4264fe7..683b92c 100644 --- a/tests/samples/OpaqueTypes.h +++ b/tests/samples/OpaqueTypes.h @@ -1,3 +1,5 @@ +#include "include/OpaqueTypes.h" + typedef struct points points; struct point; @@ -21,3 +23,37 @@ struct point { int x; int y; }; + +struct undefinedStruct; + +void usePointerToUndefinedStruct(struct undefinedStruct *); + +struct structWithPointerToUndefinedStruct { + struct undefinedStruct *field; +}; + +union unionWithPointerToUndefinedStruct { + struct undefinedStruct *field; +}; + +typedef union undefinedUnion undefinedUnion; + +typedef undefinedUnion *aliasToPointerOfUndefinedUnion; + +aliasToPointerOfUndefinedUnion *fun(); + +typedef struct undefinedStruct aliasForUndefinedStruct; // okay + +aliasForUndefinedStruct *returnPointerToAliasOfUndefinedStruct(); + +void usePointerToUndefinedIncludedStruct(undefinedIncludedStruct *); + +typedef aliasToPointerOfUndefinedUnion ( + *functionPointerWithPointerToOpaqueType)(struct undefinedStruct **); + +void useUndefinedStruct( + struct undefinedStruct); // removed. Error message is printed + +extern struct undefinedStruct removedExtern; // removed + +#define removedExternAlias removedExtern // removed diff --git a/tests/samples/OpaqueTypes.scala b/tests/samples/OpaqueTypes.scala index b5a32b0..5c181a8 100644 --- a/tests/samples/OpaqueTypes.scala +++ b/tests/samples/OpaqueTypes.scala @@ -6,13 +6,27 @@ import scala.scalanative.native._ @native.link("bindgentests") @native.extern object OpaqueTypes { + type struct_undefinedIncludedStruct = native.CStruct0 // incomplete type + type undefinedIncludedStruct = struct_undefinedIncludedStruct type struct_points = native.CStruct2[native.Ptr[struct_point], native.Ptr[struct_point]] type points = struct_points type struct_point = native.CStruct2[native.CInt, native.CInt] type union_u = native.CArray[Byte, native.Nat._4] type u = union_u + type struct_undefinedStruct = native.CStruct0 // incomplete type + type struct_structWithPointerToUndefinedStruct = native.CStruct1[native.Ptr[struct_undefinedStruct]] + type union_unionWithPointerToUndefinedStruct = native.CArray[Byte, native.Nat._8] + type union_undefinedUnion = native.CStruct0 // incomplete type + type undefinedUnion = union_undefinedUnion + type aliasToPointerOfUndefinedUnion = native.Ptr[undefinedUnion] + type aliasForUndefinedStruct = struct_undefinedStruct + type functionPointerWithPointerToOpaqueType = native.CFunctionPtr1[native.Ptr[native.Ptr[struct_undefinedStruct]], native.Ptr[undefinedUnion]] def move(point: native.Ptr[struct_point], x: native.CInt, y: native.CInt): native.Ptr[struct_point] = native.extern def processPoints(p: native.Ptr[points]): native.Ptr[union_u] = native.extern + def usePointerToUndefinedStruct(anonymous0: native.Ptr[struct_undefinedStruct]): Unit = native.extern + def fun(): native.Ptr[native.Ptr[undefinedUnion]] = native.extern + def returnPointerToAliasOfUndefinedStruct(): native.Ptr[aliasForUndefinedStruct] = native.extern + def usePointerToUndefinedIncludedStruct(anonymous0: native.Ptr[undefinedIncludedStruct]): Unit = native.extern } import OpaqueTypes._ @@ -37,10 +51,22 @@ object OpaqueTypesHelpers { def struct_point()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point] + implicit class struct_structWithPointerToUndefinedStruct_ops(val p: native.Ptr[struct_structWithPointerToUndefinedStruct]) extends AnyVal { + def field: native.Ptr[struct_undefinedStruct] = !p._1 + def field_=(value: native.Ptr[struct_undefinedStruct]): Unit = !p._1 = value + } + + def struct_structWithPointerToUndefinedStruct()(implicit z: native.Zone): native.Ptr[struct_structWithPointerToUndefinedStruct] = native.alloc[struct_structWithPointerToUndefinedStruct] + implicit class union_u_pos(val p: native.Ptr[union_u]) extends AnyVal { def i: native.Ptr[native.CInt] = p.cast[native.Ptr[native.CInt]] def i_=(value: native.CInt): Unit = !p.cast[native.Ptr[native.CInt]] = value def f: native.Ptr[native.CFloat] = p.cast[native.Ptr[native.CFloat]] def f_=(value: native.CFloat): Unit = !p.cast[native.Ptr[native.CFloat]] = value } + + implicit class union_unionWithPointerToUndefinedStruct_pos(val p: native.Ptr[union_unionWithPointerToUndefinedStruct]) extends AnyVal { + def field: native.Ptr[native.Ptr[struct_undefinedStruct]] = p.cast[native.Ptr[native.Ptr[struct_undefinedStruct]]] + def field_=(value: native.Ptr[struct_undefinedStruct]): Unit = !p.cast[native.Ptr[native.Ptr[struct_undefinedStruct]]] = value + } } diff --git a/tests/samples/include/OpaqueTypes.h b/tests/samples/include/OpaqueTypes.h new file mode 100644 index 0000000..498f26d --- /dev/null +++ b/tests/samples/include/OpaqueTypes.h @@ -0,0 +1,7 @@ +struct s; + +extern struct s externVar; // removed. No warning printed + +typedef struct undefinedIncludedStruct undefinedIncludedStruct; + +void useUndefinedIncludedStruct(undefinedIncludedStruct); diff --git a/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala b/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala index e7d2cb7..cca5df4 100644 --- a/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala +++ b/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala @@ -58,5 +58,26 @@ class BindgenReportingSpec extends FunSpec { |Warning: Function returnUnion is skipped because Scala Native does not support passing structs and arrays by value.""".stripMargin ) } + + it("Skips variable with opaque type") { + val bindings = + bindgen(input = """struct undefinedStruct; + |extern struct undefinedStruct removedExtern; + |#define removedExternAlias removedExtern + |""".stripMargin) + assert( + bindings.errs == """Error: Variable removedExtern is skipped because it has incomplete type. + |Error: Variable alias removedExternAlias is skipped because it has incomplete type.""".stripMargin) + + } + + it("Skips unused alias for opaque type") { + val bindings = + bindgen(input = """union undefinedUnion; + |typedef union undefinedUnion aliasForUndefinedUnion; + |""".stripMargin) + assert( + bindings.errs == "Warning: type alias aliasForUndefinedUnion is skipped because it is an unused alias for incomplete type.") + } } }