Skip to content

@abi checking #79466

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 22 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cd61fd4
[NFC] Fix ImplementsAttr::clone()
beccadax Dec 21, 2024
1af5c04
[NFC] Hoist getTypeSourceRangeForDiagnostics()
beccadax Feb 21, 2025
d8349e7
[NFC] Expose default param specifier computation
beccadax Feb 21, 2025
b4d545f
[NFC] Fix AllowFeatureSuppressionAttr atLoc
beccadax Feb 28, 2025
775bdac
[NFC] Extract Decl::getExplicitObjCName()
beccadax Mar 12, 2025
5bb6245
[NFC] Check that SILGen doesn’t use ABI-only decls
beccadax Mar 12, 2025
3baba0b
Type check `@abi` decls (sans attrs)
beccadax Mar 19, 2025
1bb2186
Inherit @objc, dynamic, @implementation in `@abi`
beccadax Mar 19, 2025
9be9b6f
Inherit access control in `@abi`
beccadax Mar 12, 2025
e88b99a
Inherit availability in `@abi`
beccadax Mar 19, 2025
63be84a
Inherit `override` in `@abi`
beccadax Mar 12, 2025
123447d
Don’t add HasStorageAttr to ABI-only decls
beccadax Mar 12, 2025
495d464
Add DeclAttribute::isEquivalent()
beccadax Mar 13, 2025
32b8a11
Add flags to specify `@abi` behavior of attributes
beccadax Mar 12, 2025
132f491
Check attributes in `@abi` attr
beccadax Mar 19, 2025
d227636
Support `@abi` on subscripts
beccadax Mar 5, 2025
7703d11
Filter bad attrs out of module interface `@abi`s
beccadax Mar 19, 2025
ef738e6
Make `@abi` type diagnostics more specific
beccadax Mar 19, 2025
446dc9d
Diagnose mismatched typed throws in `@abi`
beccadax Mar 15, 2025
7159111
Improve diagnosis of generic types in `@abi`
beccadax Mar 15, 2025
d5fac9f
Tweak `@abi` `@lifetime` test
beccadax Mar 24, 2025
6759ad5
Add `@abi` behavior for `@constInitialized`
beccadax Mar 25, 2025
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
273 changes: 266 additions & 7 deletions include/swift/AST/Attr.h

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions include/swift/AST/AttrKind.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ enum class DeclAttrKind : unsigned {
#include "swift/AST/DeclAttr.def"
};

StringRef getDeclAttrKindID(DeclAttrKind kind);

enum : unsigned {
NumDeclAttrKinds = static_cast<unsigned>(DeclAttrKind::Last_DeclAttr) + 1,
NumDeclAttrKindBits = countBitsUsed(NumDeclAttrKinds - 1),
Expand Down
18 changes: 11 additions & 7 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
/// from source code.
void attachParsedAttrs(DeclAttributes attrs);

/// Retrieve the custom name in the \c @objc attribute, if present.
std::optional<ObjCSelector>
getExplicitObjCName(bool allowInvalid = false) const;

/// True if this declaration provides an implementation for an imported
/// Objective-C declaration. This implies various restrictions and special
/// behaviors for it and, if it's an extension, its members.
Expand Down Expand Up @@ -5919,6 +5923,13 @@ class AbstractStorageDecl : public ValueDecl {
/// Return the interface type of the stored value.
Type getValueInterfaceType() const;

/// Retrieve the source range of the variable type, or an invalid range if the
/// variable's type is not explicitly written in the source.
///
/// Only for use in diagnostics. It is not always possible to always
/// precisely point to the variable type because of type aliases.
SourceRange getTypeSourceRangeForDiagnostics() const;

/// Determine how this storage is implemented.
StorageImplInfo getImplInfo() const;

Expand Down Expand Up @@ -6353,13 +6364,6 @@ class VarDecl : public AbstractStorageDecl {
/// and not just getInterfaceType().
Type getTypeInContext() const;

/// Retrieve the source range of the variable type, or an invalid range if the
/// variable's type is not explicitly written in the source.
///
/// Only for use in diagnostics. It is not always possible to always
/// precisely point to the variable type because of type aliases.
SourceRange getTypeSourceRangeForDiagnostics() const;

/// Determine the mutability of this variable declaration when
/// accessed from a given declaration context.
StorageMutability mutability(
Expand Down
315 changes: 158 additions & 157 deletions include/swift/AST/DeclAttr.def

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,10 @@ namespace swift {
/// Flush the active diagnostic to the diagnostic output engine.
void flush();

/// Returns the \c SourceManager associated with \c SourceLoc s for this
/// diagnostic.
SourceManager &getSourceManager();

/// Prevent the diagnostic from behaving more severely than \p limit. For
/// instance, if \c DiagnosticBehavior::Warning is passed, an error will be
/// emitted as a warning, but a note will still be emitted as a note.
Expand Down Expand Up @@ -1561,6 +1565,10 @@ namespace swift {
}
};

inline SourceManager &InFlightDiagnostic::getSourceManager() {
return Engine->SourceMgr;
}

/// Remember details about the state of a diagnostic engine and restore them
/// when the object is destroyed.
///
Expand Down
86 changes: 81 additions & 5 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8351,16 +8351,16 @@ ERROR(attr_abi_mismatched_kind,none,

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

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

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

ERROR(attr_abi_mismatched_pbd_size,none,
"cannot give pattern binding the ABI of a binding with "
Expand All @@ -8369,13 +8369,89 @@ ERROR(attr_abi_mismatched_pbd_size,none,

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

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

ERROR(attr_abi_missing_attr,none,
"missing '%0' %select{attribute|modifier}1 in '@abi'",
(StringRef, bool))
ERROR(attr_abi_extra_attr,none,
"extra %select{|implicit }2'%0' %select{attribute|modifier}1 in '@abi'",
(StringRef, bool, /*isImplicit=*/bool))
ERROR(attr_abi_forbidden_attr,none,
"unused '%0' %select{attribute|modifier}1 in '@abi'",
(StringRef, bool))
REMARK(abi_attr_inferred_attribute,none,
"inferred '%0' in '@abi' to match %select{attribute|modifier}1 on API",
(StringRef, bool))

ERROR(attr_abi_mismatched_attr,none,
"'%0' %select{attribute|modifier}1 in '@abi' should match '%2'",
(StringRef, bool, StringRef))
NOTE(attr_abi_matching_attr_here,none,
"%select{should match|matches}0 %select{attribute|modifier}1 "
"%select{|implicitly added }2here",
(/*matches=*/bool, /*isModifier=*/bool, /*isImplicit=*/bool))

#define TYPE_ORIGIN(KIND_IDX, DECL_IDX) "%select{|%kind" #DECL_IDX " |" \
"self parameter |result |thrown |%error}" #KIND_IDX
ERROR(attr_abi_mismatched_type,none,
TYPE_ORIGIN(0, 1) "type %2 in '@abi' should match %3",
(unsigned, Decl *, Type, Type))
NOTE(attr_abi_should_match_type_here,none,
"should match type here", ())

ERROR(attr_abi_mismatched_generic_signature,none,
"generic signature '%0' in '@abi' is not compatible with '%1'",
(StringRef, StringRef))
ERROR(attr_abi_missing_generic_signature,none,
"declaration in '@abi' should have generic signature compatible with "
"'%0'",
(StringRef))
ERROR(attr_abi_extra_generic_signature,none,
"declaration in '@abi' should not have generic signature because %0 "
"is not generic",
(Decl *))

ERROR(attr_abi_mismatched_param_modifier,none,
"%select{default |}0%select{attribute|modifier}2 %select{|'%0' }0"
"on " TYPE_ORIGIN(3, 4) "in '@abi' is not compatible with "
"%select{default|'%1'}1",
(StringRef, StringRef, /*isModifier=*/bool, unsigned, Decl *))
ERROR(attr_abi_no_default_arguments,none,
"%kind0 in '@abi' should not have a default argument; it does not "
"affect the parameter's ABI",
(Decl *))

// These macros insert 'final', 'non-final', or nothing depending on both the
// current decl and its counterpart, such that 'non-final' is used if the
// counterpart would be described as 'final' or 'static'. They must be kept in
// sync with `StaticnessAndFinality`.
#define NONFINAL_OR_NOTHING(COUNTERPART) \
"%select{||non-final |non-final |non-final |%error}" #COUNTERPART
#define FINAL_OR_NONFINAL_OR_NOTHING(CURRENT, COUNTERPART, FINAL_OK) \
"%select{|%select{" NONFINAL_OR_NOTHING(COUNTERPART) \
"|" NONFINAL_OR_NOTHING(COUNTERPART) \
"|final |final ||%error}" #CURRENT "}" #FINAL_OK

ERROR(attr_abi_static_final_mismatch,none,
FINAL_OR_NONFINAL_OR_NOTHING(0, 2, 4) "%kind1 in '@abi' should be "
FINAL_OR_NONFINAL_OR_NOTHING(2, 0, 4) "%kindonly3 to ensure ABI "
"compatibility",
(uint8_t, Decl *, uint8_t, Decl *, /*isClass=*/bool))

#undef NONFINAL_OR_NOTHING
#undef FINAL_OR_NONFINAL_OR_NOTHING

ERROR(attr_abi_failable_mismatch,none,
"cannot give %select{non-failable|failable}1 %kind0 the ABI of a "
"%select{non-failable|failable}2 %kindonly0",
(Decl *, bool, bool))

//===----------------------------------------------------------------------===//
// MARK: Isolated conformances
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,10 @@ enum class ParamSpecifier : uint8_t {

StringRef getNameForParamSpecifier(ParamSpecifier name);

/// What does \c ParamSpecifier::Default mean for a parameter that's directly
/// attached to \p VD ? Pass \c nullptr for the value for a closure.
ParamSpecifier getDefaultParamSpecifier(const ValueDecl *VD);

/// Provide parameter type relevant flags, i.e. variadic, autoclosure, and
/// escaping.
class ParameterTypeFlags {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ namespace swift {
/// Emit a remark on early exit in explicit interface build
bool EnableSkipExplicitInterfaceModuleBuildRemarks = false;

/// Emit a remark when \c \@abi infers an attribute or modifier.
bool EnableABIInferenceRemarks = false;

///
/// Support for alternate usage modes
///
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,10 @@ def remark_module_serialization : Flag<["-"], "Rmodule-serialization">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
HelpText<"Emit remarks about module serialization">;

def remark_abi_inference : Flag<["-"], "Rabi-inference">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
HelpText<"Emit a remark when an '@abi' attribute adds an attribute or modifier to the ABI declaration based on its presence in the API">;

def emit_tbd : Flag<["-"], "emit-tbd">,
HelpText<"Emit a TBD file">,
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>;
Expand Down
13 changes: 5 additions & 8 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1193,11 +1193,9 @@ getOverriddenSwiftProtocolObjCName(const ValueDecl *decl,
return std::nullopt;

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

return std::nullopt;
Expand Down Expand Up @@ -3379,8 +3377,7 @@ void ASTMangler::appendFunctionSignature(AnyFunctionType *fn,
}
}

static ParamSpecifier
getDefaultOwnership(const ValueDecl *forDecl) {
ParamSpecifier swift::getDefaultParamSpecifier(const ValueDecl *forDecl) {
// `consuming` is the default ownership for initializers and setters.
// Everything else defaults to borrowing.
if (!forDecl) {
Expand Down Expand Up @@ -3451,7 +3448,7 @@ getParameterFlagsForMangling(ParameterTypeFlags flags,
void ASTMangler::appendFunctionInputType(
AnyFunctionType *fnType, ArrayRef<AnyFunctionType::Param> params,
GenericSignature sig, const ValueDecl *forDecl, bool isRecursedInto) {
auto defaultSpecifier = getDefaultOwnership(forDecl);
auto defaultSpecifier = getDefaultParamSpecifier(forDecl);

switch (params.size()) {
case 0:
Expand Down
14 changes: 12 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,15 @@ void PrintAST::printAttributes(const Decl *D) {
// for each decl They cannot be shared across different decls.
assert(Options.ExcludeCustomAttrList.empty());

// If there is an `@abi` attr, we need to print it first so that it isn't
// affected by subsequent mutation of `Options.ExcludeAttrList`.
if (auto abiAttr = attrs.getAttribute<ABIAttr>()) {
if (Options.PrintImplicitAttrs && !Options.excludeAttr(abiAttr)) {
abiAttr->print(Printer, Options, D);
Options.ExcludeAttrList.push_back(DeclAttrKind::ABI);
}
}

if (Options.PrintSyntheticSILGenName
&& !D->getAttrs().hasAttribute<SILGenNameAttr>()) {
if (canPrintSyntheticSILGenName(D)) {
Expand Down Expand Up @@ -1334,7 +1343,8 @@ void PrintAST::printAttributes(const Decl *D) {
// Add SPIs to both private and package interfaces
if (!Options.printPublicInterface() &&
DeclAttribute::canAttributeAppearOnDeclKind(
DeclAttrKind::SPIAccessControl, D->getKind())) {
DeclAttrKind::SPIAccessControl, D->getKind()) &&
!Options.excludeAttrKind(DeclAttrKind::SPIAccessControl)) {
interleave(D->getSPIGroups(),
[&](Identifier spiName) {
Printer.printAttrName("_spi", true);
Expand All @@ -1358,7 +1368,7 @@ void PrintAST::printAttributes(const Decl *D) {
// If the declaration is implicitly @objc, print the attribute now.
if (auto VD = dyn_cast<ValueDecl>(D)) {
if (VD->isObjC() && !isa<EnumElementDecl>(VD) &&
!attrs.hasAttribute<ObjCAttr>()) {
!attrs.hasAttribute<ObjCAttr>() && ABIRoleInfo(D).providesAPI()) {
Printer.printAttrName("@objc");
Printer << " ";
}
Expand Down
11 changes: 11 additions & 0 deletions lib/AST/AccessRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ AccessLevel
AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const {
assert(!D->hasAccess());

// ABI decls share the access level of their API decl.
auto abiRole = ABIRoleInfo(D);
if (!abiRole.providesAPI() && abiRole.getCounterpart())
return abiRole.getCounterpart()->getFormalAccess();

// Check if the decl has an explicit access control attribute.
if (auto *AA = D->getAttrs().getAttribute<AccessControlAttr>())
return AA->getAccess();
Expand Down Expand Up @@ -201,6 +206,12 @@ AccessLevel
SetterAccessLevelRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *ASD) const {
assert(!ASD->Accessors.getInt().hasValue());

// ABI decls share the access level of their API decl.
auto abiRole = ABIRoleInfo(ASD);
if (!abiRole.providesAPI() && abiRole.getCounterpart())
return abiRole.getCounterpart()->getSetterFormalAccess();

if (auto *SAA = ASD->getAttrs().getAttribute<SetterAccessAttr>())
return SAA->getAccess();

Expand Down
Loading