Skip to content

Commit e0e9a6e

Browse files
committed
Type check ABI decls
Sema now type-checks the alternate ABI-providing decls inside of @abi attributes. Making this work—particularly, making redeclaration checking work—required making name lookup aware of ABI decls. Name lookup now evaluates both API-providing and ABI-providing declarations. In most cases, it will filter ABI-only decls out unless a specific flag is passed, in which case it will filter API-only decls out instead. Calls that simply retrieve a list of declarations, like `IterableDeclContext::getMembers()` and friends, typically only return API-providing decls; you have to access the ABI-providing ones through those. As part of that work, I have also added some basic compiler interfaces for working with the API-providing and ABI-providing variants. `ABIRole` encodes whether a declaration provides only API, only ABI, or both, and `ABIRoleInfo` combines that with a pointer to the counterpart providing the other role (for a declaration that provides both, that’ll just be a pointer to `this`). Decl checking of behavior specific to @abi will come in a future commit. Note that this probably doesn’t properly exercise some of the new code (ASTScope::lookupEnclosingABIAttributeScope(), for instance); I expect that to happen only once we can rename types using an @abi attribute, since that will create distinguishable behavior differences when resolving TypeReprs in other @abi attributes.
1 parent f37cdb2 commit e0e9a6e

28 files changed

+629
-140
lines changed

include/swift/AST/ASTScope.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class ASTScopeImpl : public ASTAllocated<ASTScopeImpl> {
142142
friend class IterableTypeBodyPortion;
143143
friend class ScopeCreator;
144144
friend class ASTSourceFileScope;
145+
friend class ABIAttributeScope;
145146
friend class Lowering::SILGenFunction;
146147

147148
#pragma mark - tree state
@@ -305,6 +306,9 @@ class ASTScopeImpl : public ASTAllocated<ASTScopeImpl> {
305306
SourceFile *sourceFile, SourceLoc loc,
306307
llvm::function_ref<bool(ASTScope::PotentialMacro)> consume);
307308

309+
static ABIAttr *lookupEnclosingABIAttributeScope(
310+
SourceFile *sourceFile, SourceLoc loc);
311+
308312
static CatchNode lookupCatchNode(ModuleDecl *module, SourceLoc loc);
309313

310314
/// Scopes that cannot bind variables may set this to true to create more
@@ -948,6 +952,40 @@ class CustomAttributeScope final : public ASTScopeImpl {
948952
}
949953
};
950954

955+
/// The scope for ABI attributes and their arguments.
956+
///
957+
/// Source locations for the attribute name and its arguments are in the
958+
/// custom attribute, so lookup is invoked from within the attribute
959+
/// itself.
960+
class ABIAttributeScope final : public ASTScopeImpl {
961+
public:
962+
ABIAttr *attr;
963+
Decl *decl;
964+
965+
ABIAttributeScope(ABIAttr *attr, Decl *decl)
966+
: ASTScopeImpl(ScopeKind::ABIAttribute), attr(attr), decl(decl) {}
967+
virtual ~ABIAttributeScope() {}
968+
969+
protected:
970+
ASTScopeImpl *expandSpecifically(ScopeCreator &) override;
971+
972+
public:
973+
SourceRange
974+
getSourceRangeOfThisASTNode(bool omitAssertions = false) const override;
975+
NullablePtr<const void> addressForPrinting() const override { return decl; }
976+
977+
bool ignoreInDebugInfo() const override { return true; }
978+
979+
private:
980+
AnnotatedInsertionPoint
981+
expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &);
982+
983+
public:
984+
static bool classof(const ASTScopeImpl *scope) {
985+
return scope->getKind() == ScopeKind::ABIAttribute;
986+
}
987+
};
988+
951989
/// PatternBindingDecl's (PBDs) are tricky (See the comment for \c
952990
/// PatternBindingDecl):
953991
///

include/swift/AST/ASTScopeNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ SCOPE_NODE(CaseLabelItem)
8989
SCOPE_NODE(CaseStmtBody)
9090
STMT_SCOPE_NODE(BraceStmt)
9191
EXPR_SCOPE_NODE(Try)
92+
DECL_ATTRIBUTE_SCOPE_NODE(ABIAttribute)
9293

9394
#undef DECL_ATTRIBUTE_SCOPE_NODE
9495
#undef EXPR_SCOPE_NODE

include/swift/AST/Decl.h

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ namespace swift {
9595
class MacroDefinition;
9696
class ModuleDecl;
9797
class NamedPattern;
98+
enum NLOptions : unsigned;
9899
class EnumCaseDecl;
99100
class EnumElementDecl;
100101
class ParameterList;
@@ -4339,6 +4340,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
43394340
IncludeAttrImplements = 1 << 0,
43404341
/// Whether to exclude members of macro expansions.
43414342
ExcludeMacroExpansions = 1 << 1,
4343+
/// If @abi attributes are present, return the decls representing the ABI,
4344+
/// not the API.
4345+
ABIProviding = 1 << 2,
43424346
};
43434347

43444348
/// Find all of the declarations with the given name within this nominal type
@@ -9773,6 +9777,126 @@ const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index);
97739777
/// nullptr if the source does not have a parameter list.
97749778
const ParamDecl *getParameterAt(const DeclContext *source, unsigned index);
97759779

9780+
class ABIRole {
9781+
public:
9782+
enum Value : uint8_t {
9783+
Neither = 0,
9784+
ProvidesABI = 1 << 0,
9785+
ProvidesAPI = 1 << 1,
9786+
Either = ProvidesABI | ProvidesAPI,
9787+
};
9788+
9789+
Value value;
9790+
9791+
ABIRole(Value value)
9792+
: value(value)
9793+
{ }
9794+
9795+
ABIRole()
9796+
: ABIRole(Neither)
9797+
{ }
9798+
9799+
explicit ABIRole(NLOptions opts);
9800+
9801+
template<typename FlagType>
9802+
explicit ABIRole(OptionSet<FlagType> flags)
9803+
: value(flags.contains(FlagType::ABIProviding) ? ProvidesABI : ProvidesAPI)
9804+
{ }
9805+
9806+
inline ABIRole operator|(ABIRole rhs) const {
9807+
return ABIRole(ABIRole::Value(value | rhs.value));
9808+
}
9809+
inline ABIRole &operator|=(ABIRole rhs) {
9810+
value = ABIRole::Value(value | rhs.value);
9811+
return *this;
9812+
}
9813+
inline ABIRole operator&(ABIRole rhs) const {
9814+
return ABIRole(ABIRole::Value(value & rhs.value));
9815+
}
9816+
inline ABIRole &operator&=(ABIRole rhs) {
9817+
value = ABIRole::Value(value & rhs.value);
9818+
return *this;
9819+
}
9820+
inline ABIRole operator~() const {
9821+
return ABIRole(ABIRole::Value(~value));
9822+
}
9823+
9824+
operator bool() const {
9825+
return value != Neither;
9826+
}
9827+
};
9828+
9829+
namespace abi_role_detail {
9830+
9831+
using Storage = llvm::PointerIntPair<Decl *, 2, ABIRole::Value>;
9832+
Storage computeStorage(Decl *decl);
9833+
9834+
}
9835+
9836+
/// Specifies the \c ABIAttr -related behavior of this declaration
9837+
/// and provides access to its counterpart.
9838+
///
9839+
/// A given declaration may provide the API, the ABI, or both. If it provides
9840+
/// API, the counterpart is the matching ABI-providing decl; if it provides
9841+
/// ABI, the countepart is the matching API-providing decl. A declaration
9842+
/// which provides both API and ABI is its own counterpart.
9843+
///
9844+
/// If the counterpart is \c nullptr , this indicates a fundamental mismatch
9845+
/// between decl and counterpart. Sometimes this mismatch is a difference in
9846+
/// decl kind; in these cases, \c getCounterpartUnchecked() will return the
9847+
/// incorrect counterpart.
9848+
template<typename SpecificDecl>
9849+
class ABIRoleInfo {
9850+
friend abi_role_detail::Storage abi_role_detail::computeStorage(Decl *);
9851+
9852+
abi_role_detail::Storage counterpartAndFlags;
9853+
9854+
ABIRoleInfo(abi_role_detail::Storage storage)
9855+
: counterpartAndFlags(storage)
9856+
{ }
9857+
9858+
public:
9859+
explicit ABIRoleInfo(const SpecificDecl *decl)
9860+
: ABIRoleInfo(abi_role_detail::computeStorage(const_cast<SpecificDecl *>(decl)))
9861+
{ }
9862+
9863+
Decl *getCounterpartUnchecked() const {
9864+
return counterpartAndFlags.getPointer();
9865+
}
9866+
9867+
SpecificDecl *getCounterpart() const {
9868+
return dyn_cast_or_null<SpecificDecl>(getCounterpartUnchecked());
9869+
}
9870+
9871+
ABIRole getRole() const {
9872+
return ABIRole(ABIRole::Value(counterpartAndFlags.getInt()));
9873+
}
9874+
9875+
bool matches(ABIRole desiredRole) const {
9876+
return getRole() & desiredRole;
9877+
}
9878+
9879+
template<typename Options>
9880+
bool matchesOptions(Options opts) const {
9881+
return matches(ABIRole(opts));
9882+
}
9883+
9884+
bool providesABI() const {
9885+
return matches(ABIRole::ProvidesABI);
9886+
}
9887+
9888+
bool providesAPI() const {
9889+
return matches(ABIRole::ProvidesAPI);
9890+
}
9891+
9892+
bool hasABIAttr() const {
9893+
return !providesABI();
9894+
}
9895+
};
9896+
9897+
template<typename SpecificDecl>
9898+
ABIRoleInfo(const SpecificDecl *decl) -> ABIRoleInfo<SpecificDecl>;
9899+
97769900
StringRef
97779901
getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article,
97789902
std::optional<bool> underscored = std::nullopt);

include/swift/AST/LookupKinds.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ enum NLOptions : unsigned {
6464
/// from a module that has not been imported.
6565
NL_IgnoreMissingImports = 1 << 9,
6666

67+
/// If @abi attributes are present, return the decls representing the ABI,
68+
/// not the API.
69+
NL_ABIProviding = 1 << 10,
70+
6771
/// The default set of options used for qualified name lookup.
6872
///
6973
/// FIXME: Eventually, add NL_ProtocolMembers to this, once all of the
@@ -97,6 +101,9 @@ void simple_display(llvm::raw_ostream &out, NLOptions options);
97101
enum class ModuleLookupFlags : unsigned {
98102
/// Exclude names introduced by macro expansions in the top-level module.
99103
ExcludeMacroExpansions = 1 << 0,
104+
/// If @abi attributes are present, return the decls representing the ABI,
105+
/// not the API.
106+
ABIProviding = 1 << 1,
100107
};
101108

102109
} // end namespace swift

include/swift/AST/NameLookup.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ enum class UnqualifiedLookupFlags {
253253
/// This lookup should include members that would otherwise be filtered out
254254
/// because they come from a module that has not been imported.
255255
IgnoreMissingImports = 1 << 10,
256+
/// If @abi attributes are present, return the decls representing the ABI,
257+
/// not the API.
258+
ABIProviding = 1 << 11,
256259

257260
// Reminder: If you add a flag, make sure you update simple_display() below
258261
};
@@ -564,6 +567,9 @@ template <typename Result>
564567
void filterForDiscriminator(SmallVectorImpl<Result> &results,
565568
DebuggerClient *debugClient);
566569

570+
/// \returns Whether the given source location is inside an \@abi attribute.
571+
bool isInABIAttr(SourceFile *sourceFile, SourceLoc loc);
572+
567573
/// \returns The set of macro declarations with the given name that
568574
/// fulfill any of the given macro roles.
569575
SmallVector<MacroDecl *, 1> lookupMacros(DeclContext *dc,
@@ -758,8 +764,16 @@ class ASTScope : public ASTAllocated<ASTScope> {
758764
/// local declarations inside the innermost syntactic scope only.
759765
static void lookupLocalDecls(SourceFile *, DeclName, SourceLoc,
760766
bool stopAfterInnermostBraceStmt,
767+
ABIRole roleFilter,
761768
SmallVectorImpl<ValueDecl *> &);
762769

770+
static void lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc,
771+
bool stopAfterInnermostBraceStmt,
772+
SmallVectorImpl<ValueDecl *> &results) {
773+
lookupLocalDecls(sf, name, loc, stopAfterInnermostBraceStmt,
774+
ABIRole::ProvidesAPI, results);
775+
}
776+
763777
/// Returns the result if there is exactly one, nullptr otherwise.
764778
static ValueDecl *lookupSingleLocalDecl(SourceFile *, DeclName, SourceLoc);
765779

@@ -804,6 +818,18 @@ class ASTScope : public ASTAllocated<ASTScope> {
804818
SourceFile *sourceFile, SourceLoc loc,
805819
llvm::function_ref<bool(PotentialMacro macro)> consume);
806820

821+
/// Look up the scope tree for the nearest enclosing ABI attribute at
822+
/// the given source location.
823+
///
824+
/// \param sourceFile The source file containing the given location.
825+
/// \param loc The source location to start lookup from.
826+
/// \param consume A function that is called when an ABI attribute
827+
/// scope is found. If \c consume returns \c true, lookup
828+
/// will stop. If \c consume returns \c false, lookup will
829+
/// continue up the scope tree.
830+
static ABIAttr *lookupEnclosingABIAttributeScope(
831+
SourceFile *sourceFile, SourceLoc loc);
832+
807833
/// Look up the scope tree for the nearest point at which an error thrown from
808834
/// this location can be caught or rethrown.
809835
///

lib/AST/ASTDumper.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,7 @@ namespace {
991991
TerminalColor Color = DeclColor) {
992992
printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label,
993993
Color);
994+
printFlag(!ABIRoleInfo(declRef.getDecl()).providesAPI(), "abi_only_decl");
994995
}
995996

996997
void printThrowDest(ThrownErrorDestination throws, bool wantNothrow) {
@@ -1186,6 +1187,8 @@ namespace {
11861187
printFieldQuoted(implAttr->CategoryName.str(), label);
11871188
}
11881189

1190+
printFlag(!ABIRoleInfo(D).providesAPI(), "abi_only");
1191+
11891192
printSourceRange(D->getSourceRange(), &D->getASTContext());
11901193
printFlag(D->TrailingSemiLoc.isValid(), "trailing_semi",
11911194
DeclModifierColor);

lib/AST/ASTScope.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ void ASTScope::lookupEnclosingMacroScope(
6767
return ASTScopeImpl::lookupEnclosingMacroScope(sourceFile, loc, body);
6868
}
6969

70+
ABIAttr *ASTScope::lookupEnclosingABIAttributeScope(
71+
SourceFile *sourceFile, SourceLoc loc) {
72+
return ASTScopeImpl::lookupEnclosingABIAttributeScope(sourceFile, loc);
73+
}
74+
7075
CatchNode ASTScope::lookupCatchNode(ModuleDecl *module, SourceLoc loc) {
7176
return ASTScopeImpl::lookupCatchNode(module, loc);
7277
}

0 commit comments

Comments
 (0)