Skip to content

Commit 26df537

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 adfc376 commit 26df537

File tree

18 files changed

+720
-81
lines changed

18 files changed

+720
-81
lines changed

include/swift/AST/Attr.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ class DeclAttribute : public AttributeBase {
201201

202202
NumFeatures : 31
203203
);
204+
205+
SWIFT_INLINE_BITFIELD(ABIAttr, DeclAttribute, 1,
206+
isInverse : 1
207+
);
204208
} Bits;
205209
// clang-format on
206210

@@ -3303,6 +3307,39 @@ class IsolatedTypeAttr : public SimpleTypeAttrWithArgs<TypeAttrKind::Isolated> {
33033307
void printImpl(ASTPrinter &printer, const PrintOptions &options) const;
33043308
};
33053309

3310+
/// Defines the @abi attribute.
3311+
class ABIAttr : public DeclAttribute {
3312+
public:
3313+
ABIAttr(Decl *abiDecl, SourceLoc AtLoc, SourceRange Range,
3314+
bool IsInverse, bool Implicit)
3315+
: DeclAttribute(DeclAttrKind::ABI, AtLoc, Range, Implicit),
3316+
abiDecl(abiDecl)
3317+
{
3318+
Bits.ABIAttr.isInverse = IsInverse;
3319+
}
3320+
3321+
ABIAttr(Decl *abiDecl, bool IsInverse, bool Implicit)
3322+
: ABIAttr(abiDecl, SourceLoc(), SourceRange(), IsInverse, Implicit) {}
3323+
3324+
/// The declaration which will be used to compute a mangled name.
3325+
///
3326+
/// \note For a \c VarDecl with a parent \c PatternBindingDecl , this should
3327+
/// point to the parent \c PatternBindingDecl . (That accommodates the way
3328+
/// sibling \c VarDecl s share attributes.)
3329+
Decl *abiDecl;
3330+
3331+
/// Is this attribute attached to the ABI declaration and pointing back to
3332+
/// the original? Inverse \c ABIAttr s are always implicit and result in an
3333+
/// \c ABIRole with \c providesAPI() but not \c providesABI() .
3334+
bool isInverse() const { return Bits.ABIAttr.isInverse; }
3335+
3336+
void connectToInverse(Decl *owner) const;
3337+
3338+
static bool classof(const DeclAttribute *DA) {
3339+
return DA->getKind() == DeclAttrKind::ABI;
3340+
}
3341+
};
3342+
33063343
using TypeOrCustomAttr =
33073344
llvm::PointerUnion<CustomAttr*, TypeAttribute*>;
33083345

include/swift/AST/Decl.h

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

2607+
/// Returns the \c VarDecl in this PBD at the same offset in the same
2608+
/// pattern entry as \p otherVar is in its PBD, or \c nullptr if this PBD is
2609+
/// too different from \p otherVar 's to find an equivalent variable.
2610+
VarDecl *getVarAtSimilarStructuralPosition(VarDecl *otherVar);
2611+
26072612
static bool classof(const Decl *D) {
26082613
return D->getKind() == DeclKind::PatternBinding;
26092614
}

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,
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
@@ -423,6 +423,9 @@ EXPERIMENTAL_FEATURE(UnspecifiedMeansMainActorIsolated, false)
423423
/// modify/read single-yield coroutines
424424
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(CoroutineAccessors, true)
425425

426+
/// Allow the @abi attribute.
427+
EXPERIMENTAL_FEATURE(ABIAttribute, true)
428+
426429
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
427430
#undef EXPERIMENTAL_FEATURE
428431
#undef UPCOMING_FEATURE

include/swift/Parse/Parser.h

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

945945
/// Flags that control the parsing of declarations.
946-
enum ParseDeclFlags {
946+
enum ParseDeclFlags : uint16_t {
947947
PD_Default = 0,
948948
PD_AllowTopLevel = 1 << 1,
949949
PD_HasContainerType = 1 << 2,
@@ -954,6 +954,7 @@ class Parser {
954954
PD_InExtension = 1 << 7,
955955
PD_InStruct = 1 << 8,
956956
PD_InEnum = 1 << 9,
957+
PD_StubOnly = 1 << 10,
957958
};
958959

959960
/// Options that control the parsing of declarations.
@@ -964,19 +965,23 @@ class Parser {
964965
ParserStatus parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
965966
bool IfConfigsAreDeclAttrs,
966967
llvm::function_ref<void(Decl *)> Handler,
967-
bool fromASTGen = false);
968+
bool fromASTGen = false,
969+
bool stubOnly = false);
968970

969971
std::pair<std::vector<Decl *>, std::optional<Fingerprint>>
970972
parseDeclListDelayed(IterableDeclContext *IDC);
971973

972974
bool parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc,
973975
Diag<> LBraceDiag, Diag<> RBraceDiag,
974-
IterableDeclContext *IDC);
976+
IterableDeclContext *IDC,
977+
ParseDeclOptions Flags);
975978

976979
bool canDelayMemberDeclParsing(bool &HasOperatorDeclarations,
977-
bool &HasNestedClassDeclarations);
980+
bool &HasNestedClassDeclarations,
981+
ParseDeclOptions Flags);
978982

979-
bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations);
983+
bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations,
984+
ParseDeclOptions Flags);
980985

981986
bool delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc,
982987
IterableDeclContext *IDC);
@@ -1342,7 +1347,8 @@ class Parser {
13421347
bool HasFuncKeyword = true);
13431348
BodyAndFingerprint
13441349
parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD);
1345-
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
1350+
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD,
1351+
ParseDeclOptions Flags);
13461352
BodyAndFingerprint
13471353
parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
13481354

lib/AST/Attr.cpp

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

1044+
// Skip inverse ABIAttrs--these are an implementation detail.
1045+
if (auto abiAttr = dyn_cast<ABIAttr>(DA))
1046+
if (abiAttr->isInverse())
1047+
continue;
1048+
10441049
// In the public interfaces of -library-level=api modules, skip attributes
10451050
// that reference SPI platforms.
10461051
if (Options.printPublicInterface() && libraryLevelAPI &&
@@ -1258,6 +1263,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
12581263
}
12591264
break;
12601265
}
1266+
1267+
case DeclAttrKind::ABI:
1268+
// Inverse attributes should never be printed.
1269+
if (cast<ABIAttr>(this)->isInverse())
1270+
return false;
1271+
break;
1272+
12611273
default:
12621274
break;
12631275
}
@@ -1826,6 +1838,17 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
18261838
break;
18271839
}
18281840

1841+
case DeclAttrKind::ABI: {
1842+
auto *attr = cast<ABIAttr>(this);
1843+
Printer << "@abi(";
1844+
ASSERT(!attr->isInverse() && "should be skipped above");
1845+
if (attr->abiDecl)
1846+
attr->abiDecl->print(Printer, Options);
1847+
Printer << ")";
1848+
1849+
break;
1850+
}
1851+
18291852
#define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS:
18301853
#include "swift/AST/DeclAttr.def"
18311854
llvm_unreachable("handled above");
@@ -1871,6 +1894,8 @@ StringRef DeclAttribute::getAttrName() const {
18711894
case DeclAttrKind::CLASS: \
18721895
return #NAME;
18731896
#include "swift/AST/DeclAttr.def"
1897+
case DeclAttrKind::ABI:
1898+
return "abi";
18741899
case DeclAttrKind::SILGenName:
18751900
return "_silgen_name";
18761901
case DeclAttrKind::Alignment:
@@ -3071,6 +3096,38 @@ LifetimeAttr *LifetimeAttr::create(ASTContext &context, SourceLoc atLoc,
30713096
return new (mem) LifetimeAttr(atLoc, baseRange, implicit, entries);
30723097
}
30733098

3099+
void ABIAttr::connectToInverse(Decl *owner) const {
3100+
if (isInverse())
3101+
return;
3102+
3103+
// If necessary, convert abiDecl's PBD to a specific VarDecl.
3104+
auto correspondingDecl = abiDecl;
3105+
if (auto PBD = dyn_cast<PatternBindingDecl>(correspondingDecl)) {
3106+
if (!isa<VarDecl>(owner))
3107+
// Invalid code (e.g. `@abi(var x) func x()`). Won't be able to connect.
3108+
return;
3109+
3110+
correspondingDecl =
3111+
PBD->getVarAtSimilarStructuralPosition(cast<VarDecl>(owner));
3112+
}
3113+
3114+
if (!correspondingDecl)
3115+
return;
3116+
3117+
auto inverseAttr = correspondingDecl->getAttrs().getAttribute<ABIAttr>();
3118+
ASSERT(inverseAttr && inverseAttr->isInverse());
3119+
3120+
// Reverse of the above: If we're trying to connect to a VarDecl, connect to
3121+
// its PBD instead.
3122+
auto inverseCorrespondingDecl = owner;
3123+
if (auto VD = dyn_cast<VarDecl>(inverseCorrespondingDecl))
3124+
if (auto PBD = VD->getParentPatternBinding())
3125+
inverseCorrespondingDecl = PBD;
3126+
3127+
inverseAttr->abiDecl = inverseCorrespondingDecl;
3128+
}
3129+
3130+
30743131
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
30753132
if (attr)
30763133
attr->print(out);

lib/AST/Decl.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4168,6 +4168,43 @@ bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) {
41684168
return false;
41694169
}
41704170

4171+
VarDecl *PatternBindingDecl::
4172+
getVarAtSimilarStructuralPosition(VarDecl *otherVar) {
4173+
auto otherPBD = otherVar->getParentPatternBinding();
4174+
4175+
if (!otherPBD)
4176+
return getSingleVar();
4177+
if (otherPBD == this)
4178+
return otherVar;
4179+
4180+
// Find the entry index and sibling index for `otherVar` within its PBD.
4181+
auto entryIndex = otherPBD->getPatternEntryIndexForVarDecl(otherVar);
4182+
4183+
SmallVector<VarDecl *, 16> otherSiblings;
4184+
otherPBD->getPattern(entryIndex)->collectVariables(otherSiblings);
4185+
size_t siblingIndex =
4186+
llvm::find(otherSiblings, otherVar) - otherSiblings.begin();
4187+
4188+
ASSERT(siblingIndex != otherSiblings.size()
4189+
&& "otherVar not in its own pattern?");
4190+
4191+
// Look up the same entry index and sibling index in this PBD, returning null
4192+
// if that index doesn't exist.
4193+
4194+
// Corresponding PBD has more patterns in it
4195+
if (entryIndex >= getNumPatternEntries())
4196+
return nullptr;
4197+
4198+
SmallVector<VarDecl *, 16> siblings;
4199+
getPattern(entryIndex)->collectVariables(siblings);
4200+
4201+
// Corresponding pattern has more vars in it
4202+
if (siblingIndex >= siblings.size())
4203+
return nullptr;
4204+
4205+
return siblings[siblingIndex];
4206+
}
4207+
41714208
SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const {
41724209
// Some decls have a parent/child split where the introducer keyword is on the
41734210
// 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
@@ -292,6 +292,11 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) {
292292
return decl->getAttrs().hasAttribute<UnsafeAttr>();
293293
}
294294

295+
static bool usesFeatureABIAttribute(Decl *decl) {
296+
auto abiAttr = decl->getAttrs().getAttribute<ABIAttr>();
297+
return abiAttr && !abiAttr->isInverse();
298+
}
299+
295300
UNINTERESTING_FEATURE(WarnUnsafe)
296301
UNINTERESTING_FEATURE(SafeInterop)
297302

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ extension ASTGenVisitor {
101101
let attrName = identTy.name.rawText
102102
let attrKind = BridgedDeclAttrKind(from: attrName.bridged)
103103
switch attrKind {
104+
case .ABI:
105+
return self.generateABIAttr(attribute: node)?.asDeclAttribute
104106
case .alignment:
105107
return self.generateAlignmentAttr(attribute: node)?.asDeclAttribute
106108
case .available:
@@ -322,6 +324,11 @@ extension ASTGenVisitor {
322324
return self.generateCustomAttr(attribute: node)?.asDeclAttribute
323325
}
324326

327+
func generateABIAttr(attribute node: AttributeSyntax) -> BridgedABIAttr? {
328+
#warning("TODO: implement generateABIAttr")
329+
fatalError("TODO: implement generateABIAttr")
330+
}
331+
325332
func generateAlignmentAttr(attribute node: AttributeSyntax) -> BridgedAlignmentAttr? {
326333
guard
327334
let arg = node.arguments?.as(TokenSyntax.self)

0 commit comments

Comments
 (0)