-
Notifications
You must be signed in to change notification settings - Fork 10.5k
PrintAsClang: Print a C block in the compatibility header for @cdecl
functions
#80917
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
Conversation
@swift-ci Please smoke test |
Before I dig into the details: What's with the nullability stuff here? I mean, obviously you want to ensure that compilers which don't support nullability annotations can still understand the header, but it seems like you're disabling (at least some parts of) nullability checking even in compilers that support it. Why? |
In C mode I don't think clang supports nullability checking. It recognizes I don't like it so much either tbh. Alternatively we could change the printing logic to simply not print those attributes in the C block, but I'm thinking these attributes may still be useful for Objective-C clients calling a |
@swift-ci Please smoke test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start, but I have a few concerns.
lib/PrintAsClang/PrintAsClang.cpp
Outdated
// Ignore nullability attributes for C clients. | ||
out << "#if !defined(__OBJC__) && !defined(__cplusplus)\n" | ||
"# if __has_feature(nullability)\n" | ||
"# pragma clang diagnostic ignored \"-Wnullability-extension\"\n" | ||
"# else\n" | ||
"# define _Nonnull\n" | ||
"# endif\n" | ||
"#endif\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few issues I’d address here:
- The comment doesn’t quite reflect what you’re actually doing here (which is why I asked about the
-W
flag; it didn’t match the comment). - It looks to me like only ObjC mode prevents the
-Wnullability-extension
warning from being emitted—C++ mode doesn't. - I believe there are circumstances where we’ll emit
_Nullable
,_Null_unspecified
, and possibly_Nullable_result
in addition to_Nonnull
.
The upshot is, consider doing this instead:
// Ignore nullability attributes for C clients. | |
out << "#if !defined(__OBJC__) && !defined(__cplusplus)\n" | |
"# if __has_feature(nullability)\n" | |
"# pragma clang diagnostic ignored \"-Wnullability-extension\"\n" | |
"# else\n" | |
"# define _Nonnull\n" | |
"# endif\n" | |
"#endif\n"; | |
// For C compilers which don’t support nullability attributes, ignore them; | |
// for ones which do, suppress warnings about them being an extension. | |
out << "#if !__has_feature(nullability)\n" | |
"# define _Nonnull\n" | |
"# define _Nullable\n" | |
"# define _Null_unspecified\n" | |
"#elif !defined(__OBJC__)\n" | |
"# pragma clang diagnostic ignored \"-Wnullability-extension\"\n" | |
"#endif\n" | |
"#if !__has_feature(nullability_nullable_result)\n" | |
"# define _Nullable_result _Nullable\n" | |
"#endif\n"; |
However, I do have a lingering concern about even this version: the C standard reserves underscore-uppercase identifiers for the compiler and standard library to use, so I believe #define
-ing _Nonnull
and friends is technically not valid. I might run this by some clang engineers to see if they recommend you do something different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I applied this change and gated it behind the feature flag for now. I'll look deeper into types representable in C next and should be able to clean up the story around nullability then.
|
||
@cdecl("has_keyword_arg_names") | ||
func c_keywordArgNames(auto: Int, union: Int) {} | ||
// CHECK-LABEL: SWIFT_EXTERN void has_keyword_arg_names(ptrdiff_t auto_, ptrdiff_t union_) SWIFT_NOEXCEPT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add test cases for various pointer types: UnsafeMutableRawPointer
should become void * _Nonnull
, UnsafeMutablePointer<Int>
should become ptrdiff_t * _Nonnull
, the non-Mutable
variants should gain a const
, and adding ?
or !
at the end should get you _Nullable
and _Null_unspecified
respectively. (Unless we’re in an assume_nonnull
region? If you think so, there should be a CHECK line for that.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I'm taking note of these. I plan on adding more test and type-checking around the types accepted by @cdecl
in a future PR, a few more are not covered either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already get the desired behavior so I've added tests for pointer types and nullability to this PR.
|
||
// C content (@cdecl) | ||
if (M->getASTContext().LangOpts.hasFeature(Feature::CDecl)) { | ||
SmallPtrSet<ImportModuleTy, 8> imports; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you’re in C++ or ObjC mode, are there going to be two sets of imports? Do both of them actually get printed? What if there’s overlap between them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept the logic collecting imports as part of the boilerplate when I set up the C block but not the imports printing part. I don't expect it to collect anything at this point and it's unclear to me if we'll need to print imports for C in the future. Maybe for imported @cdecl enums
or eventually some form of @cdecl struct
, but even then we may have to print a local copy as we wouldn't know what header to import. Do you see other scenarios where we'd need imports for C?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imports come up when a declaration uses a type from another (C) library. For instance, suppose you have this header:
// CoreToaster/CTTimeSetting.h
typedef enum __attribute__((enum_extensibility(open)) CTTimeSetting CTTimeSetting;
enum CTTimeSetting {
CTTimeSettingLight,
CTTimeSettingNormal,
CTTimeSettingDark
};
And you write this Swift file:
// ToasterKit.swift
import CoreToaster
@cdecl(TKGetDefaultTimeSetting)
public func defaultTimeSetting() -> CTTimeSetting { .normal }
The ToasterKit generated header will need to use CTTimeSetting
, which means it has to import the header it came from:
// ToasterKit+Swift.h
#include <CoreToaster/CTTimeSetting.h>
// ...other stuff...
CTTimeSetting TKGetDefaultTimeSetting();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes perfect sense. I've added imported types to the pitch already and plan on adding support for C includes in a future PR.
|
||
//--- Lib.swift | ||
|
||
// CHECK: #if defined(__cplusplus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strongly recommend adding test cases that require an import and then testing the imports you get.
Remember, to be compatible with non-clang compilers, official cdecl needs to not use @import
; it needs to use #include
instead (or at least fall back to it). It also needs to not use the .h
-less #include
s that are common in C++.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, C includes with require some new logic to the printing component.
Add a block for C clients in the compatibility header. This block contains only the `@cdecl` functions that are printed using only C types. This C block is printed above the Objective-C and C++ blocks as if we add support for `@cdecl` types other languages should be able to reference them in function signatures. Other languages block don't duplicate printing the `@cdecl` functions either as they are already accessible to them.
In C mode we still print nullability attributes. Don't let clang warn on them and ignore the attribute if the C compiler doesn't know them.
@swift-ci Please smoke test |
@@ -2999,6 +3006,14 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { | |||
return false; | |||
} | |||
|
|||
// In C output mode print only @cdecls and skip them in other modes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate on this? Where did we emit cdecls before this change?
What if someone what to consume these cdecls from ObjC or C++ code? Is that still possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We print the new C specific @cdecl
only in C mode and keep printing the classic @_cdecl
in other modes. The other modes print @_cdecl
in they own block of the compatibility header gated behind the corresponding language check. I've updated the comment clarify this.
Objective-C and C++ clients see and can call the newly printed @cdecl
functions. They are printed in a block at the beginning of the compatibility header without a language check except for an extern "C"
block.
@swift-ci Please smoke test Linux |
@swift-ci Please smoke test Windows |
@swift-ci Please smoke test |
@swift-ci Please smoke test Added tests for pointer types and changed the |
@swift-ci Please smoke test |
@swift-ci Please smoke test |
@swift-ci Please smoke test Windows |
Merging to unblock the follow up PR adding support for |
This reverts commit 9c7f0c7.
Add a block for C clients in the compatibility header. This block contains only the C representation of the
@cdecl
functions that are printed using only C types.This C block is printed above the Objective-C and C++ blocks as if we add support for
@cdecl
types other languages should be able to reference them in function signatures. Other languages block don't duplicate printing the@cdecl
functions either as they are already accessible to them.Nullability attributes may still be printed so we simply ignore them for C clients.