Skip to content

Commit 8371586

Browse files
committed
Parse and serialize @abi attribute
This attribute will allow you to specify an alternate version of the declaration used for mangling. It will allow minor adjustments to be made to declarations so long as they’re still compatible at the calling convention level, such as refining isolation or sendability, renaming without breaking ABI, etc. The attribute is behind the experimental feature flag `ABIAttribute`.
1 parent 52693c4 commit 8371586

File tree

19 files changed

+745
-85
lines changed

19 files changed

+745
-85
lines changed

include/swift/AST/Attr.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ class DeclAttribute : public AttributeBase {
219219

220220
NumFeatures : 31
221221
);
222+
223+
SWIFT_INLINE_BITFIELD(ABIAttr, DeclAttribute, 1,
224+
isInverse : 1
225+
);
222226
} Bits;
223227
// clang-format on
224228

@@ -2903,6 +2907,41 @@ class AllowFeatureSuppressionAttr final
29032907
UNIMPLEMENTED_CLONE(AllowFeatureSuppressionAttr)
29042908
};
29052909

2910+
/// Defines the @abi attribute.
2911+
class ABIAttr : public DeclAttribute {
2912+
public:
2913+
ABIAttr(Decl *abiDecl, SourceLoc AtLoc, SourceRange Range,
2914+
bool IsInverse, bool Implicit)
2915+
: DeclAttribute(DeclAttrKind::ABI, AtLoc, Range, Implicit),
2916+
abiDecl(abiDecl)
2917+
{
2918+
Bits.ABIAttr.isInverse = IsInverse;
2919+
}
2920+
2921+
ABIAttr(Decl *abiDecl, bool IsInverse, bool Implicit)
2922+
: ABIAttr(abiDecl, SourceLoc(), SourceRange(), IsInverse, Implicit) {}
2923+
2924+
/// The declaration which will be used to compute a mangled name.
2925+
///
2926+
/// \note For a \c VarDecl with a parent \c PatternBindingDecl , this should
2927+
/// point to the parent \c PatternBindingDecl . (That accommodates the way
2928+
/// sibling \c VarDecl s share attributes.)
2929+
Decl *abiDecl;
2930+
2931+
/// Is this attribute attached to the ABI declaration and pointing back to
2932+
/// the original? Inverse \c ABIAttr s are always implicit and result in an
2933+
/// \c ABIRole with \c providesAPI() but not \c providesABI() .
2934+
bool isInverse() const { return Bits.ABIAttr.isInverse; }
2935+
2936+
void connectToInverse(Decl *owner) const;
2937+
2938+
static bool classof(const DeclAttribute *DA) {
2939+
return DA->getKind() == DeclAttrKind::ABI;
2940+
}
2941+
2942+
UNIMPLEMENTED_CLONE(ABIAttr)
2943+
};
2944+
29062945
/// Attributes that may be applied to declarations.
29072946
class DeclAttributes {
29082947
/// Linked list of declaration attributes.

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,6 +2638,11 @@ class PatternBindingDecl final : public Decl,
26382638
/// Returns \c true if this pattern binding was created by the debugger.
26392639
bool isDebuggerBinding() const { return Bits.PatternBindingDecl.IsDebugger; }
26402640

2641+
/// Returns the \c VarDecl in this PBD at the same offset in the same
2642+
/// pattern entry as \p otherVar is in its PBD, or \c nullptr if this PBD is
2643+
/// too different from \p otherVar 's to find an equivalent variable.
2644+
VarDecl *getVarAtSimilarStructuralPosition(VarDecl *otherVar);
2645+
26412646
static bool classof(const Decl *D) {
26422647
return D->getKind() == DeclKind::PatternBinding;
26432648
}

include/swift/AST/DeclAttr.def

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,11 @@ DECL_ATTR(lifetime, Lifetime,
513513
OnAccessor | OnConstructor | OnFunc | OnSubscript | LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | AllowMultipleAttributes,
514514
161)
515515

516-
LAST_DECL_ATTR(Lifetime)
516+
DECL_ATTR(abi, ABI,
517+
OnAbstractFunction | OnVar /* will eventually add types */ | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
518+
162)
519+
520+
LAST_DECL_ATTR(ABI)
517521

518522
#undef DECL_ATTR_ALIAS
519523
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ ERROR(let_cannot_be_addressed_property,none,
228228
ERROR(disallowed_var_multiple_getset,none,
229229
"'var' declarations with multiple variables cannot have explicit"
230230
" getters/setters", ())
231+
ERROR(stub_decl_cannot_have_body,none,
232+
"stub %0 cannot have body",
233+
(DescriptiveDeclKind))
231234

232235
ERROR(disallowed_init,none,
233236
"initial value is not allowed here", ())

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,9 @@ EXPERIMENTAL_FEATURE(GenerateForceToMainActorThunks, false)
432432

433433
EXPERIMENTAL_FEATURE(AddressableParameters, true)
434434

435+
/// Allow the @abi attribute.
436+
EXPERIMENTAL_FEATURE(ABIAttribute, true)
437+
435438
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
436439
#undef EXPERIMENTAL_FEATURE
437440
#undef UPCOMING_FEATURE

include/swift/Parse/Parser.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ class Parser {
933933
bool isStartOfGetSetAccessor();
934934

935935
/// Flags that control the parsing of declarations.
936-
enum ParseDeclFlags {
936+
enum ParseDeclFlags : uint16_t {
937937
PD_Default = 0,
938938
PD_AllowTopLevel = 1 << 1,
939939
PD_HasContainerType = 1 << 2,
@@ -944,6 +944,7 @@ class Parser {
944944
PD_InExtension = 1 << 7,
945945
PD_InStruct = 1 << 8,
946946
PD_InEnum = 1 << 9,
947+
PD_StubOnly = 1 << 10,
947948
};
948949

949950
/// Options that control the parsing of declarations.
@@ -953,20 +954,24 @@ class Parser {
953954

954955
ParserStatus parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
955956
bool IfConfigsAreDeclAttrs,
956-
llvm::function_ref<void(Decl *)> Handler);
957+
llvm::function_ref<void(Decl *)> Handler,
958+
bool stubOnly = false);
957959

958960
std::pair<std::vector<Decl *>, std::optional<Fingerprint>>
959961
parseDeclListDelayed(IterableDeclContext *IDC);
960962

961963
bool parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc,
962964
Diag<> LBraceDiag, Diag<> RBraceDiag,
963-
IterableDeclContext *IDC);
965+
IterableDeclContext *IDC,
966+
ParseDeclOptions Flags);
964967

965968
bool canDelayMemberDeclParsing(bool &HasOperatorDeclarations,
966969
bool &HasNestedClassDeclarations,
967-
bool &HasDerivativeDeclarations);
970+
bool &HasDerivativeDeclarations,
971+
ParseDeclOptions Flags);
968972

969-
bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations);
973+
bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations,
974+
ParseDeclOptions Flags);
970975

971976
bool delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc,
972977
IterableDeclContext *IDC);
@@ -1333,7 +1338,8 @@ class Parser {
13331338
bool HasFuncKeyword = true);
13341339
BodyAndFingerprint
13351340
parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD);
1336-
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
1341+
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD,
1342+
ParseDeclOptions Flags);
13371343
BodyAndFingerprint
13381344
parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
13391345

lib/AST/ASTDumper.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -966,9 +966,10 @@ namespace {
966966
}
967967

968968
/// Print an unnamed field containing a node's name, read from a declaration.
969-
void printDeclName(const ValueDecl *D, bool leadingSpace = true) {
970-
if (D->getName()) {
971-
printName(D->getName(), leadingSpace);
969+
void printDeclName(const Decl *D, bool leadingSpace = true) {
970+
auto VD = dyn_cast<ValueDecl>(D);
971+
if (VD && VD->getName()) {
972+
printName(VD->getName(), leadingSpace);
972973
} else {
973974
if (leadingSpace)
974975
OS << ' ';
@@ -978,7 +979,7 @@ namespace {
978979
}
979980

980981
/// Print a field containing a node's name, read from a declaration.
981-
void printDeclNameField(const ValueDecl *D, StringRef name) {
982+
void printDeclNameField(const Decl *D, StringRef name) {
982983
printFieldRaw([&](raw_ostream &os) {
983984
printDeclName(D, /*leadingSpace=*/false);
984985
}, name);
@@ -3864,6 +3865,19 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, StringRef>,
38643865

38653866
#undef TRIVIAL_ATTR_PRINTER
38663867

3868+
void visitABIAttr(ABIAttr *Attr, StringRef label) {
3869+
printCommon(Attr, "abi_attr", label);
3870+
3871+
if (Attr->isInverse()) {
3872+
// Inverse attrs point to a parent, so we can't fully dump them.
3873+
printDeclNameField(Attr->abiDecl, "inverse_decl");
3874+
} else {
3875+
printRec(Attr->abiDecl, "decl");
3876+
}
3877+
3878+
printFoot();
3879+
3880+
}
38673881
void visitAccessControlAttr(AccessControlAttr *Attr, StringRef label) {
38683882
printCommon(Attr, "access_control_attr", label);
38693883
printField(Attr->getAccess(), "access_level");

lib/AST/Attr.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
802802
if (Options.excludeAttr(DA))
803803
continue;
804804

805+
// Skip inverse ABIAttrs--these are an implementation detail.
806+
if (auto abiAttr = dyn_cast<ABIAttr>(DA))
807+
if (abiAttr->isInverse())
808+
continue;
809+
805810
// In the public interfaces of -library-level=api modules, skip attributes
806811
// that reference SPI platforms.
807812
if (Options.printPublicInterface() && libraryLevelAPI &&
@@ -1022,6 +1027,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
10221027
}
10231028
break;
10241029
}
1030+
1031+
case DeclAttrKind::ABI:
1032+
// Inverse attributes should never be printed.
1033+
if (cast<ABIAttr>(this)->isInverse())
1034+
return false;
1035+
break;
1036+
10251037
default:
10261038
break;
10271039
}
@@ -1584,6 +1596,17 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
15841596
break;
15851597
}
15861598

1599+
case DeclAttrKind::ABI: {
1600+
auto *attr = cast<ABIAttr>(this);
1601+
Printer << "@abi(";
1602+
ASSERT(!attr->isInverse() && "should be skipped above");
1603+
if (attr->abiDecl)
1604+
attr->abiDecl->print(Printer, Options);
1605+
Printer << ")";
1606+
1607+
break;
1608+
}
1609+
15871610
#define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS:
15881611
#include "swift/AST/DeclAttr.def"
15891612
llvm_unreachable("handled above");
@@ -1629,6 +1652,8 @@ StringRef DeclAttribute::getAttrName() const {
16291652
case DeclAttrKind::CLASS: \
16301653
return #NAME;
16311654
#include "swift/AST/DeclAttr.def"
1655+
case DeclAttrKind::ABI:
1656+
return "abi";
16321657
case DeclAttrKind::SILGenName:
16331658
return "_silgen_name";
16341659
case DeclAttrKind::Alignment:
@@ -2832,6 +2857,38 @@ LifetimeAttr *LifetimeAttr::create(ASTContext &context, SourceLoc atLoc,
28322857
return new (context) LifetimeAttr(atLoc, baseRange, implicit, entry);
28332858
}
28342859

2860+
void ABIAttr::connectToInverse(Decl *owner) const {
2861+
if (isInverse())
2862+
return;
2863+
2864+
// If necessary, convert abiDecl's PBD to a specific VarDecl.
2865+
auto correspondingDecl = abiDecl;
2866+
if (auto PBD = dyn_cast<PatternBindingDecl>(correspondingDecl)) {
2867+
if (!isa<VarDecl>(owner))
2868+
// Invalid code (e.g. `@abi(var x) func x()`). Won't be able to connect.
2869+
return;
2870+
2871+
correspondingDecl =
2872+
PBD->getVarAtSimilarStructuralPosition(cast<VarDecl>(owner));
2873+
}
2874+
2875+
if (!correspondingDecl)
2876+
return;
2877+
2878+
auto inverseAttr = correspondingDecl->getAttrs().getAttribute<ABIAttr>();
2879+
ASSERT(inverseAttr && inverseAttr->isInverse());
2880+
2881+
// Reverse of the above: If we're trying to connect to a VarDecl, connect to
2882+
// its PBD instead.
2883+
auto inverseCorrespondingDecl = owner;
2884+
if (auto VD = dyn_cast<VarDecl>(inverseCorrespondingDecl))
2885+
if (auto PBD = VD->getParentPatternBinding())
2886+
inverseCorrespondingDecl = PBD;
2887+
2888+
inverseAttr->abiDecl = inverseCorrespondingDecl;
2889+
}
2890+
2891+
28352892
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
28362893
if (attr)
28372894
attr->print(out);

lib/AST/Decl.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4244,6 +4244,43 @@ void ValueDecl::setRenamedDecl(const AvailableAttr *attr,
42444244
std::move(renameDecl));
42454245
}
42464246

4247+
VarDecl *PatternBindingDecl::
4248+
getVarAtSimilarStructuralPosition(VarDecl *otherVar) {
4249+
auto otherPBD = otherVar->getParentPatternBinding();
4250+
4251+
if (!otherPBD)
4252+
return getSingleVar();
4253+
if (otherPBD == this)
4254+
return otherVar;
4255+
4256+
// Find the entry index and sibling index for `otherVar` within its PBD.
4257+
auto entryIndex = otherPBD->getPatternEntryIndexForVarDecl(otherVar);
4258+
4259+
SmallVector<VarDecl *, 16> otherSiblings;
4260+
otherPBD->getPattern(entryIndex)->collectVariables(otherSiblings);
4261+
size_t siblingIndex =
4262+
llvm::find(otherSiblings, otherVar) - otherSiblings.begin();
4263+
4264+
ASSERT(siblingIndex != otherSiblings.size()
4265+
&& "otherVar not in its own pattern?");
4266+
4267+
// Look up the same entry index and sibling index in this PBD, returning null
4268+
// if that index doesn't exist.
4269+
4270+
// Corresponding PBD has more patterns in it
4271+
if (entryIndex >= getNumPatternEntries())
4272+
return nullptr;
4273+
4274+
SmallVector<VarDecl *, 16> siblings;
4275+
getPattern(entryIndex)->collectVariables(siblings);
4276+
4277+
// Corresponding pattern has more vars in it
4278+
if (siblingIndex >= siblings.size())
4279+
return nullptr;
4280+
4281+
return siblings[siblingIndex];
4282+
}
4283+
42474284
SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const {
42484285
// Some decls have a parent/child split where the introducer keyword is on the
42494286
// parent, but the attributes are on the children. If this is a child in such

lib/AST/FeatureSet.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,11 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) {
319319
return decl->getAttrs().hasAttribute<UnsafeAttr>();
320320
}
321321

322+
static bool usesFeatureABIAttribute(Decl *decl) {
323+
auto abiAttr = decl->getAttrs().getAttribute<ABIAttr>();
324+
return abiAttr && !abiAttr->isInverse();
325+
}
326+
322327
UNINTERESTING_FEATURE(WarnUnsafe)
323328
UNINTERESTING_FEATURE(SafeInterop)
324329
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)

0 commit comments

Comments
 (0)