Skip to content

Commit bab2fb6

Browse files
authored
Merge pull request swiftlang#36458 from DougGregor/custom-type-attributes
Custom type attributes
2 parents f4f3d52 + 09d7f47 commit bab2fb6

File tree

8 files changed

+369
-235
lines changed

8 files changed

+369
-235
lines changed

include/swift/AST/Attr.h

Lines changed: 167 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "swift/AST/Requirement.h"
3838
#include "swift/AST/StorageImpl.h"
3939
#include "swift/AST/TrailingCallArguments.h"
40+
#include "llvm/ADT/iterator_range.h"
4041
#include "llvm/ADT/SmallVector.h"
4142
#include "llvm/ADT/StringRef.h"
4243
#include "llvm/Support/ErrorHandling.h"
@@ -47,6 +48,7 @@ namespace swift {
4748
class ASTPrinter;
4849
class ASTContext;
4950
struct PrintOptions;
51+
class CustomAttr;
5052
class Decl;
5153
class AbstractFunctionDecl;
5254
class FuncDecl;
@@ -58,133 +60,6 @@ class PatternBindingInitializer;
5860
class TrailingWhereClause;
5961
class TypeExpr;
6062

61-
/// TypeAttributes - These are attributes that may be applied to types.
62-
class TypeAttributes {
63-
// Get a SourceLoc for every possible attribute that can be parsed in source.
64-
// the presence of the attribute is indicated by its location being set.
65-
SourceLoc AttrLocs[TAK_Count];
66-
public:
67-
/// AtLoc - This is the location of the first '@' in the attribute specifier.
68-
/// If this is an empty attribute specifier, then this will be an invalid loc.
69-
SourceLoc AtLoc;
70-
71-
struct Convention {
72-
StringRef Name = {};
73-
DeclNameRef WitnessMethodProtocol = {};
74-
Located<StringRef> ClangType = Located<StringRef>(StringRef(), {});
75-
/// Convenience factory function to create a Swift convention.
76-
///
77-
/// Don't use this function if you are creating a C convention as you
78-
/// probably need a ClangType field as well.
79-
static Convention makeSwiftConvention(StringRef name) {
80-
return {name, DeclNameRef(), Located<StringRef>("", {})};
81-
}
82-
};
83-
84-
Optional<Convention> ConventionArguments;
85-
86-
// Indicates whether the type's '@differentiable' attribute has a 'linear'
87-
// argument.
88-
DifferentiabilityKind differentiabilityKind =
89-
DifferentiabilityKind::NonDifferentiable;
90-
91-
// For an opened existential type, the known ID.
92-
Optional<UUID> OpenedID;
93-
94-
// For a reference to an opaque return type, the mangled name and argument
95-
// index into the generic signature.
96-
struct OpaqueReturnTypeRef {
97-
StringRef mangledName;
98-
unsigned index;
99-
};
100-
Optional<OpaqueReturnTypeRef> OpaqueReturnTypeOf;
101-
102-
TypeAttributes() {}
103-
104-
bool isValid() const { return AtLoc.isValid(); }
105-
106-
void clearAttribute(TypeAttrKind A) {
107-
AttrLocs[A] = SourceLoc();
108-
}
109-
110-
bool has(TypeAttrKind A) const {
111-
return getLoc(A).isValid();
112-
}
113-
114-
SourceLoc getLoc(TypeAttrKind A) const {
115-
return AttrLocs[A];
116-
}
117-
118-
void setOpaqueReturnTypeOf(StringRef mangling, unsigned index) {
119-
OpaqueReturnTypeOf = OpaqueReturnTypeRef{mangling, index};
120-
}
121-
122-
void setAttr(TypeAttrKind A, SourceLoc L) {
123-
assert(!L.isInvalid() && "Cannot clear attribute with this method");
124-
AttrLocs[A] = L;
125-
}
126-
127-
void getAttrLocs(SmallVectorImpl<SourceLoc> &Locs) const {
128-
for (auto Loc : AttrLocs) {
129-
if (Loc.isValid())
130-
Locs.push_back(Loc);
131-
}
132-
}
133-
134-
// This attribute list is empty if no attributes are specified. Note that
135-
// the presence of the leading @ is not enough to tell, because we want
136-
// clients to be able to remove attributes they process until they get to
137-
// an empty list.
138-
bool empty() const {
139-
for (SourceLoc elt : AttrLocs)
140-
if (elt.isValid())
141-
return false;
142-
143-
return true;
144-
}
145-
146-
bool hasConvention() const { return ConventionArguments.hasValue(); }
147-
148-
/// Returns the primary calling convention string.
149-
///
150-
/// Note: For C conventions, this may not represent the full convention.
151-
StringRef getConventionName() const {
152-
return ConventionArguments.getValue().Name;
153-
}
154-
155-
/// Show the string enclosed between @convention(..)'s parentheses.
156-
///
157-
/// For example, @convention(foo, bar) will give the string "foo, bar".
158-
void getConventionArguments(SmallVectorImpl<char> &buffer) const;
159-
160-
bool hasOwnership() const {
161-
return getOwnership() != ReferenceOwnership::Strong;
162-
}
163-
ReferenceOwnership getOwnership() const {
164-
#define REF_STORAGE(Name, name, ...) \
165-
if (has(TAK_sil_##name)) return ReferenceOwnership::Name;
166-
#include "swift/AST/ReferenceStorage.def"
167-
return ReferenceOwnership::Strong;
168-
}
169-
170-
void clearOwnership() {
171-
#define REF_STORAGE(Name, name, ...) \
172-
clearAttribute(TAK_sil_##name);
173-
#include "swift/AST/ReferenceStorage.def"
174-
}
175-
176-
bool hasOpenedID() const { return OpenedID.hasValue(); }
177-
UUID getOpenedID() const { return *OpenedID; }
178-
179-
/// Given a name like "autoclosure", return the type attribute ID that
180-
/// corresponds to it. This returns TAK_Count on failure.
181-
///
182-
static TypeAttrKind getAttrKindFromString(StringRef Str);
183-
184-
/// Return the name (like "autoclosure") for an attribute ID.
185-
static const char *getAttrName(TypeAttrKind kind);
186-
};
187-
18863
class alignas(1 << AttrAlignInBits) AttributeBase {
18964
public:
19065
/// The location of the '@'.
@@ -229,6 +104,7 @@ enum class DeclKind : uint8_t;
229104
/// Represents one declaration attribute.
230105
class DeclAttribute : public AttributeBase {
231106
friend class DeclAttributes;
107+
friend class TypeAttributes;
232108

233109
protected:
234110
union {
@@ -2409,6 +2285,170 @@ class DeclAttributes {
24092285
SourceLoc getStartLoc(bool forModifiers = false) const;
24102286
};
24112287

2288+
/// TypeAttributes - These are attributes that may be applied to types.
2289+
class TypeAttributes {
2290+
// Get a SourceLoc for every possible attribute that can be parsed in source.
2291+
// the presence of the attribute is indicated by its location being set.
2292+
SourceLoc AttrLocs[TAK_Count];
2293+
2294+
/// The custom attributes, in a linked list.
2295+
CustomAttr *CustomAttrs = nullptr;
2296+
2297+
public:
2298+
/// AtLoc - This is the location of the first '@' in the attribute specifier.
2299+
/// If this is an empty attribute specifier, then this will be an invalid loc.
2300+
SourceLoc AtLoc;
2301+
2302+
struct Convention {
2303+
StringRef Name = {};
2304+
DeclNameRef WitnessMethodProtocol = {};
2305+
Located<StringRef> ClangType = Located<StringRef>(StringRef(), {});
2306+
/// Convenience factory function to create a Swift convention.
2307+
///
2308+
/// Don't use this function if you are creating a C convention as you
2309+
/// probably need a ClangType field as well.
2310+
static Convention makeSwiftConvention(StringRef name) {
2311+
return {name, DeclNameRef(), Located<StringRef>("", {})};
2312+
}
2313+
};
2314+
2315+
Optional<Convention> ConventionArguments;
2316+
2317+
// Indicates whether the type's '@differentiable' attribute has a 'linear'
2318+
// argument.
2319+
DifferentiabilityKind differentiabilityKind =
2320+
DifferentiabilityKind::NonDifferentiable;
2321+
2322+
// For an opened existential type, the known ID.
2323+
Optional<UUID> OpenedID;
2324+
2325+
// For a reference to an opaque return type, the mangled name and argument
2326+
// index into the generic signature.
2327+
struct OpaqueReturnTypeRef {
2328+
StringRef mangledName;
2329+
unsigned index;
2330+
};
2331+
Optional<OpaqueReturnTypeRef> OpaqueReturnTypeOf;
2332+
2333+
TypeAttributes() {}
2334+
2335+
bool isValid() const { return AtLoc.isValid(); }
2336+
2337+
void clearAttribute(TypeAttrKind A) {
2338+
AttrLocs[A] = SourceLoc();
2339+
}
2340+
2341+
bool has(TypeAttrKind A) const {
2342+
return getLoc(A).isValid();
2343+
}
2344+
2345+
SourceLoc getLoc(TypeAttrKind A) const {
2346+
return AttrLocs[A];
2347+
}
2348+
2349+
void setOpaqueReturnTypeOf(StringRef mangling, unsigned index) {
2350+
OpaqueReturnTypeOf = OpaqueReturnTypeRef{mangling, index};
2351+
}
2352+
2353+
void setAttr(TypeAttrKind A, SourceLoc L) {
2354+
assert(!L.isInvalid() && "Cannot clear attribute with this method");
2355+
AttrLocs[A] = L;
2356+
}
2357+
2358+
void getAttrLocs(SmallVectorImpl<SourceLoc> &Locs) const {
2359+
for (auto Loc : AttrLocs) {
2360+
if (Loc.isValid())
2361+
Locs.push_back(Loc);
2362+
}
2363+
}
2364+
2365+
// This attribute list is empty if no attributes are specified. Note that
2366+
// the presence of the leading @ is not enough to tell, because we want
2367+
// clients to be able to remove attributes they process until they get to
2368+
// an empty list.
2369+
bool empty() const {
2370+
if (CustomAttrs)
2371+
return false;
2372+
2373+
for (SourceLoc elt : AttrLocs)
2374+
if (elt.isValid())
2375+
return false;
2376+
2377+
return true;
2378+
}
2379+
2380+
bool hasConvention() const { return ConventionArguments.hasValue(); }
2381+
2382+
/// Returns the primary calling convention string.
2383+
///
2384+
/// Note: For C conventions, this may not represent the full convention.
2385+
StringRef getConventionName() const {
2386+
return ConventionArguments.getValue().Name;
2387+
}
2388+
2389+
/// Show the string enclosed between @convention(..)'s parentheses.
2390+
///
2391+
/// For example, @convention(foo, bar) will give the string "foo, bar".
2392+
void getConventionArguments(SmallVectorImpl<char> &buffer) const;
2393+
2394+
bool hasOwnership() const {
2395+
return getOwnership() != ReferenceOwnership::Strong;
2396+
}
2397+
ReferenceOwnership getOwnership() const {
2398+
#define REF_STORAGE(Name, name, ...) \
2399+
if (has(TAK_sil_##name)) return ReferenceOwnership::Name;
2400+
#include "swift/AST/ReferenceStorage.def"
2401+
return ReferenceOwnership::Strong;
2402+
}
2403+
2404+
void clearOwnership() {
2405+
#define REF_STORAGE(Name, name, ...) \
2406+
clearAttribute(TAK_sil_##name);
2407+
#include "swift/AST/ReferenceStorage.def"
2408+
}
2409+
2410+
bool hasOpenedID() const { return OpenedID.hasValue(); }
2411+
UUID getOpenedID() const { return *OpenedID; }
2412+
2413+
/// Given a name like "autoclosure", return the type attribute ID that
2414+
/// corresponds to it. This returns TAK_Count on failure.
2415+
///
2416+
static TypeAttrKind getAttrKindFromString(StringRef Str);
2417+
2418+
/// Return the name (like "autoclosure") for an attribute ID.
2419+
static const char *getAttrName(TypeAttrKind kind);
2420+
2421+
void addCustomAttr(CustomAttr *attr) {
2422+
attr->Next = CustomAttrs;
2423+
CustomAttrs = attr;
2424+
}
2425+
2426+
// Iterator for the custom type attributes.
2427+
class iterator
2428+
: public std::iterator<std::forward_iterator_tag, CustomAttr *> {
2429+
CustomAttr *attr;
2430+
2431+
public:
2432+
iterator() : attr(nullptr) { }
2433+
explicit iterator(CustomAttr *attr) : attr(attr) { }
2434+
2435+
iterator &operator++() {
2436+
attr = static_cast<CustomAttr *>(attr->Next);
2437+
return *this;
2438+
}
2439+
2440+
bool operator==(iterator x) const { return x.attr == attr; }
2441+
bool operator!=(iterator x) const { return x.attr != attr; }
2442+
2443+
CustomAttr *operator*() const { return attr; }
2444+
CustomAttr &operator->() const { return *attr; }
2445+
};
2446+
2447+
llvm::iterator_range<iterator> getCustomAttrs() const {
2448+
return llvm::make_range(iterator(CustomAttrs), iterator());
2449+
}
2450+
};
2451+
24122452
void simple_display(llvm::raw_ostream &out, const DeclAttribute *attr);
24132453

24142454
inline SourceLoc extractNearestSourceLoc(const DeclAttribute *attr) {

include/swift/Parse/Parser.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ class Parser {
656656
/// Read tokens until we get to one of the specified tokens, then
657657
/// return without consuming it. Because we cannot guarantee that the token
658658
/// will ever occur, this skips to some likely good stopping point.
659-
void skipUntil(tok T1, tok T2 = tok::NUM_TOKENS);
659+
ParserStatus skipUntil(tok T1, tok T2 = tok::NUM_TOKENS);
660660
void skipUntilAnyOperator();
661661

662662
/// Skip until a token that starts with '>', and consume it if found.
@@ -680,7 +680,10 @@ class Parser {
680680
/// Note: this does \em not match angle brackets ("<" and ">")! These are
681681
/// matched in the source when they refer to a generic type,
682682
/// but not when used as comparison operators.
683-
void skipSingle();
683+
///
684+
/// Returns a parser status that can capture whether a code completion token
685+
/// was returned.
686+
ParserStatus skipSingle();
684687

685688
/// Skip until the next '#else', '#endif' or until eof.
686689
void skipUntilConditionalBlockClose();
@@ -1061,8 +1064,23 @@ class Parser {
10611064

10621065
/// Parse a specific attribute.
10631066
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
1067+
PatternBindingInitializer *&initContext,
10641068
bool isFromClangAttribute = false);
10651069

1070+
bool isCustomAttributeArgument();
1071+
bool canParseCustomAttribute();
1072+
1073+
/// Parse a custom attribute after the initial '@'.
1074+
///
1075+
/// \param atLoc The location of the already-parsed '@'.
1076+
///
1077+
/// \param initContext A reference to the initializer context used
1078+
/// for the set of custom attributes. This should start as nullptr, and
1079+
/// will get filled in by this function. The same variable should be provided
1080+
/// for every custom attribute within the same attribute list.
1081+
ParserResult<CustomAttr> parseCustomAttribute(
1082+
SourceLoc atLoc, PatternBindingInitializer *&initContext);
1083+
10661084
bool parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
10671085
DeclAttrKind DK,
10681086
bool isFromClangAttribute = false);
@@ -1090,6 +1108,7 @@ class Parser {
10901108
TypeAttributes::Convention &convention);
10911109

10921110
bool parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc,
1111+
PatternBindingInitializer *&initContext,
10931112
bool justChecking = false);
10941113

10951114

lib/ClangImporter/ImportDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8028,6 +8028,7 @@ void ClangImporter::Implementation::importAttributes(
80288028

80298029
// Scan through Clang attributes and map them onto Swift
80308030
// equivalents.
8031+
PatternBindingInitializer *initContext = nullptr;
80318032
bool AnyUnavailable = MappedDecl->getAttrs().isUnavailable(C);
80328033
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
80338034
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
@@ -8207,7 +8208,8 @@ void ClangImporter::Implementation::importAttributes(
82078208
SourceLoc atLoc;
82088209
if (parser.consumeIf(tok::at_sign, atLoc)) {
82098210
(void)parser.parseDeclAttribute(
8210-
MappedDecl->getAttrs(), atLoc, /*isFromClangAttribute=*/true);
8211+
MappedDecl->getAttrs(), atLoc, initContext,
8212+
/*isFromClangAttribute=*/true);
82118213
} else {
82128214
// Complain about the missing '@'.
82138215
auto &clangSrcMgr = getClangASTContext().getSourceManager();

0 commit comments

Comments
 (0)