Skip to content

Sema: Update more diagnostics for @cdecl and representability in the C language #81519

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

Merged
merged 2 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/DiagnosticsCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ namespace swift {
} // end namespace detail

enum class StaticSpellingKind : uint8_t;
enum class ForeignLanguage : uint8_t;

namespace diag {

Expand Down
49 changes: 31 additions & 18 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5512,11 +5512,15 @@ FIXIT(insert_globalactor_attr, "@%0 ", (Type))
ERROR(main_function_must_be_mainActor,none,
"main() must be '@MainActor'", ())

// Keep aligned with enum ForeignLanguage
#define FOREIGN_LANG_SELECT "select{C|Objective-C}"

ERROR(not_objc_function_async,none,
"'async' %kindonly0 cannot be represented in Objective-C",
(const AbstractFunctionDecl *))
NOTE(not_objc_function_type_async,none,
"'async' function types cannot be represented in Objective-C", ())
"'async' function types cannot be represented "
"in %" FOREIGN_LANG_SELECT "0", (ForeignLanguage))
ERROR(actor_isolated_objc,none,
"actor-isolated %kind0 cannot be '@objc'",
(const ValueDecl *))
Expand Down Expand Up @@ -6529,9 +6533,6 @@ ERROR(objc_cannot_infer_name_raw_identifier,none,
// If you change this, also change enum ObjCReason
#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}"

// Keep aligned with enum ForeignLanguage
#define FOREIGN_LANG_SELECT "select{C|Objective-C}"

ERROR(objc_invalid_on_var,none,
"property cannot be %" OBJC_ATTR_SELECT "0 "
"because its type cannot be represented in Objective-C", (unsigned))
Expand Down Expand Up @@ -6566,25 +6567,32 @@ NOTE(not_objc_error_protocol_composition,none,
"protocol-constrained type containing 'Error' cannot be represented "
"in Objective-C", ())
NOTE(not_objc_empty_tuple,none,
"empty tuple type cannot be represented in Objective-C", ())
"empty tuple type cannot be represented in %" FOREIGN_LANG_SELECT "0",
(ForeignLanguage))
NOTE(not_objc_non_trivial_cxx_class,none,
"non-trivial C++ classes cannot be represented in Objective-C", ())
"non-trivial C++ classes cannot be represented in "
"%" FOREIGN_LANG_SELECT "0",
(ForeignLanguage))
NOTE(not_objc_tuple,none,
"tuples cannot be represented in Objective-C", ())
"tuples cannot be represented in %" FOREIGN_LANG_SELECT "0",
(ForeignLanguage))
NOTE(not_objc_swift_class,none,
"classes not annotated with '@objc' cannot be represented "
"in Objective-C", ())
NOTE(not_objc_swift_struct,none,
"Swift structs cannot be represented in Objective-C", ())
"Swift structs cannot be represented in %" FOREIGN_LANG_SELECT "0",
(ForeignLanguage))
NOTE(not_objc_swift_enum,none,
"non-'@objc' enums cannot be represented in Objective-C", ())
NOTE(not_objc_generic_type_param,none,
"generic type parameters cannot be represented in Objective-C", ())
"generic type parameters cannot be represented in "
"%" FOREIGN_LANG_SELECT "0", (ForeignLanguage))
NOTE(not_objc_function_type_param,none,
"function types cannot be represented in Objective-C unless their "
"parameters and returns can be", ())
"function types cannot be represented in %" FOREIGN_LANG_SELECT "0 "
"unless their parameters and returns can be", (ForeignLanguage))
NOTE(not_objc_function_type_throwing,none,
"throwing function types cannot be represented in Objective-C", ())
"throwing function types cannot be represented in "
"%" FOREIGN_LANG_SELECT "0", (ForeignLanguage))
NOTE(objc_inferring_on_objc_protocol_member,none,
"inferring '@objc' because the declaration is a member of "
"an '@objc' protocol", ())
Expand All @@ -6594,6 +6602,11 @@ NOTE(objc_witness_objc_requirement,none,
"satisfying requirement for %kind0 in protocol %1",
(const ValueDecl *, const ProtocolDecl *))

NOTE(cdecl_incompatible_with_protocols,none,
"protocols cannot be represented in C", ())
NOTE(cdecl_incompatible_with_classes,none,
"classes cannot be represented in C", ())

ERROR(no_opaque_return_type_of,none,
"unable to resolve type for _opaqueReturnTypeOf attribute", ())

Expand All @@ -6607,24 +6620,24 @@ ERROR(objc_addressor, none,
ERROR(objc_coroutine_accessor, none,
"'read' and 'modify' accessors are not allowed to be marked '@objc'", ())
ERROR(objc_invalid_on_func_variadic,none,
"method cannot be %" OBJC_ATTR_SELECT "0 because it has a variadic "
"parameter", (unsigned))
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because it has a variadic "
"parameter", (const AbstractFunctionDecl*, unsigned))
ERROR(objc_invalid_on_func_inout,none,
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because inout "
"parameters cannot be represented in %" FOREIGN_LANG_SELECT "2",
(const AbstractFunctionDecl*, unsigned, unsigned))
(const AbstractFunctionDecl*, unsigned, ForeignLanguage))
ERROR(objc_invalid_on_func_param_type,none,
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "2 because the type of the "
"parameter %1 cannot be represented in %" FOREIGN_LANG_SELECT "3",
(const AbstractFunctionDecl*, unsigned, unsigned, unsigned))
(const AbstractFunctionDecl*, unsigned, unsigned, ForeignLanguage))
ERROR(objc_invalid_on_func_single_param_type,none,
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because the type of the "
"parameter cannot be represented in %" FOREIGN_LANG_SELECT "2",
(const AbstractFunctionDecl*, unsigned, unsigned))
(const AbstractFunctionDecl*, unsigned, ForeignLanguage))
ERROR(objc_invalid_on_func_result_type,none,
"%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because its result type "
"cannot be represented in %" FOREIGN_LANG_SELECT "2",
(const AbstractFunctionDecl*, unsigned, unsigned))
(const AbstractFunctionDecl*, unsigned, ForeignLanguage))
ERROR(objc_invalid_on_foreign_class,none,
"method cannot be %" OBJC_ATTR_SELECT "0 because Core Foundation "
"types are not classes in Objective-C", (unsigned))
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) {

/// Enumeration describing foreign languages to which Swift may be
/// bridged.
enum class ForeignLanguage {
enum class ForeignLanguage : uint8_t {
C,
ObjectiveC,
};
Expand Down
64 changes: 43 additions & 21 deletions lib/Sema/TypeCheckDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,32 @@ void ObjCReason::setAttrInvalid() const {
static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC,
Type T,
SourceRange TypeRange,
DiagnosticBehavior behavior) {
DiagnosticBehavior behavior,
ObjCReason reason) {
auto &diags = DC->getASTContext().Diags;
auto language = reason.getForeignLanguage();

// Special diagnostic for tuples.
if (T->is<TupleType>()) {
if (T->isVoid())
diags.diagnose(TypeRange.Start, diag::not_objc_empty_tuple)
diags.diagnose(TypeRange.Start, diag::not_objc_empty_tuple, language)
.highlight(TypeRange)
.limitBehavior(behavior);
else
diags.diagnose(TypeRange.Start, diag::not_objc_tuple)
diags.diagnose(TypeRange.Start, diag::not_objc_tuple, language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
}

// Special diagnostic for classes.
if (auto *CD = T->getClassOrBoundGenericClass()) {
if (language == ForeignLanguage::C) {
diags.diagnose(TypeRange.Start, diag::cdecl_incompatible_with_classes)
.limitBehavior(behavior);
return;
}

if (!CD->isObjC())
diags.diagnose(TypeRange.Start, diag::not_objc_swift_class)
.highlight(TypeRange)
Expand All @@ -199,12 +207,14 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC,
if (auto *SD = T->getStructOrBoundGenericStruct()) {
if (isa_and_nonnull<clang::CXXRecordDecl>(SD->getClangDecl())) {
// This can be a non-trivial C++ record.
diags.diagnose(TypeRange.Start, diag::not_objc_non_trivial_cxx_class)
diags.diagnose(TypeRange.Start, diag::not_objc_non_trivial_cxx_class,
language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
}
diags.diagnose(TypeRange.Start, diag::not_objc_swift_struct)
diags.diagnose(TypeRange.Start, diag::not_objc_swift_struct,
language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
Expand All @@ -220,6 +230,13 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC,

// Special diagnostic for protocols and protocol compositions.
if (T->isExistentialType()) {
// No protocol is representable in C.
if (language == ForeignLanguage::C) {
diags.diagnose(TypeRange.Start, diag::cdecl_incompatible_with_protocols)
.limitBehavior(behavior);
return;
}

if (T->isAny()) {
// Any is not @objc.
diags.diagnose(TypeRange.Start,
Expand Down Expand Up @@ -267,28 +284,32 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC,
}

if (T->is<ArchetypeType>() || T->isTypeParameter()) {
diags.diagnose(TypeRange.Start, diag::not_objc_generic_type_param)
diags.diagnose(TypeRange.Start, diag::not_objc_generic_type_param,
language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
}

if (auto fnTy = T->getAs<FunctionType>()) {
if (fnTy->getExtInfo().isAsync()) {
diags.diagnose(TypeRange.Start, diag::not_objc_function_type_async)
diags.diagnose(TypeRange.Start, diag::not_objc_function_type_async,
language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
}

if (fnTy->getExtInfo().isThrowing()) {
diags.diagnose(TypeRange.Start, diag::not_objc_function_type_throwing)
diags.diagnose(TypeRange.Start, diag::not_objc_function_type_throwing,
language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
}

diags.diagnose(TypeRange.Start, diag::not_objc_function_type_param)
diags.diagnose(TypeRange.Start, diag::not_objc_function_type_param,
language)
.highlight(TypeRange)
.limitBehavior(behavior);
return;
Expand All @@ -305,25 +326,26 @@ static void diagnoseFunctionParamNotRepresentable(
softenIfAccessNote(AFD, Reason.getAttr(),
AFD->diagnose(diag::objc_invalid_on_func_single_param_type,
AFD, getObjCDiagnosticAttrKind(Reason),
(unsigned)language)
language)
.limitBehavior(behavior));
} else {
softenIfAccessNote(AFD, Reason.getAttr(),
AFD->diagnose(diag::objc_invalid_on_func_param_type,
AFD, ParamIndex + 1, getObjCDiagnosticAttrKind(Reason),
(unsigned)language)
language)
.limitBehavior(behavior));
}
SourceRange SR;

if (P->hasAttachedPropertyWrapper()) {
auto wrapperTy = P->getPropertyWrapperBackingPropertyType();
SR = P->getOutermostAttachedPropertyWrapper()->getRange();
diagnoseTypeNotRepresentableInObjC(AFD, wrapperTy, SR, behavior);
diagnoseTypeNotRepresentableInObjC(AFD, wrapperTy, SR, behavior, Reason);
} else {
if (auto typeRepr = P->getTypeRepr())
SR = typeRepr->getSourceRange();
diagnoseTypeNotRepresentableInObjC(AFD, P->getTypeInContext(), SR, behavior);
diagnoseTypeNotRepresentableInObjC(AFD, P->getTypeInContext(), SR,
behavior, Reason);
}
Reason.describe(AFD);
}
Expand All @@ -345,7 +367,7 @@ static bool isParamListRepresentableInLanguage(const AbstractFunctionDecl *AFD,
if (param->isVariadic()) {
softenIfAccessNote(AFD, Reason.getAttr(),
diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_variadic,
getObjCDiagnosticAttrKind(Reason))
AFD, getObjCDiagnosticAttrKind(Reason))
.highlight(param->getSourceRange())
.limitBehavior(behavior));
Reason.describe(AFD);
Expand All @@ -358,7 +380,7 @@ static bool isParamListRepresentableInLanguage(const AbstractFunctionDecl *AFD,
softenIfAccessNote(AFD, Reason.getAttr(),
diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_inout,
AFD, getObjCDiagnosticAttrKind(Reason),
(unsigned)language)
language)
.highlight(param->getSourceRange())
.limitBehavior(behavior));
Reason.describe(AFD);
Expand Down Expand Up @@ -800,11 +822,11 @@ bool swift::isRepresentableInLanguage(
softenIfAccessNote(AFD, Reason.getAttr(),
AFD->diagnose(diag::objc_invalid_on_func_result_type,
FD, getObjCDiagnosticAttrKind(Reason),
(unsigned)language)
language)
.limitBehavior(behavior));
diagnoseTypeNotRepresentableInObjC(FD, ResultType,
FD->getResultTypeSourceRange(),
behavior);
behavior, Reason);
Reason.describe(FD);
return false;
}
Expand Down Expand Up @@ -857,11 +879,11 @@ bool swift::isRepresentableInLanguage(
softenIfAccessNote(AFD, Reason.getAttr(),
AFD->diagnose(diag::objc_invalid_on_func_result_type,
FD, getObjCDiagnosticAttrKind(Reason),
(unsigned)language)
language)
.limitBehavior(behavior));
diagnoseTypeNotRepresentableInObjC(FD, type,
FD->getResultTypeSourceRange(),
behavior);
behavior, Reason);
Reason.describe(FD);

return true;
Expand Down Expand Up @@ -1168,7 +1190,7 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) {
.limitBehavior(behavior));
diagnoseTypeNotRepresentableInObjC(VD->getDeclContext(),
VD->getInterfaceType(),
TypeRange, behavior);
TypeRange, behavior, Reason);
Reason.describe(VD);
}

Expand Down Expand Up @@ -1256,7 +1278,7 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) {
diagnoseTypeNotRepresentableInObjC(SD->getDeclContext(),
!IndexResult ? IndexType
: ElementType,
TypeRange, behavior);
TypeRange, behavior, Reason);
Reason.describe(SD);
}

Expand Down
Loading