Skip to content

Commit 6d09112

Browse files
committed
Sema: Type-check @cdecl enums
@cdecl enums are Swift enums representable in C. These enums must have an integer raw type. They can be referenced from @cdecl functions and @objc methods. @objc enums are still rejected from @cdecl functions.
1 parent 5df615d commit 6d09112

10 files changed

+118
-29
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3758,9 +3758,9 @@ ERROR(enum_with_raw_type_case_with_argument,none,
37583758
NOTE(enum_raw_type_here,none,
37593759
"declared raw type %0 here", (Type))
37603760
ERROR(objc_enum_no_raw_type,none,
3761-
"'@objc' enum must declare an integer raw type", ())
3761+
"'%0' enum must declare an integer raw type", (DeclAttribute))
37623762
ERROR(objc_enum_raw_type_not_integer,none,
3763-
"'@objc' enum raw type %0 is not an integer type", (Type))
3763+
"'%0' enum raw type %1 is not an integer type", (DeclAttribute, Type))
37643764
ERROR(enum_non_integer_raw_value_auto_increment,none,
37653765
"enum case must declare a raw value when the preceding raw value is not an integer", ())
37663766
ERROR(enum_non_integer_convertible_raw_type_no_value,none,
@@ -6615,6 +6615,10 @@ NOTE(not_objc_swift_struct,none,
66156615
(ForeignLanguage))
66166616
NOTE(not_objc_swift_enum,none,
66176617
"non-'@objc' enums cannot be represented in Objective-C", ())
6618+
NOTE(not_cdecl_or_objc_swift_enum,none,
6619+
"Swift enums not marked '@cdecl'%select{| or '@objc'}0 cannot be "
6620+
"represented in %" FOREIGN_LANG_SELECT "0",
6621+
(ForeignLanguage))
66186622
NOTE(not_objc_generic_type_param,none,
66196623
"generic type parameters cannot be represented in "
66206624
"%" FOREIGN_LANG_SELECT "0", (ForeignLanguage))

include/swift/AST/TypeCheckRequests.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4853,9 +4853,9 @@ 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,
4856+
/// Check @cdecl functions for compatibility with the foreign language.
4857+
class TypeCheckCDeclFunctionRequest
4858+
: public SimpleRequest<TypeCheckCDeclFunctionRequest,
48594859
evaluator::SideEffect(FuncDecl *FD,
48604860
CDeclAttr *attr),
48614861
RequestFlags::Cached> {
@@ -4872,6 +4872,25 @@ class TypeCheckCDeclAttributeRequest
48724872
bool isCached() const { return true; }
48734873
};
48744874

4875+
/// Check @cdecl enums for compatibility with C.
4876+
class TypeCheckCDeclEnumRequest
4877+
: public SimpleRequest<TypeCheckCDeclEnumRequest,
4878+
evaluator::SideEffect(EnumDecl *ED,
4879+
CDeclAttr *attr),
4880+
RequestFlags::Cached> {
4881+
public:
4882+
using SimpleRequest::SimpleRequest;
4883+
4884+
private:
4885+
friend SimpleRequest;
4886+
4887+
evaluator::SideEffect
4888+
evaluate(Evaluator &evaluator, EnumDecl *ED, CDeclAttr *attr) const;
4889+
4890+
public:
4891+
bool isCached() const { return true; }
4892+
};
4893+
48754894
void simple_display(llvm::raw_ostream &out, ASTNode node);
48764895
void simple_display(llvm::raw_ostream &out, Type value);
48774896
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,11 @@ 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 *),
553+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclFunctionRequest,
554+
evaluator::SideEffect(FunctionDecl *, CDeclAttr *),
555+
Cached, NoLocationInfo)
556+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclEnumRequest,
557+
evaluator::SideEffect(EnumDecl *, CDeclAttr *),
555558
Cached, NoLocationInfo)
556559
SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest,
557560
bool(AbstractStorageDecl *), Cached,

lib/AST/SwiftNameTranslation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/AST/SwiftNameTranslation.h"
1818
#include "swift/AST/ASTContext.h"
19+
#include "swift/AST/Attr.h"
1920
#include "swift/AST/Decl.h"
2021
#include "swift/AST/DiagnosticsSema.h"
2122
#include "swift/AST/LazyResolver.h"
@@ -48,6 +49,9 @@ getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) {
4849
}
4950
}
5051

52+
if (auto cdeclAttr = VD->getAttrs().getAttribute<CDeclAttr>())
53+
return cdeclAttr->Name;
54+
5155
if (customNamesOnly)
5256
return StringRef();
5357

lib/AST/Type.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3082,6 +3082,12 @@ getForeignRepresentable(Type type, ForeignLanguage language,
30823082
// Imported classes and protocols are not representable in C.
30833083
if (isa<ClassDecl>(nominal) || isa<ProtocolDecl>(nominal))
30843084
return failure();
3085+
3086+
// @objc enums are not representable in C, @cdecl ones and imported ones
3087+
// are ok.
3088+
if (!nominal->hasClangNode())
3089+
return failure();
3090+
30853091
LLVM_FALLTHROUGH;
30863092

30873093
case ForeignLanguage::ObjectiveC:
@@ -3120,6 +3126,11 @@ getForeignRepresentable(Type type, ForeignLanguage language,
31203126
}
31213127
}
31223128

3129+
// @cdecl enums are representable in C and Objective-C.
3130+
if (nominal->getAttrs().getAttribute<CDeclAttr>()) {
3131+
return { ForeignRepresentableKind::Trivial, nullptr };
3132+
}
3133+
31233134
// Pointers may be representable in ObjC.
31243135
PointerTypeKind pointerKind;
31253136
if (auto pointerElt = type->getAnyPointerElementType(pointerKind)) {

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,17 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC,
223223

224224
// Special diagnostic for enums.
225225
if (T->is<EnumType>()) {
226-
diags.diagnose(TypeRange.Start, diag::not_objc_swift_enum)
227-
.highlight(TypeRange)
228-
.limitBehavior(behavior);
226+
if (DC->getASTContext().LangOpts.hasFeature(Feature::CDecl)) {
227+
// New dialog mentioning @cdecl.
228+
diags.diagnose(TypeRange.Start, diag::not_cdecl_or_objc_swift_enum,
229+
language)
230+
.highlight(TypeRange)
231+
.limitBehavior(behavior);
232+
} else {
233+
diags.diagnose(TypeRange.Start, diag::not_objc_swift_enum)
234+
.highlight(TypeRange)
235+
.limitBehavior(behavior);
236+
}
229237
return;
230238
}
231239

@@ -1766,19 +1774,18 @@ static bool isCIntegerType(Type type) {
17661774
}
17671775

17681776
/// Determine whether the given enum should be @objc.
1769-
static bool isEnumObjC(EnumDecl *enumDecl) {
1777+
static bool isEnumObjC(EnumDecl *enumDecl, DeclAttribute *attr) {
17701778
// FIXME: Use shouldMarkAsObjC once it loses it's TypeChecker argument.
17711779

1772-
// If there is no @objc attribute, it's not @objc.
1773-
auto attr = enumDecl->getAttrs().getAttribute<ObjCAttr>();
1780+
// If there is no @objc or @cdecl attribute, skip it.
17741781
if (!attr)
17751782
return false;
17761783

17771784
Type rawType = enumDecl->getRawType();
17781785

1779-
// @objc enums must have a raw type.
1786+
// @objc/@cdecl enums must have a raw type.
17801787
if (!rawType) {
1781-
enumDecl->diagnose(diag::objc_enum_no_raw_type);
1788+
enumDecl->diagnose(diag::objc_enum_no_raw_type, attr);
17821789
return false;
17831790
}
17841791

@@ -1791,7 +1798,7 @@ static bool isEnumObjC(EnumDecl *enumDecl) {
17911798
SourceRange errorRange;
17921799
if (!enumDecl->getInherited().empty())
17931800
errorRange = enumDecl->getInherited().getEntry(0).getSourceRange();
1794-
enumDecl->diagnose(diag::objc_enum_raw_type_not_integer, rawType)
1801+
enumDecl->diagnose(diag::objc_enum_raw_type_not_integer, attr, rawType)
17951802
.highlight(errorRange);
17961803
return false;
17971804
}
@@ -1801,7 +1808,8 @@ static bool isEnumObjC(EnumDecl *enumDecl) {
18011808
enumDecl->diagnose(diag::empty_enum_raw_type);
18021809
}
18031810

1804-
checkObjCNameValidity(enumDecl, attr);
1811+
if (auto objcAttr = dyn_cast<ObjCAttr>(attr))
1812+
checkObjCNameValidity(enumDecl, objcAttr);
18051813
return true;
18061814
}
18071815

@@ -1842,9 +1850,9 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const {
18421850
} else if (auto enumDecl = dyn_cast<EnumDecl>(VD)) {
18431851
// Enums can be @objc so long as they have a raw type that is representable
18441852
// as an arithmetic type in C.
1845-
if (isEnumObjC(enumDecl))
1846-
isObjC = objCReasonForObjCAttr(
1847-
enumDecl->getAttrs().getAttribute<ObjCAttr>());
1853+
auto attr = enumDecl->getAttrs().getAttribute<ObjCAttr>();
1854+
if (attr && isEnumObjC(enumDecl, attr))
1855+
isObjC = objCReasonForObjCAttr(attr);
18481856
} else if (auto enumElement = dyn_cast<EnumElementDecl>(VD)) {
18491857
// Enum elements can be @objc so long as the containing enum is @objc.
18501858
if (enumElement->getParentEnum()->isObjC()) {
@@ -4209,9 +4217,9 @@ evaluate(Evaluator &evaluator, Decl *D) const {
42094217
}
42104218

42114219
evaluator::SideEffect
4212-
TypeCheckCDeclAttributeRequest::evaluate(Evaluator &evaluator,
4213-
FuncDecl *FD,
4214-
CDeclAttr *attr) const {
4220+
TypeCheckCDeclFunctionRequest::evaluate(Evaluator &evaluator,
4221+
FuncDecl *FD,
4222+
CDeclAttr *attr) const {
42154223
auto &ctx = FD->getASTContext();
42164224

42174225
auto lang = FD->getCDeclKind();
@@ -4236,6 +4244,14 @@ TypeCheckCDeclAttributeRequest::evaluate(Evaluator &evaluator,
42364244
} else {
42374245
reason.setAttrInvalid();
42384246
}
4247+
return {};
4248+
}
42394249

4250+
evaluator::SideEffect
4251+
TypeCheckCDeclEnumRequest::evaluate(Evaluator &evaluator,
4252+
EnumDecl *ED,
4253+
CDeclAttr *attr) const {
4254+
// Apply @objc's logic.
4255+
isEnumObjC(ED, attr);
42404256
return {};
42414257
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3280,6 +3280,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
32803280
}
32813281
}
32823282

3283+
// If the enum is exported to C, it must be representable in C.
3284+
if (auto CDeclAttr = ED->getAttrs().getAttribute<swift::CDeclAttr>()) {
3285+
evaluateOrDefault(Ctx.evaluator,
3286+
TypeCheckCDeclEnumRequest{ED, CDeclAttr},
3287+
{});
3288+
}
3289+
32833290
// -----
32843291
// NonCopyableChecks
32853292
//
@@ -3813,7 +3820,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
38133820
// If the function is exported to C, it must be representable in (Obj-)C.
38143821
if (auto CDeclAttr = FD->getAttrs().getAttribute<swift::CDeclAttr>()) {
38153822
evaluateOrDefault(Ctx.evaluator,
3816-
TypeCheckCDeclAttributeRequest{FD, CDeclAttr},
3823+
TypeCheckCDeclFunctionRequest{FD, CDeclAttr},
38173824
{});
38183825
}
38193826

test/attr/attr_cdecl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ enum SwiftEnum { case A, B }
2525
#endif
2626

2727
@_cdecl("enum") // expected-error {{@_cdecl may only be used on 'func' declarations}}
28-
enum UnderscoreCDeclEnum: Int { case A, B }
28+
enum UnderscoreCDeclEnum: CInt { case A, B }
2929

3030
@_cdecl("swiftStruct")
3131
func swiftStruct(x: SwiftStruct) {} // expected-error{{cannot be represented}} expected-note{{Swift struct}}

test/attr/attr_cdecl_official.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,35 @@ func noBody(x: inout Int) { } // expected-error{{global function cannot be marke
2727

2828
struct SwiftStruct { var x, y: Int }
2929
enum SwiftEnum { case A, B }
30+
3031
#if os(Windows) && (arch(x86_64) || arch(arm64))
31-
@objc enum CEnum: Int32 { case A, B }
32+
@cdecl("CEnum") enum CEnum: Int32 { case A, B }
3233
#else
33-
@objc enum CEnum: Int { case A, B }
34+
@cdecl("CEnum") enum CEnum: Int { case A, B }
3435
#endif
3536

37+
@cdecl("CEnumNoRawType") enum CEnumNoRawType { case A, B }
38+
// expected-error @-1 {{'@cdecl' enum must declare an integer raw type}}
39+
40+
@cdecl("CEnumStringRawType") enum CEnumStringRawType: String { case A, B }
41+
// expected-error @-1 {{'@cdecl' enum raw type 'String' is not an integer type}}
42+
3643
@cdecl("swiftStruct")
3744
func swiftStruct(x: SwiftStruct) {}
3845
// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}}
3946
// expected-note @-2 {{Swift structs cannot be represented in C}}
4047

4148
@cdecl("swiftEnum")
42-
func swiftEnum(x: SwiftEnum) {} // expected-error{{cannot be represented}} expected-note{{non-'@objc' enum}}
49+
func swiftEnum(x: SwiftEnum) {}
50+
// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}}
51+
// expected-note @-2 {{Swift enums not marked '@cdecl' cannot be represented in C}}
4352

4453
@cdecl("cEnum")
4554
func cEnum(x: CEnum) {}
4655

4756
@cdecl("CDeclAndObjC") // expected-error {{cannot apply both '@cdecl' and '@objc' to enum}}
4857
@objc
49-
enum CDeclAndObjC: Int { case A, B }
58+
enum CDeclAndObjC: CInt { case A, B }
5059

5160
@cdecl("TwoCDecls") // expected-note {{attribute already specified here}}
5261
@_cdecl("TwoCDecls") // expected-error {{duplicate attribute}}

test/attr/attr_cdecl_official_with_objc.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,19 @@ protocol ObjCProtocol {}
2424
@cdecl("objcProtocol") func objcProtocol(a: ObjCProtocol) { }
2525
// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}}
2626
// expected-note @-2 {{protocols cannot be represented in C}}
27+
28+
@objc
29+
enum ObjCEnum: Int { case A, B }
30+
@cdecl("objcEnumUseInCDecl") func objcEnumUseInCDecl(a: ObjCEnum) { }
31+
// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}}
32+
// expected-note @-2 {{Swift enums not marked '@cdecl' cannot be represented in C}}
33+
34+
/// Objective-C accepts @cdecl enums.
35+
@cdecl("CEnum")
36+
enum CEnum: Int { case A, B }
37+
@_cdecl("cdeclEnumUseInObjc") func cdeclEnumUseInObjc(a: CEnum) { }
38+
39+
enum SwiftEnum { case A, B }
40+
@_cdecl("swiftEnumUseInObjc") func swiftEnumUseInObjc(a: SwiftEnum) { }
41+
// expected-error @-1 {{global function cannot be marked '@_cdecl' because the type of the parameter cannot be represented in Objective-C}}
42+
// expected-note @-2 {{Swift enums not marked '@cdecl' or '@objc' cannot be represented in Objective-C}}

0 commit comments

Comments
 (0)