Skip to content

Commit 405a84e

Browse files
authored
Merge pull request #80744 from xymus/cdecl-global-function-checking
Sema: Intro experimental @cdecl and basic C compatibility check
2 parents f89dcf7 + a6beaf8 commit 405a84e

28 files changed

+329
-114
lines changed

include/swift/AST/Attr.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -690,22 +690,29 @@ class SectionAttr : public DeclAttribute {
690690
/// Defines the @_cdecl attribute.
691691
class CDeclAttr : public DeclAttribute {
692692
public:
693-
CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
694-
: DeclAttribute(DeclAttrKind::CDecl, AtLoc, Range, Implicit), Name(Name) {
693+
CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit,
694+
bool Underscored)
695+
: DeclAttribute(DeclAttrKind::CDecl, AtLoc, Range, Implicit),
696+
Name(Name), Underscored(Underscored) {
695697
}
696698

697-
CDeclAttr(StringRef Name, bool Implicit)
698-
: CDeclAttr(Name, SourceLoc(), SourceRange(), Implicit) {}
699+
CDeclAttr(StringRef Name, bool Implicit, bool Underscored)
700+
: CDeclAttr(Name, SourceLoc(), SourceRange(), Implicit, Underscored) {}
699701

700702
/// The symbol name.
701703
const StringRef Name;
702704

705+
/// Is this the version of the attribute that's underscored?
706+
/// Used to preserve retro compatibility with early adopters.
707+
const bool Underscored;
708+
703709
static bool classof(const DeclAttribute *DA) {
704710
return DA->getKind() == DeclAttrKind::CDecl;
705711
}
706712

707713
CDeclAttr *clone(ASTContext &ctx) const {
708-
return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit());
714+
return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit(),
715+
Underscored);
709716
}
710717

711718
bool isEquivalent(const CDeclAttr *other, Decl *attachedTo) const {

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8134,6 +8134,11 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
81348134
/// instance method.
81358135
bool isObjCInstanceMethod() const;
81368136

8137+
/// Get the foreign language targeted by a @cdecl-style attribute, if any.
8138+
/// Used to abstract away the change in meaning of @cdecl vs @_cdecl while
8139+
/// formalizing the attribute.
8140+
std::optional<ForeignLanguage> getCDeclKind() const;
8141+
81378142
/// Determine whether the name of an argument is an API name by default
81388143
/// depending on the function context.
81398144
bool argumentNameIsAPIByDefault() const;

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ DECL_ATTR(_cdecl, CDecl,
368368
OnFunc | OnAccessor,
369369
LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
370370
63)
371+
DECL_ATTR_ALIAS(cdecl, CDecl)
371372

372373
SIMPLE_DECL_ATTR(usableFromInline, UsableFromInline,
373374
OnAbstractFunction | OnVar | OnSubscript | OnNominalType | OnTypeAlias,

include/swift/AST/DiagnosticsSema.def

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,11 +2044,14 @@ WARNING(wrap_objc_implementation_will_become_error,none,
20442044
(DiagnosticInfo *))
20452045

20462046
ERROR(cdecl_not_at_top_level,none,
2047-
"@_cdecl can only be applied to global functions", ())
2047+
"%0 can only be applied to global functions", (DeclAttribute))
20482048
ERROR(cdecl_empty_name,none,
2049-
"@_cdecl symbol name cannot be empty", ())
2049+
"%0 symbol name cannot be empty", (DeclAttribute))
20502050
ERROR(cdecl_throws,none,
2051-
"raising errors from @_cdecl functions is not supported", ())
2051+
"raising errors from %0 functions is not supported", (DeclAttribute))
2052+
ERROR(cdecl_feature_required,none,
2053+
"@cdecl requires '-enable-experimental-feature CDecl'",
2054+
())
20522055

20532056
// @_used and @_section
20542057
ERROR(section_linkage_markers_disabled,none,
@@ -6499,7 +6502,10 @@ ERROR(objc_cannot_infer_name_raw_identifier,none,
64996502
(const ValueDecl *))
65006503

65016504
// If you change this, also change enum ObjCReason
6502-
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @objcMembers|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|in an @objc @implementation extension of a class (without final or @nonobjc)|marked @objc by an access note}"
6505+
#define OBJC_ATTR_SELECT "select{marked @cdecl|marked @_cdecl|marked dynamic|marked @objc|marked @objcMembers|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|in an @objc @implementation extension of a class (without final or @nonobjc)|marked @objc by an access note}"
6506+
6507+
// Keep aligned with enum ForeignLanguage
6508+
#define FOREIGN_LANG_SELECT "select{C|Objective-C}"
65036509

65046510
ERROR(objc_invalid_on_var,none,
65056511
"property cannot be %" OBJC_ATTR_SELECT "0 "
@@ -6579,17 +6585,21 @@ ERROR(objc_invalid_on_func_variadic,none,
65796585
"method cannot be %" OBJC_ATTR_SELECT "0 because it has a variadic "
65806586
"parameter", (unsigned))
65816587
ERROR(objc_invalid_on_func_inout,none,
6582-
"method cannot be %" OBJC_ATTR_SELECT "0 because inout "
6583-
"parameters cannot be represented in Objective-C", (unsigned))
6588+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because inout "
6589+
"parameters cannot be represented in %" FOREIGN_LANG_SELECT "2",
6590+
(const AbstractFunctionDecl*, unsigned, unsigned))
65846591
ERROR(objc_invalid_on_func_param_type,none,
6585-
"method cannot be %" OBJC_ATTR_SELECT "1 because the type of the "
6586-
"parameter %0 cannot be represented in Objective-C", (unsigned, unsigned))
6592+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "2 because the type of the "
6593+
"parameter %1 cannot be represented in %" FOREIGN_LANG_SELECT "3",
6594+
(const AbstractFunctionDecl*, unsigned, unsigned, unsigned))
65876595
ERROR(objc_invalid_on_func_single_param_type,none,
6588-
"method cannot be %" OBJC_ATTR_SELECT "0 because the type of the "
6589-
"parameter cannot be represented in Objective-C", (unsigned))
6596+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because the type of the "
6597+
"parameter cannot be represented in %" FOREIGN_LANG_SELECT "2",
6598+
(const AbstractFunctionDecl*, unsigned, unsigned))
65906599
ERROR(objc_invalid_on_func_result_type,none,
6591-
"method cannot be %" OBJC_ATTR_SELECT "0 because its result type "
6592-
"cannot be represented in Objective-C", (unsigned))
6600+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because its result type "
6601+
"cannot be represented in %" FOREIGN_LANG_SELECT "2",
6602+
(const AbstractFunctionDecl*, unsigned, unsigned))
65936603
ERROR(objc_invalid_on_foreign_class,none,
65946604
"method cannot be %" OBJC_ATTR_SELECT "0 because Core Foundation "
65956605
"types are not classes in Objective-C", (unsigned))
@@ -6715,6 +6725,7 @@ ERROR(nonobjc_not_allowed,none,
67156725
#undef OBJC_DIAG_SELECT_2
67166726
#undef OBJC_DIAG_SELECT
67176727
#undef OBJC_ATTR_SELECT
6728+
#undef FOREIGN_LANG_SELECT
67186729

67196730
//------------------------------------------------------------------------------
67206731
// MARK: @exclusivity

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4853,6 +4853,25 @@ class TypeCheckObjCImplementationRequest
48534853
bool isCached() const { return true; }
48544854
};
48554855

4856+
/// Check @cdecl-style attributes for compatibility with the foreign language.
4857+
class TypeCheckCDeclAttributeRequest
4858+
: public SimpleRequest<TypeCheckCDeclAttributeRequest,
4859+
evaluator::SideEffect(FuncDecl *FD,
4860+
CDeclAttr *attr),
4861+
RequestFlags::Cached> {
4862+
public:
4863+
using SimpleRequest::SimpleRequest;
4864+
4865+
private:
4866+
friend SimpleRequest;
4867+
4868+
evaluator::SideEffect
4869+
evaluate(Evaluator &evaluator, FuncDecl *FD, CDeclAttr *attr) const;
4870+
4871+
public:
4872+
bool isCached() const { return true; }
4873+
};
4874+
48564875
void simple_display(llvm::raw_ostream &out, ASTNode node);
48574876
void simple_display(llvm::raw_ostream &out, Type value);
48584877
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,9 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest,
550550
SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest,
551551
unsigned(ExtensionDecl *),
552552
Cached, NoLocationInfo)
553+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclAttributeRequest,
554+
evaluator::SideEffect(FuncDecl *, CDeclAttr *),
555+
Cached, NoLocationInfo)
553556
SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest,
554557
bool(AbstractStorageDecl *), Cached,
555558
NoLocationInfo)

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,9 @@ EXPERIMENTAL_FEATURE(ClosureBodyMacro, true)
514514
/// Allow declarations of Swift runtime symbols using @_silgen_name.
515515
EXPERIMENTAL_FEATURE(AllowRuntimeSymbolDeclarations, true)
516516

517+
/// Allow use of `@cdecl`
518+
EXPERIMENTAL_FEATURE(CDecl, false)
519+
517520
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
518521
#undef EXPERIMENTAL_FEATURE
519522
#undef UPCOMING_FEATURE

lib/AST/Attr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1783,7 +1783,9 @@ StringRef DeclAttribute::getAttrName() const {
17831783
case DeclAttrKind::Alignment:
17841784
return "_alignment";
17851785
case DeclAttrKind::CDecl:
1786-
return "_cdecl";
1786+
if (cast<CDeclAttr>(this)->Underscored)
1787+
return "_cdecl";
1788+
return "cdecl";
17871789
case DeclAttrKind::SwiftNativeObjCRuntimeBase:
17881790
return "_swift_native_objc_runtime_base";
17891791
case DeclAttrKind::Semantics:

lib/AST/Bridging/DeclAttributeBridging.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext,
250250
BridgedStringRef cName) {
251251
return new (cContext.unbridged())
252252
CDeclAttr(cName.unbridged(), cAtLoc.unbridged(), cRange.unbridged(),
253-
/*Implicit=*/false);
253+
/*Implicit=*/false, /*Underscored*/true);
254254
}
255255

256256
BridgedCustomAttr BridgedCustomAttr_createParsed(

lib/AST/Decl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10520,6 +10520,15 @@ bool AbstractFunctionDecl::isObjCInstanceMethod() const {
1052010520
return isInstanceMember() || isa<ConstructorDecl>(this);
1052110521
}
1052210522

10523+
std::optional<ForeignLanguage> AbstractFunctionDecl::getCDeclKind() const {
10524+
auto attr = getAttrs().getAttribute<CDeclAttr>();
10525+
if (!attr)
10526+
return std::nullopt;
10527+
10528+
return attr->Underscored ? ForeignLanguage::ObjectiveC
10529+
: ForeignLanguage::C;
10530+
}
10531+
1052310532
bool AbstractFunctionDecl::needsNewVTableEntry() const {
1052410533
auto &ctx = getASTContext();
1052510534
return evaluateOrDefault(

lib/AST/FeatureSet.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ static bool usesFeatureClosureBodyMacro(Decl *decl) {
410410
return false;
411411
}
412412

413+
static bool usesFeatureCDecl(Decl *decl) {
414+
auto attr = decl->getAttrs().getAttribute<CDeclAttr>();
415+
return attr && !attr->Underscored;
416+
}
417+
413418
static bool usesFeatureMemorySafetyAttributes(Decl *decl) {
414419
if (decl->getAttrs().hasAttribute<SafeAttr>() ||
415420
decl->getAttrs().hasAttribute<UnsafeAttr>())

lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,10 +3074,12 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
30743074
if (DK == DeclAttrKind::SILGenName)
30753075
Attributes.add(new (Context) SILGenNameAttr(AsmName.value(), Raw, AtLoc,
30763076
AttrRange, /*Implicit=*/false));
3077-
else if (DK == DeclAttrKind::CDecl)
3077+
else if (DK == DeclAttrKind::CDecl) {
3078+
bool underscored = AttrName.starts_with("_");
30783079
Attributes.add(new (Context) CDeclAttr(AsmName.value(), AtLoc,
3079-
AttrRange, /*Implicit=*/false));
3080-
else if (DK == DeclAttrKind::Expose) {
3080+
AttrRange, /*Implicit=*/false,
3081+
/*isUnderscored*/underscored));
3082+
} else if (DK == DeclAttrKind::Expose) {
30813083
for (auto *EA : Attributes.getAttributes<ExposeAttr>()) {
30823084
// A single declaration cannot have two @_exported attributes with
30833085
// the same exposure kind.

lib/Sema/CodeSynthesis.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,8 +637,9 @@ configureInheritedDesignatedInitAttributes(ClassDecl *classDecl,
637637
std::optional<ForeignAsyncConvention> asyncConvention;
638638
std::optional<ForeignErrorConvention> errorConvention;
639639
if (superclassCtor->isObjC() &&
640-
!isRepresentableInObjC(ctor, ObjCReason::MemberOfObjCSubclass,
641-
asyncConvention, errorConvention))
640+
!isRepresentableInLanguage(ctor, ObjCReason::MemberOfObjCSubclass,
641+
asyncConvention, errorConvention,
642+
ForeignLanguage::ObjectiveC))
642643
ctor->getAttrs().add(new (ctx) NonObjCAttr(/*isImplicit=*/true));
643644
}
644645

lib/Sema/TypeCheckAttr.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,17 +2368,24 @@ static bool canDeclareSymbolName(StringRef symbol, ModuleDecl *fromModule) {
23682368
void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) {
23692369
// Only top-level func decls are currently supported.
23702370
if (D->getDeclContext()->isTypeContext())
2371-
diagnose(attr->getLocation(), diag::cdecl_not_at_top_level);
2371+
diagnose(attr->getLocation(), diag::cdecl_not_at_top_level,
2372+
attr);
23722373

23732374
// The name must not be empty.
23742375
if (attr->Name.empty())
2375-
diagnose(attr->getLocation(), diag::cdecl_empty_name);
2376+
diagnose(attr->getLocation(), diag::cdecl_empty_name,
2377+
attr);
23762378

23772379
// The standard library can use @_cdecl to implement runtime functions.
23782380
if (!canDeclareSymbolName(attr->Name, D->getModuleContext())) {
23792381
diagnose(attr->getLocation(), diag::reserved_runtime_symbol_name,
23802382
attr->Name);
23812383
}
2384+
2385+
if (!attr->Underscored &&
2386+
!Ctx.LangOpts.hasFeature(Feature::CDecl)) {
2387+
Ctx.Diags.diagnose(attr->getLocation(), diag::cdecl_feature_required);
2388+
}
23822389
}
23832390

23842391
void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,10 @@ CaptureInfo CaptureInfoRequest::evaluate(Evaluator &evaluator,
787787
std::optional<ForeignAsyncConvention> asyncConvention;
788788
std::optional<ForeignErrorConvention> errorConvention;
789789
if (!AFD->isObjC() &&
790-
isRepresentableInObjC(AFD, ObjCReason::MemberOfObjCMembersClass,
791-
asyncConvention, errorConvention)) {
790+
isRepresentableInLanguage(AFD,
791+
ObjCReason::MemberOfObjCMembersClass,
792+
asyncConvention, errorConvention,
793+
ForeignLanguage::ObjectiveC)) {
792794
AFD->diagnose(
793795
diag::objc_generic_extension_using_type_parameter_try_objc)
794796
.fixItInsert(AFD->getAttributeInsertionLoc(false), "@objc ");

0 commit comments

Comments
 (0)