Skip to content

[cxx-interop] Ban ObjCBool from being substituted into C++ templates #74790

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

Merged
merged 1 commit into from
Jul 4, 2024

Conversation

egorzhdan
Copy link
Contributor

This fixes a compiler crash when calling a templated C++ function with a parameter of type ObjCBool from Swift. The compiler now emits an error for this.

Previously the following would happen:

  1. Swift starts to emit SILGen for a call expression takeTAsConstRef(ObjCBool(true)).
  2. takeTAsConstRef is a templated C++ function, so Swift asks Clang to instantiate a takeTAsConstRef with a built-in Obj-C boolean type.
  3. Swift gets an instantiated function template back that looks like this: void takeTAsConstRef(_Bool t) { ... }.
  4. Swift's ClangImporter begins to import that instantiated function.
  5. Since the parameter type is not spelled as BOOL, the parameter is not imported as ObjCBool. Instead, it's imported as regular Swift Bool.
  6. Swift realizes that the types don't match (ObjCBool vs Bool), tries to apply any of the known implicit conversions, fails to do so, crashes.

rdar://130424969

@egorzhdan egorzhdan added the c++ interop Feature: Interoperability with C++ label Jun 27, 2024
@egorzhdan egorzhdan requested a review from Xazax-hun June 27, 2024 17:38
@egorzhdan
Copy link
Contributor Author

@swift-ci please test

This fixes a compiler crash when calling a templated C++ function with a parameter of type `ObjCBool` from Swift. The compiler now emits an error for this.

Previously the following would happen:
1. Swift starts to emit SILGen for a call expression `takeTAsConstRef(ObjCBool(true))`.
2. `takeTAsConstRef` is a templated C++ function, so Swift asks Clang to instantiate a `takeTAsConstRef` with a built-in Obj-C boolean type.
3. Swift gets an instantiated function template back that looks like this: `void takeTAsConstRef(_Bool t) { ... }`.
4. Swift's ClangImporter begins to import that instantiated function.
5. Since the parameter type is not spelled as `BOOL`, the parameter is not imported as `ObjCBool`. Instead, it's imported as regular Swift `Bool`.
6. Swift realizes that the types don't match (`ObjCBool` vs `Bool`), tries to apply any of the known implicit conversions, fails to do so, crashes.

rdar://130424969
@egorzhdan egorzhdan force-pushed the egorzhdan/objcbool-template-param branch from ae872f8 to fe5b009 Compare June 27, 2024 18:49
@egorzhdan egorzhdan requested a review from ahatanaka June 27, 2024 18:49
@egorzhdan
Copy link
Contributor Author

@swift-ci please test

// Verify that ObjCBool type is banned from C++ template parameters.

takesValue(ObjCBool(true))
// CHECK: error: could not generate C++ types from the generic Swift types provided; the following Swift type(s) provided to 'takesValue' could not be converted: ObjCBool
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way to customize this error message for this case? Can we emit a fix-it that says use .boolValue to convert to a Swift boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Emitting a fix-it seems like a bit like an overkill for this case, I think. We would also need to handle possible explicit generic parameters (e.g. myCxxMethod<T1, ObjCBool, T2>).
This bit of logic also doesn't have access to source locations. We could certainly pipe them through, but I'm not sure if it's worth the effort.

@egorzhdan egorzhdan requested a review from rjmccall June 28, 2024 18:18
Copy link
Contributor

@beccadax beccadax left a comment

Choose a reason for hiding this comment

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

Question. I notice this line in ClangTypeConverter::visitStructType() (ClangTypeConverter.cpp:265 or so):

  CHECK_NAMED_TYPE("ObjCBool", ctx.ObjCBuiltinBoolTy);

If you change ctx.ObjCBuiltinBoolTy to ctx.getBOOLType(), do these templates start importing correctly? I think that might help explain why ObjCBool isn't round-tripping.

If that doesn't help, this PR looks like a good band-aid to prevent the crash, but it seems like a better fix ought to be possible in the future.

@egorzhdan
Copy link
Contributor Author

If you change ctx.ObjCBuiltinBoolTy to ctx.getBOOLType(), do these templates start importing correctly? I think that might help explain why ObjCBool isn't round-tripping.

That's a good question. I initially tried that, and found out that ctx.getBOOLType() returns nullptr until Clang's Sema processes an Obj-C BOOL literal, and then does a name lookup for BOOL. I tried duplicating that logic in this patch, but unfortunately the name lookup won't find BOOL unless objc.h is transitively included in the C++ module which is being processed.

@egorzhdan egorzhdan merged commit b6844f5 into main Jul 4, 2024
5 checks passed
@egorzhdan egorzhdan deleted the egorzhdan/objcbool-template-param branch July 4, 2024 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ interop Feature: Interoperability with C++
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants