Skip to content

Commit 1c5c76f

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 076ae9e commit 1c5c76f

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
@@ -3742,9 +3742,9 @@ ERROR(enum_with_raw_type_case_with_argument,none,
37423742
NOTE(enum_raw_type_here,none,
37433743
"declared raw type %0 here", (Type))
37443744
ERROR(objc_enum_no_raw_type,none,
3745-
"'@objc' enum must declare an integer raw type", ())
3745+
"'%0' enum must declare an integer raw type", (DeclAttribute))
37463746
ERROR(objc_enum_raw_type_not_integer,none,
3747-
"'@objc' enum raw type %0 is not an integer type", (Type))
3747+
"'%0' enum raw type %1 is not an integer type", (DeclAttribute, Type))
37483748
ERROR(enum_non_integer_raw_value_auto_increment,none,
37493749
"enum case must declare a raw value when the preceding raw value is not an integer", ())
37503750
ERROR(enum_non_integer_convertible_raw_type_no_value,none,
@@ -6583,6 +6583,10 @@ NOTE(not_objc_swift_struct,none,
65836583
(ForeignLanguage))
65846584
NOTE(not_objc_swift_enum,none,
65856585
"non-'@objc' enums cannot be represented in Objective-C", ())
6586+
NOTE(not_cdecl_or_objc_swift_enum,none,
6587+
"Swift enums not marked '@cdecl'%select{| or '@objc'}0 cannot be "
6588+
"represented in %" FOREIGN_LANG_SELECT "0",
6589+
(ForeignLanguage))
65866590
NOTE(not_objc_generic_type_param,none,
65876591
"generic type parameters cannot be represented in "
65886592
"%" FOREIGN_LANG_SELECT "0", (ForeignLanguage))

include/swift/AST/TypeCheckRequests.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4875,9 +4875,9 @@ class TypeCheckObjCImplementationRequest
48754875
bool isCached() const { return true; }
48764876
};
48774877

4878-
/// Check @cdecl-style attributes for compatibility with the foreign language.
4879-
class TypeCheckCDeclAttributeRequest
4880-
: public SimpleRequest<TypeCheckCDeclAttributeRequest,
4878+
/// Check @cdecl functions for compatibility with the foreign language.
4879+
class TypeCheckCDeclFunctionRequest
4880+
: public SimpleRequest<TypeCheckCDeclFunctionRequest,
48814881
evaluator::SideEffect(FuncDecl *FD,
48824882
CDeclAttr *attr),
48834883
RequestFlags::Cached> {
@@ -4894,6 +4894,25 @@ class TypeCheckCDeclAttributeRequest
48944894
bool isCached() const { return true; }
48954895
};
48964896

4897+
/// Check @cdecl enums for compatibility with C.
4898+
class TypeCheckCDeclEnumRequest
4899+
: public SimpleRequest<TypeCheckCDeclEnumRequest,
4900+
evaluator::SideEffect(EnumDecl *ED,
4901+
CDeclAttr *attr),
4902+
RequestFlags::Cached> {
4903+
public:
4904+
using SimpleRequest::SimpleRequest;
4905+
4906+
private:
4907+
friend SimpleRequest;
4908+
4909+
evaluator::SideEffect
4910+
evaluate(Evaluator &evaluator, EnumDecl *ED, CDeclAttr *attr) const;
4911+
4912+
public:
4913+
bool isCached() const { return true; }
4914+
};
4915+
48974916
void simple_display(llvm::raw_ostream &out, ASTNode node);
48984917
void simple_display(llvm::raw_ostream &out, Type value);
48994918
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
@@ -554,8 +554,11 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest,
554554
SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest,
555555
unsigned(ExtensionDecl *),
556556
Cached, NoLocationInfo)
557-
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclAttributeRequest,
558-
evaluator::SideEffect(FuncDecl *, CDeclAttr *),
557+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclFunctionRequest,
558+
evaluator::SideEffect(FunctionDecl *, CDeclAttr *),
559+
Cached, NoLocationInfo)
560+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclEnumRequest,
561+
evaluator::SideEffect(EnumDecl *, CDeclAttr *),
559562
Cached, NoLocationInfo)
560563
SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest,
561564
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/ClangModuleLoader.h"
2021
#include "swift/AST/Decl.h"
2122
#include "swift/AST/DiagnosticsSema.h"
@@ -49,6 +50,9 @@ getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) {
4950
}
5051
}
5152

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

lib/AST/Type.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,6 +3056,12 @@ getForeignRepresentable(Type type, ForeignLanguage language,
30563056
// Imported classes and protocols are not representable in C.
30573057
if (isa<ClassDecl>(nominal) || isa<ProtocolDecl>(nominal))
30583058
return failure();
3059+
3060+
// @objc enums are not representable in C, @cdecl ones and imported ones
3061+
// are ok.
3062+
if (!nominal->hasClangNode())
3063+
return failure();
3064+
30593065
LLVM_FALLTHROUGH;
30603066

30613067
case ForeignLanguage::ObjectiveC:
@@ -3094,6 +3100,11 @@ getForeignRepresentable(Type type, ForeignLanguage language,
30943100
}
30953101
}
30963102

3103+
// @cdecl enums are representable in C and Objective-C.
3104+
if (nominal->getAttrs().getAttribute<CDeclAttr>()) {
3105+
return { ForeignRepresentableKind::Trivial, nullptr };
3106+
}
3107+
30973108
// Pointers may be representable in ObjC.
30983109
PointerTypeKind pointerKind;
30993110
if (auto pointerElt = type->getAnyPointerElementType(pointerKind)) {

lib/Sema/TypeCheckDeclObjC.cpp

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

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

@@ -1771,19 +1779,18 @@ static bool isCIntegerType(Type type) {
17711779
}
17721780

17731781
/// Determine whether the given enum should be @objc.
1774-
static bool isEnumObjC(EnumDecl *enumDecl) {
1782+
static bool isEnumObjC(EnumDecl *enumDecl, DeclAttribute *attr) {
17751783
// FIXME: Use shouldMarkAsObjC once it loses it's TypeChecker argument.
17761784

1777-
// If there is no @objc attribute, it's not @objc.
1778-
auto attr = enumDecl->getAttrs().getAttribute<ObjCAttr>();
1785+
// If there is no @objc or @cdecl attribute, skip it.
17791786
if (!attr)
17801787
return false;
17811788

17821789
Type rawType = enumDecl->getRawType();
17831790

1784-
// @objc enums must have a raw type.
1791+
// @objc/@cdecl enums must have a raw type.
17851792
if (!rawType) {
1786-
enumDecl->diagnose(diag::objc_enum_no_raw_type);
1793+
enumDecl->diagnose(diag::objc_enum_no_raw_type, attr);
17871794
return false;
17881795
}
17891796

@@ -1796,7 +1803,7 @@ static bool isEnumObjC(EnumDecl *enumDecl) {
17961803
SourceRange errorRange;
17971804
if (!enumDecl->getInherited().empty())
17981805
errorRange = enumDecl->getInherited().getEntry(0).getSourceRange();
1799-
enumDecl->diagnose(diag::objc_enum_raw_type_not_integer, rawType)
1806+
enumDecl->diagnose(diag::objc_enum_raw_type_not_integer, attr, rawType)
18001807
.highlight(errorRange);
18011808
return false;
18021809
}
@@ -1806,7 +1813,8 @@ static bool isEnumObjC(EnumDecl *enumDecl) {
18061813
enumDecl->diagnose(diag::empty_enum_raw_type);
18071814
}
18081815

1809-
checkObjCNameValidity(enumDecl, attr);
1816+
if (auto objcAttr = dyn_cast<ObjCAttr>(attr))
1817+
checkObjCNameValidity(enumDecl, objcAttr);
18101818
return true;
18111819
}
18121820

@@ -1847,9 +1855,9 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const {
18471855
} else if (auto enumDecl = dyn_cast<EnumDecl>(VD)) {
18481856
// Enums can be @objc so long as they have a raw type that is representable
18491857
// as an arithmetic type in C.
1850-
if (isEnumObjC(enumDecl))
1851-
isObjC = objCReasonForObjCAttr(
1852-
enumDecl->getAttrs().getAttribute<ObjCAttr>());
1858+
auto attr = enumDecl->getAttrs().getAttribute<ObjCAttr>();
1859+
if (attr && isEnumObjC(enumDecl, attr))
1860+
isObjC = objCReasonForObjCAttr(attr);
18531861
} else if (auto enumElement = dyn_cast<EnumElementDecl>(VD)) {
18541862
// Enum elements can be @objc so long as the containing enum is @objc.
18551863
if (enumElement->getParentEnum()->isObjC()) {
@@ -4191,9 +4199,9 @@ evaluate(Evaluator &evaluator, Decl *D) const {
41914199
}
41924200

41934201
evaluator::SideEffect
4194-
TypeCheckCDeclAttributeRequest::evaluate(Evaluator &evaluator,
4195-
FuncDecl *FD,
4196-
CDeclAttr *attr) const {
4202+
TypeCheckCDeclFunctionRequest::evaluate(Evaluator &evaluator,
4203+
FuncDecl *FD,
4204+
CDeclAttr *attr) const {
41974205
auto &ctx = FD->getASTContext();
41984206

41994207
auto lang = FD->getCDeclKind();
@@ -4218,6 +4226,14 @@ TypeCheckCDeclAttributeRequest::evaluate(Evaluator &evaluator,
42184226
} else {
42194227
reason.setAttrInvalid();
42204228
}
4229+
return {};
4230+
}
42214231

4232+
evaluator::SideEffect
4233+
TypeCheckCDeclEnumRequest::evaluate(Evaluator &evaluator,
4234+
EnumDecl *ED,
4235+
CDeclAttr *attr) const {
4236+
// Apply @objc's logic.
4237+
isEnumObjC(ED, attr);
42224238
return {};
42234239
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3288,6 +3288,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
32883288
}
32893289
}
32903290

3291+
// If the enum is exported to C, it must be representable in C.
3292+
if (auto CDeclAttr = ED->getAttrs().getAttribute<swift::CDeclAttr>()) {
3293+
evaluateOrDefault(Ctx.evaluator,
3294+
TypeCheckCDeclEnumRequest{ED, CDeclAttr},
3295+
{});
3296+
}
3297+
32913298
// -----
32923299
// NonCopyableChecks
32933300
//
@@ -3821,7 +3828,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
38213828
// If the function is exported to C, it must be representable in (Obj-)C.
38223829
if (auto CDeclAttr = FD->getAttrs().getAttribute<swift::CDeclAttr>()) {
38233830
evaluateOrDefault(Ctx.evaluator,
3824-
TypeCheckCDeclAttributeRequest{FD, CDeclAttr},
3831+
TypeCheckCDeclFunctionRequest{FD, CDeclAttr},
38253832
{});
38263833
}
38273834

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
@@ -42,26 +42,35 @@ func noBody(x: inout Int) { } // expected-error{{global function cannot be marke
4242

4343
struct SwiftStruct { var x, y: Int }
4444
enum SwiftEnum { case A, B }
45+
4546
#if os(Windows) && (arch(x86_64) || arch(arm64))
46-
@objc enum CEnum: Int32 { case A, B }
47+
@cdecl("CEnum") enum CEnum: Int32 { case A, B }
4748
#else
48-
@objc enum CEnum: Int { case A, B }
49+
@cdecl("CEnum") enum CEnum: Int { case A, B }
4950
#endif
5051

52+
@cdecl("CEnumNoRawType") enum CEnumNoRawType { case A, B }
53+
// expected-error @-1 {{'@cdecl' enum must declare an integer raw type}}
54+
55+
@cdecl("CEnumStringRawType") enum CEnumStringRawType: String { case A, B }
56+
// expected-error @-1 {{'@cdecl' enum raw type 'String' is not an integer type}}
57+
5158
@cdecl("swiftStruct")
5259
func swiftStruct(x: SwiftStruct) {}
5360
// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}}
5461
// expected-note @-2 {{Swift structs cannot be represented in C}}
5562

5663
@cdecl("swiftEnum")
57-
func swiftEnum(x: SwiftEnum) {} // expected-error{{cannot be represented}} expected-note{{non-'@objc' enum}}
64+
func swiftEnum(x: SwiftEnum) {}
65+
// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}}
66+
// expected-note @-2 {{Swift enums not marked '@cdecl' cannot be represented in C}}
5867

5968
@cdecl("cEnum")
6069
func cEnum(x: CEnum) {}
6170

6271
@cdecl("CDeclAndObjC") // expected-error {{cannot apply both '@cdecl' and '@objc' to enum}}
6372
@objc
64-
enum CDeclAndObjC: Int { case A, B }
73+
enum CDeclAndObjC: CInt { case A, B }
6574

6675
@cdecl("TwoCDecls") // expected-note {{attribute already specified here}}
6776
@_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)