Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix opaque types usages #114

Merged
merged 1 commit into from
Jul 22, 2018
Merged

Fix opaque types usages #114

merged 1 commit into from
Jul 22, 2018

Conversation

kornilova203
Copy link
Member

Closes #106

Replaces pointers to opaque types with pointers to Byte

struct undefinedStruct;
struct undefinedStruct *getUndefinedStruct();
def getUndefinedStruct(): native.Ptr[Byte] = native.extern

If there exists a declaration that uses an opaque type value then the declaration is skipped and error messages are printed:

struct undefinedStruct;
extern struct undefinedStruct s;
Error: type definition for struct_undefinedStruct was not found.
Error: Variable s is skipped because it references an opaque type.

@@ -0,0 +1,7 @@
struct s;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to start with upper case: OpaqueTypes.h


bool usesOpaqueType() const override;

using TypeAndName::replaceType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeDef inherits from Type and TypeAndName, so we need to specify what version of replaceType should be used.
A typedef may contain pointer to opaque type that should be replaced with pointer to Byte:

typedef struct s *s;
type s = native.Ptr[Byte]

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[Byte]): Unit = native.extern
Copy link
Member

@jonas jonas Jul 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this I think it would be better to have

type struct_structWithPointerToUndefinedStruct = native.CStruct0
type union_unionWithPointerToUndefinedStruct = native.CArray[Byte, native.Nat._0] // not sure if 0 length is supported

def usePointerToUndefinedStruct(anonymous0: native.Ptr[struct_structWithPointerToUndefinedStruct]): Unit = native.extern

So CStruct0 for opaque structs and then use the type alias in the pointers. With default opaque representations for structs and unions it should be possible to remove most use of replaceType except for updates to structs and unions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No helper class should be generated for such types.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good solution.
Should we leave a comment beside CStruct0 that it is an incomplete type or opaque type, so no one will think that it is indeed empty structure?

@@ -23,3 +23,14 @@ bool PointerType::operator==(const Type &other) const {
}
return false;
}

bool PointerType::usesOpaqueType() const { return type->usesOpaqueType(); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A pointer to an opaque type is always sound, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but by the time this method is called, all pointers to opaque type are replaced with pointers to Byte, so this method is used to finds illegal usages of opaque type

errs == "Error: type definition for struct_undefinedStruct was not found.\n" +
"Error: Function useUndefinedStruct is skipped because it references an opaque type.")
case _ => assert(errs == "")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the tests clean, I would prefer to have checks for errors in a separate Spec.

@kornilova203
Copy link
Member Author

kornilova203 commented Jul 12, 2018

Opaque types may be only Struct or Union, they are referenced only through corresponding pointers, so it is okay to replace nullptr in these TypeDefs with CStruct0.
But then we need to check if there are cases when the type is used as value. It may happen in these situations:

  • struct/union field of opaque type. This situation is illegal, Clang prints error error: field has incomplete type but it does not prevent construction of AST tree, so Scala Code is generated.
    I don't think that we should stop bindgen in this case because them problem may be related to inaccuracy in compilation database. It should be okay to remove such damaged structures and all code that uses them.
  • struct/union field of array type with elements of opaque type. The same situation as above.
  • function pointer type that uses value of opaque type.
    Seems like it is okay. Such pointers may be passed between Scala code and native lib code. It is even possible to execute such pointer if cast it to a function pointer that uses complete type.
  • function pointer type that uses array of opaque type.
    That looks strange to me but C code does not compile if a function parameter is an array of incomplete type.
    If we simply change struct incompleteS[] to struct incompleteS * code does compile.
    I think we should not care about this much because in any case parameter of array type becomes parameter of pointer type in AST tree.
  • function uses value of opaque type. All functions that use values of structs or unions should be removed because it is not supported in Scala Native. (this is done in Skip functions that pass arrays or structs by value #119)
  • extern variable of opaque type. The variable and corresponding VarDefines may be safely removed.

@kornilova203 kornilova203 changed the base branch from master to skip-illegal-scala-functions July 12, 2018 22:44
@kornilova203
Copy link
Member Author

I decided not to remove structs with fields of opaque types because it is not normal situation, clangs prints error in this case.
Removing such structs and all declarations that use these structs will require relatively big amount of new code and it does not make a lot of sense because bindings cannot be correct if header files of compilation database is broken.

if (typeDef.type) {
s << typeDef.getType()->str();
} else {
s << "native.CStruct0 // incomplete type";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to remove the comment completely, else please change it to say: opaque type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used incomplete because it is the term that clang uses in error messages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay makes sense then.

case "LiteralDefine" =>
assert(
errs == "Warning: integer value does not fit into 8 bytes: 18446744073709551615\n" +
"Warning: integer value does not fit into 8 bytes: 9223372036854775809")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for not being more clear, what I meant by "have checks for errors in a separate Spec" was to do something like:

class BindgenReportingSpec extends FunSpec {
  it("Skip variable with opaque type") {
    val result = bindgen(
      input = "struct undefinedStruct;
              |extern struct undefinedStruct removedExtern;
              |#define removedExternAlias removedExtern
              |".stripMargin)
    assert(
      result.error == "Error: Variable alias removedExternAlias is skipped because it has incomplete type")
    )
  }
}

That is avoid mixing code with errors into the header files used by BindgenSpec and instead have focused tests.

I know it is a bit more work but I think the clarity it brings will pay off in the long run.

@kornilova203 kornilova203 changed the base branch from skip-illegal-scala-functions to 0.3 July 16, 2018 09:09
@kornilova203 kornilova203 force-pushed the fix-opaque-types-usages branch 2 times, most recently from 0048a24 to 8e88cf7 Compare July 17, 2018 17:10
@kornilova203 kornilova203 force-pushed the fix-opaque-types-usages branch 2 times, most recently from b1b7d5f to 591b889 Compare July 21, 2018 05:24
Add comment line to incomplete types
@kornilova203 kornilova203 merged commit 9eb0ba5 into 0.3 Jul 22, 2018
@kornilova203 kornilova203 deleted the fix-opaque-types-usages branch July 22, 2018 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants