Skip to content

Commit 596485e

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 786b247 commit 596485e

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

@@ -3298,6 +3302,39 @@ class IsolatedTypeAttr : public SimpleTypeAttrWithArgs<TypeAttrKind::Isolated> {
32983302
void printImpl(ASTPrinter &printer, const PrintOptions &options) const;
32993303
};
33003304

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

include/swift/AST/Decl.h

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

2612+
/// Returns the \c VarDecl in this PBD at the same offset in the same
2613+
/// pattern entry as \p otherVar is in its PBD, or \c nullptr if this PBD is
2614+
/// too different from \p otherVar 's to find an equivalent variable.
2615+
VarDecl *getVarAtSimilarStructuralPosition(VarDecl *otherVar);
2616+
26122617
static bool classof(const Decl *D) {
26132618
return D->getKind() == DeclKind::PatternBinding;
26142619
}

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
@@ -436,6 +436,9 @@ EXPERIMENTAL_FEATURE(CoroutineAccessorsAllocateInCallee, false)
436436
// When a parameter has unspecified isolation, infer it as main actor isolated.
437437
EXPERIMENTAL_FEATURE(GenerateForceToMainActorThunks, false)
438438

439+
/// Allow the @abi attribute.
440+
EXPERIMENTAL_FEATURE(ABIAttribute, true)
441+
439442
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
440443
#undef EXPERIMENTAL_FEATURE
441444
#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);
@@ -1340,7 +1345,8 @@ class Parser {
13401345
bool HasFuncKeyword = true);
13411346
BodyAndFingerprint
13421347
parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD);
1343-
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
1348+
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD,
1349+
ParseDeclOptions Flags);
13441350
BodyAndFingerprint
13451351
parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
13461352

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
}
@@ -1818,6 +1830,17 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
18181830
break;
18191831
}
18201832

1833+
case DeclAttrKind::ABI: {
1834+
auto *attr = cast<ABIAttr>(this);
1835+
Printer << "@abi(";
1836+
ASSERT(!attr->isInverse() && "should be skipped above");
1837+
if (attr->abiDecl)
1838+
attr->abiDecl->print(Printer, Options);
1839+
Printer << ")";
1840+
1841+
break;
1842+
}
1843+
18211844
#define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS:
18221845
#include "swift/AST/DeclAttr.def"
18231846
llvm_unreachable("handled above");
@@ -1863,6 +1886,8 @@ StringRef DeclAttribute::getAttrName() const {
18631886
case DeclAttrKind::CLASS: \
18641887
return #NAME;
18651888
#include "swift/AST/DeclAttr.def"
1889+
case DeclAttrKind::ABI:
1890+
return "abi";
18661891
case DeclAttrKind::SILGenName:
18671892
return "_silgen_name";
18681893
case DeclAttrKind::Alignment:
@@ -3053,6 +3078,38 @@ LifetimeAttr *LifetimeAttr::create(ASTContext &context, SourceLoc atLoc,
30533078
return new (context) LifetimeAttr(atLoc, baseRange, implicit, entry);
30543079
}
30553080

3081+
void ABIAttr::connectToInverse(Decl *owner) const {
3082+
if (isInverse())
3083+
return;
3084+
3085+
// If necessary, convert abiDecl's PBD to a specific VarDecl.
3086+
auto correspondingDecl = abiDecl;
3087+
if (auto PBD = dyn_cast<PatternBindingDecl>(correspondingDecl)) {
3088+
if (!isa<VarDecl>(owner))
3089+
// Invalid code (e.g. `@abi(var x) func x()`). Won't be able to connect.
3090+
return;
3091+
3092+
correspondingDecl =
3093+
PBD->getVarAtSimilarStructuralPosition(cast<VarDecl>(owner));
3094+
}
3095+
3096+
if (!correspondingDecl)
3097+
return;
3098+
3099+
auto inverseAttr = correspondingDecl->getAttrs().getAttribute<ABIAttr>();
3100+
ASSERT(inverseAttr && inverseAttr->isInverse());
3101+
3102+
// Reverse of the above: If we're trying to connect to a VarDecl, connect to
3103+
// its PBD instead.
3104+
auto inverseCorrespondingDecl = owner;
3105+
if (auto VD = dyn_cast<VarDecl>(inverseCorrespondingDecl))
3106+
if (auto PBD = VD->getParentPatternBinding())
3107+
inverseCorrespondingDecl = PBD;
3108+
3109+
inverseAttr->abiDecl = inverseCorrespondingDecl;
3110+
}
3111+
3112+
30563113
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
30573114
if (attr)
30583115
attr->print(out);

lib/AST/Decl.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4191,6 +4191,43 @@ bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) {
41914191
return false;
41924192
}
41934193

4194+
VarDecl *PatternBindingDecl::
4195+
getVarAtSimilarStructuralPosition(VarDecl *otherVar) {
4196+
auto otherPBD = otherVar->getParentPatternBinding();
4197+
4198+
if (!otherPBD)
4199+
return getSingleVar();
4200+
if (otherPBD == this)
4201+
return otherVar;
4202+
4203+
// Find the entry index and sibling index for `otherVar` within its PBD.
4204+
auto entryIndex = otherPBD->getPatternEntryIndexForVarDecl(otherVar);
4205+
4206+
SmallVector<VarDecl *, 16> otherSiblings;
4207+
otherPBD->getPattern(entryIndex)->collectVariables(otherSiblings);
4208+
size_t siblingIndex =
4209+
llvm::find(otherSiblings, otherVar) - otherSiblings.begin();
4210+
4211+
ASSERT(siblingIndex != otherSiblings.size()
4212+
&& "otherVar not in its own pattern?");
4213+
4214+
// Look up the same entry index and sibling index in this PBD, returning null
4215+
// if that index doesn't exist.
4216+
4217+
// Corresponding PBD has more patterns in it
4218+
if (entryIndex >= getNumPatternEntries())
4219+
return nullptr;
4220+
4221+
SmallVector<VarDecl *, 16> siblings;
4222+
getPattern(entryIndex)->collectVariables(siblings);
4223+
4224+
// Corresponding pattern has more vars in it
4225+
if (siblingIndex >= siblings.size())
4226+
return nullptr;
4227+
4228+
return siblings[siblingIndex];
4229+
}
4230+
41944231
SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const {
41954232
// Some decls have a parent/child split where the introducer keyword is on the
41964233
// 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
@@ -294,6 +294,11 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) {
294294
return decl->getAttrs().hasAttribute<UnsafeAttr>();
295295
}
296296

297+
static bool usesFeatureABIAttribute(Decl *decl) {
298+
auto abiAttr = decl->getAttrs().getAttribute<ABIAttr>();
299+
return abiAttr && !abiAttr->isInverse();
300+
}
301+
297302
UNINTERESTING_FEATURE(WarnUnsafe)
298303
UNINTERESTING_FEATURE(SafeInterop)
299304
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)

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)