Skip to content

Commit 498055c

Browse files
committed
Sema: Basic type-checking of @cdecl on global functions
This implements basic checks on the validity of the @cdecl attribute and ensures the parameters and result types are representable in C. Many more diagnostics will need to be updated to verify full representability in C.
1 parent c40bda5 commit 498055c

9 files changed

+174
-49
lines changed

Diff for: include/swift/AST/DiagnosticsSema.def

+20-12
Original file line numberDiff line numberDiff line change
@@ -2031,11 +2031,11 @@ WARNING(wrap_objc_implementation_will_become_error,none,
20312031
(DiagnosticInfo *))
20322032

20332033
ERROR(cdecl_not_at_top_level,none,
2034-
"@_cdecl can only be applied to global functions", ())
2034+
"%0 can only be applied to global functions", (DeclAttribute))
20352035
ERROR(cdecl_empty_name,none,
2036-
"@_cdecl symbol name cannot be empty", ())
2036+
"%0 symbol name cannot be empty", (DeclAttribute))
20372037
ERROR(cdecl_throws,none,
2038-
"raising errors from @_cdecl functions is not supported", ())
2038+
"raising errors from %0 functions is not supported", (DeclAttribute))
20392039
ERROR(cdecl_feature_required,none,
20402040
"@cdecl requires '-enable-experimental-feature CDecl'",
20412041
())
@@ -6489,7 +6489,10 @@ ERROR(objc_cannot_infer_name_raw_identifier,none,
64896489
(const ValueDecl *))
64906490

64916491
// If you change this, also change enum ObjCReason
6492-
#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}"
6492+
#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}"
6493+
6494+
// Keep aligned with enum ForeignLanguage
6495+
#define FOREIGN_LANG_SELECT "select{C|Objective-C}"
64936496

64946497
ERROR(objc_invalid_on_var,none,
64956498
"property cannot be %" OBJC_ATTR_SELECT "0 "
@@ -6569,17 +6572,21 @@ ERROR(objc_invalid_on_func_variadic,none,
65696572
"method cannot be %" OBJC_ATTR_SELECT "0 because it has a variadic "
65706573
"parameter", (unsigned))
65716574
ERROR(objc_invalid_on_func_inout,none,
6572-
"method cannot be %" OBJC_ATTR_SELECT "0 because inout "
6573-
"parameters cannot be represented in Objective-C", (unsigned))
6575+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because inout "
6576+
"parameters cannot be represented in %" FOREIGN_LANG_SELECT "2",
6577+
(const AbstractFunctionDecl*, unsigned, unsigned))
65746578
ERROR(objc_invalid_on_func_param_type,none,
6575-
"method cannot be %" OBJC_ATTR_SELECT "1 because the type of the "
6576-
"parameter %0 cannot be represented in Objective-C", (unsigned, unsigned))
6579+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "2 because the type of the "
6580+
"parameter %1 cannot be represented in %" FOREIGN_LANG_SELECT "3",
6581+
(const AbstractFunctionDecl*, unsigned, unsigned, unsigned))
65776582
ERROR(objc_invalid_on_func_single_param_type,none,
6578-
"method cannot be %" OBJC_ATTR_SELECT "0 because the type of the "
6579-
"parameter cannot be represented in Objective-C", (unsigned))
6583+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because the type of the "
6584+
"parameter cannot be represented in %" FOREIGN_LANG_SELECT "2",
6585+
(const AbstractFunctionDecl*, unsigned, unsigned))
65806586
ERROR(objc_invalid_on_func_result_type,none,
6581-
"method cannot be %" OBJC_ATTR_SELECT "0 because its result type "
6582-
"cannot be represented in Objective-C", (unsigned))
6587+
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because its result type "
6588+
"cannot be represented in %" FOREIGN_LANG_SELECT "2",
6589+
(const AbstractFunctionDecl*, unsigned, unsigned))
65836590
ERROR(objc_invalid_on_foreign_class,none,
65846591
"method cannot be %" OBJC_ATTR_SELECT "0 because Core Foundation "
65856592
"types are not classes in Objective-C", (unsigned))
@@ -6705,6 +6712,7 @@ ERROR(nonobjc_not_allowed,none,
67056712
#undef OBJC_DIAG_SELECT_2
67066713
#undef OBJC_DIAG_SELECT
67076714
#undef OBJC_ATTR_SELECT
6715+
#undef FOREIGN_LANG_SELECT
67086716

67096717
//------------------------------------------------------------------------------
67106718
// MARK: @exclusivity

Diff for: lib/Sema/CodeSynthesis.cpp

+3-2
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

Diff for: lib/Sema/TypeCheckAttr.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -2356,11 +2356,13 @@ static bool canDeclareSymbolName(StringRef symbol, ModuleDecl *fromModule) {
23562356
void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) {
23572357
// Only top-level func decls are currently supported.
23582358
if (D->getDeclContext()->isTypeContext())
2359-
diagnose(attr->getLocation(), diag::cdecl_not_at_top_level);
2359+
diagnose(attr->getLocation(), diag::cdecl_not_at_top_level,
2360+
attr);
23602361

23612362
// The name must not be empty.
23622363
if (attr->Name.empty())
2363-
diagnose(attr->getLocation(), diag::cdecl_empty_name);
2364+
diagnose(attr->getLocation(), diag::cdecl_empty_name,
2365+
attr);
23642366

23652367
// The standard library can use @_cdecl to implement runtime functions.
23662368
if (!canDeclareSymbolName(attr->Name, D->getModuleContext())) {

Diff for: lib/Sema/TypeCheckCaptures.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -789,8 +789,10 @@ CaptureInfo CaptureInfoRequest::evaluate(Evaluator &evaluator,
789789
std::optional<ForeignAsyncConvention> asyncConvention;
790790
std::optional<ForeignErrorConvention> errorConvention;
791791
if (!AFD->isObjC() &&
792-
isRepresentableInObjC(AFD, ObjCReason::MemberOfObjCMembersClass,
793-
asyncConvention, errorConvention)) {
792+
isRepresentableInLanguage(AFD,
793+
ObjCReason::MemberOfObjCMembersClass,
794+
asyncConvention, errorConvention,
795+
ForeignLanguage::ObjectiveC)) {
794796
AFD->diagnose(
795797
diag::objc_generic_extension_using_type_parameter_try_objc)
796798
.fixItInsert(AFD->getAttributeInsertionLoc(false), "@objc ");

Diff for: lib/Sema/TypeCheckDeclObjC.cpp

+52-29
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ swift::behaviorLimitForObjCReason(ObjCReason reason, ASTContext &ctx) {
4848
LLVM_FALLTHROUGH;
4949

5050
case ObjCReason::ExplicitlyCDecl:
51+
case ObjCReason::ExplicitlyUnderscoreCDecl:
5152
case ObjCReason::ExplicitlyDynamic:
5253
case ObjCReason::ExplicitlyObjC:
5354
case ObjCReason::ExplicitlyObjCMembers:
@@ -81,6 +82,7 @@ swift::behaviorLimitForObjCReason(ObjCReason reason, ASTContext &ctx) {
8182
unsigned swift::getObjCDiagnosticAttrKind(ObjCReason reason) {
8283
switch (reason) {
8384
case ObjCReason::ExplicitlyCDecl:
85+
case ObjCReason::ExplicitlyUnderscoreCDecl:
8486
case ObjCReason::ExplicitlyDynamic:
8587
case ObjCReason::ExplicitlyObjC:
8688
case ObjCReason::ExplicitlyObjCMembers:
@@ -132,6 +134,7 @@ void ObjCReason::describe(const Decl *D) const {
132134

133135
case ObjCReason::ExplicitlyObjCByAccessNote:
134136
case ObjCReason::ExplicitlyCDecl:
137+
case ObjCReason::ExplicitlyUnderscoreCDecl:
135138
case ObjCReason::ExplicitlyDynamic:
136139
case ObjCReason::ExplicitlyObjC:
137140
case ObjCReason::ExplicitlyObjCMembers:
@@ -293,18 +296,21 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC,
293296

294297
static void diagnoseFunctionParamNotRepresentable(
295298
const AbstractFunctionDecl *AFD, unsigned NumParams,
296-
unsigned ParamIndex, const ParamDecl *P, ObjCReason Reason) {
299+
unsigned ParamIndex, const ParamDecl *P, ObjCReason Reason,
300+
ForeignLanguage Language) {
297301
auto behavior = behaviorLimitForObjCReason(Reason, AFD->getASTContext());
298302

299303
if (NumParams == 1) {
300304
softenIfAccessNote(AFD, Reason.getAttr(),
301305
AFD->diagnose(diag::objc_invalid_on_func_single_param_type,
302-
getObjCDiagnosticAttrKind(Reason))
306+
AFD, getObjCDiagnosticAttrKind(Reason),
307+
(unsigned)Language)
303308
.limitBehavior(behavior));
304309
} else {
305310
softenIfAccessNote(AFD, Reason.getAttr(),
306311
AFD->diagnose(diag::objc_invalid_on_func_param_type,
307-
ParamIndex + 1, getObjCDiagnosticAttrKind(Reason))
312+
AFD, ParamIndex + 1, getObjCDiagnosticAttrKind(Reason),
313+
(unsigned)Language)
308314
.limitBehavior(behavior));
309315
}
310316
SourceRange SR;
@@ -321,9 +327,10 @@ static void diagnoseFunctionParamNotRepresentable(
321327
Reason.describe(AFD);
322328
}
323329

324-
static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD,
325-
const ParameterList *PL,
326-
ObjCReason Reason) {
330+
static bool isParamListRepresentableInLanguage(const AbstractFunctionDecl *AFD,
331+
const ParameterList *PL,
332+
ObjCReason Reason,
333+
ForeignLanguage Language) {
327334
// If you change this function, you must add or modify a test in PrintAsClang.
328335
ASTContext &ctx = AFD->getASTContext();
329336
auto &diags = ctx.Diags;
@@ -349,7 +356,7 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD,
349356
if (param->isInOut()) {
350357
softenIfAccessNote(AFD, Reason.getAttr(),
351358
diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_inout,
352-
getObjCDiagnosticAttrKind(Reason))
359+
AFD, getObjCDiagnosticAttrKind(Reason), (unsigned)Language)
353360
.highlight(param->getSourceRange())
354361
.limitBehavior(behavior));
355362
Reason.describe(AFD);
@@ -362,11 +369,11 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD,
362369

363370
if (param->hasAttachedPropertyWrapper()) {
364371
if (param->getPropertyWrapperBackingPropertyType()->isRepresentableIn(
365-
ForeignLanguage::ObjectiveC,
372+
Language,
366373
const_cast<AbstractFunctionDecl *>(AFD)))
367374
continue;
368375
} else if (param->getTypeInContext()->isRepresentableIn(
369-
ForeignLanguage::ObjectiveC,
376+
Language,
370377
const_cast<AbstractFunctionDecl *>(AFD))) {
371378
continue;
372379
}
@@ -387,7 +394,7 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD,
387394

388395
IsObjC = false;
389396
diagnoseFunctionParamNotRepresentable(AFD, NumParams, ParamIndex,
390-
param, Reason);
397+
param, Reason, Language);
391398
}
392399
return IsObjC;
393400
}
@@ -645,14 +652,16 @@ static bool isValidObjectiveCErrorResultType(DeclContext *dc, Type type) {
645652
llvm_unreachable("Unhandled ForeignRepresentableKind in switch.");
646653
}
647654

648-
bool swift::isRepresentableInObjC(
655+
bool swift::isRepresentableInLanguage(
649656
const AbstractFunctionDecl *AFD, ObjCReason Reason,
650657
std::optional<ForeignAsyncConvention> &asyncConvention,
651-
std::optional<ForeignErrorConvention> &errorConvention) {
658+
std::optional<ForeignErrorConvention> &errorConvention,
659+
ForeignLanguage Language) {
652660
auto abiRole = ABIRoleInfo(AFD);
653661
if (!abiRole.providesAPI() && abiRole.getCounterpart())
654-
return isRepresentableInObjC(abiRole.getCounterpart(), Reason,
655-
asyncConvention, errorConvention);
662+
return isRepresentableInLanguage(abiRole.getCounterpart(), Reason,
663+
asyncConvention, errorConvention,
664+
Language);
656665

657666
// Clear out the async and error conventions. They will be added later if
658667
// needed.
@@ -688,6 +697,7 @@ bool swift::isRepresentableInObjC(
688697
auto storage = accessor->getStorage();
689698
bool storageIsObjC = storage->isObjC()
690699
|| Reason == ObjCReason::ExplicitlyCDecl
700+
|| Reason == ObjCReason::ExplicitlyUnderscoreCDecl
691701
|| Reason == ObjCReason::WitnessToObjC
692702
|| Reason == ObjCReason::MemberOfObjCProtocol;
693703

@@ -773,9 +783,10 @@ bool swift::isRepresentableInObjC(
773783
isSpecialInit = init->isObjCZeroParameterWithLongSelector();
774784

775785
if (!isSpecialInit &&
776-
!isParamListRepresentableInObjC(AFD,
777-
AFD->getParameters(),
778-
Reason)) {
786+
!isParamListRepresentableInLanguage(AFD,
787+
AFD->getParameters(),
788+
Reason,
789+
Language)) {
779790
return false;
780791
}
781792

@@ -785,11 +796,12 @@ bool swift::isRepresentableInObjC(
785796
!ResultType->hasError() &&
786797
!ResultType->isVoid() &&
787798
!ResultType->isUninhabited() &&
788-
!ResultType->isRepresentableIn(ForeignLanguage::ObjectiveC,
799+
!ResultType->isRepresentableIn(Language,
789800
const_cast<FuncDecl *>(FD))) {
790801
softenIfAccessNote(AFD, Reason.getAttr(),
791802
AFD->diagnose(diag::objc_invalid_on_func_result_type,
792-
getObjCDiagnosticAttrKind(Reason))
803+
FD, getObjCDiagnosticAttrKind(Reason),
804+
(unsigned)Language)
793805
.limitBehavior(behavior));
794806
diagnoseTypeNotRepresentableInObjC(FD, ResultType,
795807
FD->getResultTypeSourceRange(),
@@ -842,11 +854,11 @@ bool swift::isRepresentableInObjC(
842854
completionHandlerParams.push_back(AnyFunctionType::Param(type));
843855

844856
// Make sure that the parameter type is representable in Objective-C.
845-
if (!type->isRepresentableIn(
846-
ForeignLanguage::ObjectiveC, const_cast<FuncDecl *>(FD))) {
857+
if (!type->isRepresentableIn(Language, const_cast<FuncDecl *>(FD))) {
847858
softenIfAccessNote(AFD, Reason.getAttr(),
848859
AFD->diagnose(diag::objc_invalid_on_func_result_type,
849-
getObjCDiagnosticAttrKind(Reason))
860+
FD, getObjCDiagnosticAttrKind(Reason),
861+
(unsigned)Language)
850862
.limitBehavior(behavior));
851863
diagnoseTypeNotRepresentableInObjC(FD, type,
852864
FD->getResultTypeSourceRange(),
@@ -1260,8 +1272,10 @@ bool swift::canBeRepresentedInObjC(const ValueDecl *decl) {
12601272
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
12611273
std::optional<ForeignAsyncConvention> asyncConvention;
12621274
std::optional<ForeignErrorConvention> errorConvention;
1263-
return isRepresentableInObjC(func, ObjCReason::MemberOfObjCMembersClass,
1264-
asyncConvention, errorConvention);
1275+
return isRepresentableInLanguage(func,
1276+
ObjCReason::MemberOfObjCMembersClass,
1277+
asyncConvention, errorConvention,
1278+
ForeignLanguage::ObjectiveC);
12651279
}
12661280

12671281
if (auto var = dyn_cast<VarDecl>(decl))
@@ -1865,8 +1879,9 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const {
18651879
} else if (isa<DestructorDecl>(VD)) {
18661880
// Destructors need no additional checking.
18671881
} else if (auto func = dyn_cast<AbstractFunctionDecl>(VD)) {
1868-
if (!isRepresentableInObjC(
1869-
func, *isObjC, asyncConvention, errorConvention)) {
1882+
if (!isRepresentableInLanguage(
1883+
func, *isObjC, asyncConvention, errorConvention,
1884+
ForeignLanguage::ObjectiveC)) {
18701885
isObjC->setAttrInvalid();
18711886
return false;
18721887
}
@@ -4150,17 +4165,25 @@ TypeCheckCDeclAttributeRequest::evaluate(Evaluator &evaluator,
41504165
CDeclAttr *attr) const {
41514166
auto &ctx = FD->getASTContext();
41524167

4168+
auto lang = FD->getCDeclKind();
4169+
assert(lang && "missing @cdecl?");
4170+
auto kind = lang == ForeignLanguage::ObjectiveC
4171+
? ObjCReason::ExplicitlyUnderscoreCDecl
4172+
: ObjCReason::ExplicitlyCDecl;
4173+
ObjCReason reason(kind, attr);
4174+
41534175
std::optional<ForeignAsyncConvention> asyncConvention;
41544176
std::optional<ForeignErrorConvention> errorConvention;
4155-
ObjCReason reason(ObjCReason::ExplicitlyCDecl, attr);
4156-
if (isRepresentableInObjC(FD, reason, asyncConvention, errorConvention)) {
4177+
if (isRepresentableInLanguage(FD, reason, asyncConvention, errorConvention,
4178+
*lang)) {
41574179
if (FD->hasAsync()) {
41584180
FD->setForeignAsyncConvention(*asyncConvention);
41594181
ctx.Diags.diagnose(attr->getLocation(), diag::attr_decl_async,
41604182
attr, FD);
41614183
} else if (FD->hasThrows()) {
41624184
FD->setForeignErrorConvention(*errorConvention);
4163-
ctx.Diags.diagnose(attr->getLocation(), diag::cdecl_throws);
4185+
ctx.Diags.diagnose(attr->getLocation(), diag::cdecl_throws,
4186+
attr);
41644187
}
41654188
} else {
41664189
reason.setAttrInvalid();

Diff for: lib/Sema/TypeCheckObjC.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class ObjCReason {
4343
enum Kind {
4444
/// Has the '@cdecl' attribute.
4545
ExplicitlyCDecl,
46+
/// Has the '@_cdecl' attribute.
47+
ExplicitlyUnderscoreCDecl,
4648
/// Has the 'dynamic' modifier.
4749
ExplicitlyDynamic,
4850
/// Has an explicit '@objc' attribute.
@@ -101,6 +103,7 @@ class ObjCReason {
101103
static bool requiresAttr(Kind kind) {
102104
switch (kind) {
103105
case ExplicitlyCDecl:
106+
case ExplicitlyUnderscoreCDecl:
104107
case ExplicitlyDynamic:
105108
case ExplicitlyObjC:
106109
case ExplicitlyObjCMembers:
@@ -183,10 +186,11 @@ unsigned getObjCDiagnosticAttrKind(ObjCReason reason);
183186

184187
/// Determine whether the given function can be represented in Objective-C,
185188
/// and figure out its foreign error convention (if any).
186-
bool isRepresentableInObjC(
189+
bool isRepresentableInLanguage(
187190
const AbstractFunctionDecl *AFD, ObjCReason Reason,
188191
std::optional<ForeignAsyncConvention> &asyncConvention,
189-
std::optional<ForeignErrorConvention> &errorConvention);
192+
std::optional<ForeignErrorConvention> &errorConvention,
193+
ForeignLanguage lang);
190194

191195
/// Determine whether the given variable can be represented in Objective-C.
192196
bool isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason);

0 commit comments

Comments
 (0)