Skip to content

Commit 1822c33

Browse files
committed
Type check @abi for compatibility
This commit compares the decl inside the `@abi` attribute to the decl it’s attached to, diagnosing ABI-incompatible differences or attributes that don’t need to be specified. It includes logic which ensures that some of the fundamental properties of a declaration, such as its availability, access control, and @objc-ness, are shared between an ABI-only declaration and its counterpart. (Specifically, the ABI-only declaration delegates the computation to its counterpart.) This allows these attributes to be omitted from the `@abi` attribute instead of forcing developers to repeat themselves.
1 parent 5d5eca9 commit 1822c33

34 files changed

+4028
-136
lines changed

include/swift/AST/Attr.h

+215
Large diffs are not rendered by default.

include/swift/AST/Decl.h

+4
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
10471047
/// from source code.
10481048
void attachParsedAttrs(DeclAttributes attrs);
10491049

1050+
/// Retrieve the custom name in the \c @objc attribute, if present.
1051+
std::optional<ObjCSelector>
1052+
getExplicitObjCName(bool allowInvalid = false) const;
1053+
10501054
/// True if this declaration provides an implementation for an imported
10511055
/// Objective-C declaration. This implies various restrictions and special
10521056
/// behaviors for it and, if it's an extension, its members.

include/swift/AST/DiagnosticsSema.def

+78-5
Original file line numberDiff line numberDiff line change
@@ -8236,16 +8236,16 @@ ERROR(attr_abi_mismatched_kind,none,
82368236

82378237
ERROR(attr_abi_mismatched_arity,none,
82388238
"cannot give %kind0 the ABI of a %kindonly0 with a different number of "
8239-
"low-level parameters",
8240-
(ValueDecl *))
8239+
"%select{|generic }1parameters",
8240+
(Decl *, /*genericParams=*/bool))
82418241

82428242
ERROR(attr_abi_mismatched_throws,none,
82438243
"cannot give %0 the ABI of a %kindonly0 which %select{cannot|can}1 throw",
8244-
(ValueDecl *, /*abiCanThrow=*/bool))
8244+
(Decl *, /*abiCanThrow=*/bool))
82458245

82468246
ERROR(attr_abi_mismatched_async,none,
82478247
"cannot give %0 the ABI of %select{a non-async|an async}1 %kindonly0",
8248-
(ValueDecl *, /*abiIsAsync=*/bool))
8248+
(Decl *, /*abiIsAsync=*/bool))
82498249

82508250
ERROR(attr_abi_mismatched_pbd_size,none,
82518251
"cannot give pattern binding the ABI of a binding with "
@@ -8254,13 +8254,86 @@ ERROR(attr_abi_mismatched_pbd_size,none,
82548254

82558255
ERROR(attr_abi_mismatched_var,none,
82568256
"no match for %select{%kind0 in the ABI|ABI %kind0}1",
8257-
(ValueDecl *, /*isABI=*/bool))
8257+
(Decl *, /*isABI=*/bool))
82588258

82598259
ERROR(attr_abi_incompatible_with_silgen_name,none,
82608260
"cannot use '@_silgen_name' and '@abi' on the same %0 because they serve "
82618261
"the same purpose",
82628262
(DescriptiveDeclKind))
82638263

8264+
ERROR(attr_abi_missing_attr,none,
8265+
"missing '%0' %select{attribute|modifier}1 in '@abi'",
8266+
(StringRef, bool))
8267+
ERROR(attr_abi_extra_attr,none,
8268+
"extra %select{|implicit }2'%0' %select{attribute|modifier}1 in '@abi'",
8269+
(StringRef, bool, /*isImplicit=*/bool))
8270+
ERROR(attr_abi_forbidden_attr,none,
8271+
"unused '%0' %select{attribute|modifier}1 in '@abi'",
8272+
(StringRef, bool))
8273+
REMARK(abi_attr_inferred_attribute,none,
8274+
"inferred '%0' in '@abi' to match %select{attribute|modifier}1 on API",
8275+
(StringRef, bool))
8276+
8277+
ERROR(attr_abi_mismatched_attr,none,
8278+
"'%0' %select{attribute|modifier}1 in '@abi' should match '%2'",
8279+
(StringRef, bool, StringRef))
8280+
NOTE(attr_abi_matching_attr_here,none,
8281+
"%select{should match|matches}0 %select{attribute|modifier}1 "
8282+
"%select{|implicitly added }2here",
8283+
(/*matches=*/bool, /*isModifier=*/bool, /*isImplicit=*/bool))
8284+
8285+
ERROR(attr_abi_mismatched_type,none,
8286+
"type %0 in '@abi' should match %1",
8287+
(Type, Type))
8288+
NOTE(attr_abi_should_match_type_here,none,
8289+
"should match type here", ())
8290+
8291+
ERROR(attr_abi_mismatched_generic_signature,none,
8292+
"generic signature '%0' in '@abi' is not compatible with '%1'",
8293+
(StringRef, StringRef))
8294+
ERROR(attr_abi_missing_generic_signature,none,
8295+
"declaration in '@abi' should have generic signature compatible with "
8296+
"'%0'",
8297+
(StringRef))
8298+
ERROR(attr_abi_extra_generic_signature,none,
8299+
"declaration in '@abi' should not have generic signature because %0 "
8300+
"is not generic",
8301+
(Decl *))
8302+
8303+
ERROR(attr_abi_mismatched_param_modifier,none,
8304+
"%select{default |}0%3 %select{attribute|modifier}2 "
8305+
"%select{|'%0' }0in '@abi' is not compatible with %select{default|'%1'}1",
8306+
(StringRef, StringRef, /*isModifier=*/bool, DescriptiveDeclKind))
8307+
ERROR(attr_abi_no_default_arguments,none,
8308+
"%kind0 in '@abi' should not have a default argument; it does not "
8309+
"affect the parameter's ABI",
8310+
(Decl *))
8311+
8312+
// These macros insert 'final', 'non-final', or nothing depending on both the
8313+
// current decl and its counterpart, such that 'non-final' is used if the
8314+
// counterpart would be described as 'final' or 'static'. They must be kept in
8315+
// sync with `StaticnessAndFinality`.
8316+
#define NONFINAL_OR_NOTHING(COUNTERPART) \
8317+
"%select{||non-final |non-final |non-final |%error}" #COUNTERPART
8318+
#define FINAL_OR_NONFINAL_OR_NOTHING(CURRENT, COUNTERPART, FINAL_OK) \
8319+
"%select{|%select{" NONFINAL_OR_NOTHING(COUNTERPART) \
8320+
"|" NONFINAL_OR_NOTHING(COUNTERPART) \
8321+
"|final |final ||%error}" #CURRENT "}" #FINAL_OK
8322+
8323+
ERROR(attr_abi_static_final_mismatch,none,
8324+
FINAL_OR_NONFINAL_OR_NOTHING(0, 2, 4) "%kind1 in '@abi' should be "
8325+
FINAL_OR_NONFINAL_OR_NOTHING(2, 0, 4) "%kindonly3 to ensure ABI "
8326+
"compatibility",
8327+
(uint8_t, Decl *, uint8_t, Decl *, /*isClass=*/bool))
8328+
8329+
#undef NONFINAL_OR_NOTHING
8330+
#undef FINAL_OR_NONFINAL_OR_NOTHING
8331+
8332+
ERROR(attr_abi_failable_mismatch,none,
8333+
"cannot give %select{non-failable|failable}1 %kind0 the ABI of a "
8334+
"%select{non-failable|failable}2 %kindonly0",
8335+
(Decl *, bool, bool))
8336+
82648337
//===----------------------------------------------------------------------===//
82658338
// MARK: @execution Attribute
82668339
//===----------------------------------------------------------------------===//

include/swift/Basic/LangOptions.h

+3
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ namespace swift {
264264
/// Emit a remark on early exit in explicit interface build
265265
bool EnableSkipExplicitInterfaceModuleBuildRemarks = false;
266266

267+
/// Emit a remark when \c \@abi infers an attribute or modifier.
268+
bool EnableABIInferenceRemarks = false;
269+
267270
///
268271
/// Support for alternate usage modes
269272
///

include/swift/Option/Options.td

+4
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ def remark_module_serialization : Flag<["-"], "Rmodule-serialization">,
468468
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
469469
HelpText<"Emit remarks about module serialization">;
470470

471+
def remark_abi_inference : Flag<["-"], "Rabi-inference">,
472+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
473+
HelpText<"Emit a remark when an '@abi' attribute adds an attribute or modifier to the ABI declaration based on its presence in the API">;
474+
471475
def emit_tbd : Flag<["-"], "emit-tbd">,
472476
HelpText<"Emit a TBD file">,
473477
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>;

lib/AST/ASTMangler.cpp

+3-5
Original file line numberDiff line numberDiff line change
@@ -1128,11 +1128,9 @@ getOverriddenSwiftProtocolObjCName(const ValueDecl *decl,
11281128
return std::nullopt;
11291129

11301130
// If there is an 'objc' attribute with a name, use that name.
1131-
if (auto objc = proto->getAttrs().getAttribute<ObjCAttr>()) {
1132-
if (auto name = objc->getName()) {
1133-
llvm::SmallString<4> buffer;
1134-
return std::string(name->getString(buffer));
1135-
}
1131+
if (auto objcName = proto->getExplicitObjCName()) {
1132+
llvm::SmallString<4> buffer;
1133+
return std::string(objcName->getString(buffer));
11361134
}
11371135

11381136
return std::nullopt;

lib/AST/AccessRequests.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ AccessLevel
4343
AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const {
4444
assert(!D->hasAccess());
4545

46+
// ABI decls share the access level of their API decl.
47+
auto abiRole = ABIRoleInfo(D);
48+
if (!abiRole.providesAPI() && abiRole.getCounterpart())
49+
return abiRole.getCounterpart()->getFormalAccess();
50+
4651
// Check if the decl has an explicit access control attribute.
4752
if (auto *AA = D->getAttrs().getAttribute<AccessControlAttr>())
4853
return AA->getAccess();
@@ -201,6 +206,12 @@ AccessLevel
201206
SetterAccessLevelRequest::evaluate(Evaluator &evaluator,
202207
AbstractStorageDecl *ASD) const {
203208
assert(!ASD->Accessors.getInt().hasValue());
209+
210+
// ABI decls share the access level of their API decl.
211+
auto abiRole = ABIRoleInfo(ASD);
212+
if (!abiRole.providesAPI() && abiRole.getCounterpart())
213+
return abiRole.getCounterpart()->getSetterFormalAccess();
214+
204215
if (auto *SAA = ASD->getAttrs().getAttribute<SetterAccessAttr>())
205216
return SAA->getAccess();
206217

0 commit comments

Comments
 (0)