Skip to content

Commit beb2bd2

Browse files
committed
AST: support @_effects attribute with custom strings.
In addition to the predefined cases, like "readnone", "readonly", etc. support providing a custom string, which will be parsed later. Also, allow multiple effects attributes to be put onto a function.
1 parent 67c2b75 commit beb2bd2

File tree

13 files changed

+128
-42
lines changed

13 files changed

+128
-42
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ CONTEXTUAL_DECL_ATTR(weak, ReferenceOwnership,
323323
CONTEXTUAL_DECL_ATTR_ALIAS(unowned, ReferenceOwnership)
324324
DECL_ATTR(_effects, Effects,
325325
OnAbstractFunction |
326-
UserInaccessible |
326+
AllowMultipleAttributes | UserInaccessible |
327327
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
328328
50)
329329
DECL_ATTR(__objc_bridged, ObjCBridged,

include/swift/AST/Attr.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,15 +1227,37 @@ class ExclusivityAttr : public DeclAttribute {
12271227

12281228
/// Represents the side effects attribute.
12291229
class EffectsAttr : public DeclAttribute {
1230+
StringRef customString;
1231+
SourceLoc customStringLoc;
1232+
12301233
public:
12311234
EffectsAttr(SourceLoc atLoc, SourceRange range, EffectsKind kind)
12321235
: DeclAttribute(DAK_Effects, atLoc, range, /*Implicit=*/false) {
12331236
Bits.EffectsAttr.kind = unsigned(kind);
12341237
}
12351238

1239+
EffectsAttr(SourceLoc atLoc, SourceRange range, StringRef customString,
1240+
SourceLoc customStringLoc)
1241+
: DeclAttribute(DAK_Effects, atLoc, range, /*Implicit=*/false),
1242+
customString(customString), customStringLoc(customStringLoc) {
1243+
Bits.EffectsAttr.kind = unsigned(EffectsKind::Custom);
1244+
}
1245+
12361246
EffectsAttr(EffectsKind kind)
12371247
: EffectsAttr(SourceLoc(), SourceRange(), kind) {}
12381248

1249+
EffectsAttr(StringRef customString)
1250+
: EffectsAttr(SourceLoc(), SourceRange(), customString, SourceLoc()) {}
1251+
1252+
StringRef getCustomString() const {
1253+
assert(getKind() == EffectsKind::Custom);
1254+
return customString;
1255+
}
1256+
1257+
SourceLoc getCustomStringLocation() const {
1258+
return customStringLoc;
1259+
}
1260+
12391261
EffectsKind getKind() const { return EffectsKind(Bits.EffectsAttr.kind); }
12401262
static bool classof(const DeclAttribute *DA) {
12411263
return DA->getKind() == DAK_Effects;

include/swift/AST/AttrKind.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ enum class EffectsKind : uint8_t {
8989
ReleaseNone,
9090
ReadWrite,
9191
Unspecified,
92+
Custom,
9293
Last_EffectsKind = Unspecified
9394
};
9495

include/swift/AST/DiagnosticsParse.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,11 @@ ERROR(mutually_exclusive_attrs,none,
14111411
ERROR(invalid_infix_on_func,none,
14121412
"'infix' modifier is not required or allowed on func declarations", ())
14131413

1414+
ERROR(error_in_effects_attribute,none,
1415+
"error in effects attribute: %0", (StringRef))
1416+
WARNING(warning_in_effects_attribute,none,
1417+
"effects attribute ignored: %0", (StringRef))
1418+
14141419
ERROR(expected_in_attribute_list,none,
14151420
"expected ']' or ',' in attribute list", ())
14161421

include/swift/SIL/SILDeclRef.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ namespace swift {
4242
class AutoClosureExpr;
4343
class ASTContext;
4444
class ClassDecl;
45+
class EffectsAttr;
4546
class FileUnit;
4647
class SILFunctionType;
4748
enum IsSerialized_t : unsigned char;
@@ -344,12 +345,6 @@ struct SILDeclRef {
344345
/// True if the function has __always inline attribute.
345346
bool isAlwaysInline() const;
346347

347-
/// \return True if the function has an effects attribute.
348-
bool hasEffectsAttribute() const;
349-
350-
/// \return the effects kind of the function.
351-
EffectsKind getEffectsAttribute() const;
352-
353348
/// Return the expected linkage of this declaration.
354349
SILLinkage getLinkage(ForDefinition_t forDefinition) const;
355350

lib/AST/Attr.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
885885
case DAK_Optimize:
886886
case DAK_Exclusivity:
887887
case DAK_NonSendable:
888-
if (DeclAttribute::isDeclModifier(getKind())) {
888+
if (getKind() == DAK_Effects &&
889+
cast<EffectsAttr>(this)->getKind() == EffectsKind::Custom) {
890+
Printer.printAttrName("@_effects");
891+
Printer << "(" << cast<EffectsAttr>(this)->getCustomString() << ")";
892+
} else if (DeclAttribute::isDeclModifier(getKind())) {
889893
Printer.printKeyword(getAttrName(), Options);
890894
} else if (Options.IsForSwiftInterface && getKind() == DAK_ResultBuilder) {
891895
// Use @_functionBuilder in Swift interfaces to maintain backward
@@ -1312,6 +1316,8 @@ StringRef DeclAttribute::getAttrName() const {
13121316
return "_effects(readwrite)";
13131317
case EffectsKind::Unspecified:
13141318
return "_effects(unspecified)";
1319+
case EffectsKind::Custom:
1320+
return "_effects";
13151321
}
13161322
case DAK_AccessControl:
13171323
case DAK_SetterAccess: {

lib/Parse/ParseDecl.cpp

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,19 +1949,58 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
19491949
break;
19501950

19511951
case DAK_Effects: {
1952-
auto kind = parseSingleAttrOption<EffectsKind>
1953-
(*this, Loc, AttrRange, AttrName, DK)
1954-
.when("readonly", EffectsKind::ReadOnly)
1955-
.when("readnone", EffectsKind::ReadNone)
1956-
.when("readwrite", EffectsKind::ReadWrite)
1957-
.when("releasenone", EffectsKind::ReleaseNone)
1958-
.diagnoseWhenOmitted();
1959-
if (!kind)
1960-
return false;
1952+
if (!consumeIf(tok::l_paren)) {
1953+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1954+
DeclAttribute::isDeclModifier(DK)); return false;
1955+
}
1956+
EffectsKind kind = EffectsKind::Unspecified;
1957+
SourceLoc customStart, customEnd;
1958+
{
1959+
SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList);
19611960

1962-
if (!DiscardAttribute)
1963-
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, *kind));
1961+
if (Tok.isNot(tok::identifier)) {
1962+
diagnose(Loc, diag::error_in_effects_attribute, "expected identifier");
1963+
return false;
1964+
}
19641965

1966+
if (Tok.getText() == "readonly")
1967+
kind = EffectsKind::ReadOnly;
1968+
else if (Tok.getText() == "readnone")
1969+
kind = EffectsKind::ReadNone;
1970+
else if (Tok.getText() == "readwrite")
1971+
kind = EffectsKind::ReadWrite;
1972+
else if (Tok.getText() == "releasenone")
1973+
kind = EffectsKind::ReleaseNone;
1974+
else {
1975+
customStart = customEnd = Tok.getLoc();
1976+
while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) {
1977+
consumeToken();
1978+
}
1979+
customEnd = Tok.getLoc();
1980+
kind = EffectsKind::Custom;
1981+
AttrRange = SourceRange(Loc, customEnd);
1982+
}
1983+
if (kind != EffectsKind::Custom) {
1984+
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
1985+
consumeToken(tok::identifier);
1986+
}
1987+
}
1988+
if (!consumeIf(tok::r_paren)) {
1989+
diagnose(Loc, diag::attr_expected_rparen, AttrName,
1990+
DeclAttribute::isDeclModifier(DK));
1991+
return false;
1992+
}
1993+
1994+
if (!DiscardAttribute) {
1995+
if (kind == EffectsKind::Custom) {
1996+
StringRef customStr = SourceMgr.extractText(
1997+
CharSourceRange(SourceMgr, customStart, customEnd));
1998+
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange,
1999+
customStr, customStart));
2000+
} else {
2001+
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, kind));
2002+
}
2003+
}
19652004
break;
19662005
}
19672006

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -727,18 +727,6 @@ bool SILDeclRef::isAlwaysInline() const {
727727
return false;
728728
}
729729

730-
bool SILDeclRef::hasEffectsAttribute() const {
731-
if (!hasDecl())
732-
return false;
733-
return getDecl()->getAttrs().hasAttribute<EffectsAttr>();
734-
}
735-
736-
EffectsKind SILDeclRef::getEffectsAttribute() const {
737-
assert(hasEffectsAttribute());
738-
EffectsAttr *MA = getDecl()->getAttrs().getAttribute<EffectsAttr>();
739-
return MA->getKind();
740-
}
741-
742730
bool SILDeclRef::isAnyThunk() const {
743731
return isForeignToNativeThunk() ||
744732
isNativeToForeignThunk() ||

lib/SIL/IR/SILFunctionBuilder.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/SIL/SILFunctionBuilder.h"
1414
#include "swift/AST/AttrKind.h"
1515
#include "swift/AST/Availability.h"
16+
#include "swift/AST/DiagnosticsParse.h"
1617
#include "swift/AST/Decl.h"
1718
#include "swift/AST/SemanticAttrs.h"
1819

@@ -98,6 +99,24 @@ void SILFunctionBuilder::addFunctionAttributes(
9899
}
99100
}
100101

102+
llvm::SmallVector<const EffectsAttr *, 8> customEffects;
103+
for (auto *attr : Attrs.getAttributes<EffectsAttr>()) {
104+
auto *effectsAttr = cast<EffectsAttr>(attr);
105+
if (effectsAttr->getKind() == EffectsKind::Custom) {
106+
customEffects.push_back(effectsAttr);
107+
} else {
108+
if (F->getEffectsKind() != EffectsKind::Unspecified &&
109+
F->getEffectsKind() != effectsAttr->getKind()) {
110+
mod.getASTContext().Diags.diagnose(effectsAttr->getLocation(),
111+
diag::warning_in_effects_attribute, "mismatching function effects");
112+
} else {
113+
F->setEffectsKind(effectsAttr->getKind());
114+
}
115+
}
116+
}
117+
118+
// TODO: handle custom effects.
119+
101120
if (auto *OA = Attrs.getAttribute<OptimizeAttr>()) {
102121
F->setOptimizationMode(OA->getMode());
103122
}
@@ -213,10 +232,6 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction(
213232
constant.isTransparent() ? IsTransparent : IsNotTransparent;
214233
IsSerialized_t IsSer = constant.isSerialized();
215234

216-
EffectsKind EK = constant.hasEffectsAttribute()
217-
? constant.getEffectsAttribute()
218-
: EffectsKind::Unspecified;
219-
220235
Inline_t inlineStrategy = InlineDefault;
221236
if (constant.isNoinline())
222237
inlineStrategy = NoInline;
@@ -240,7 +255,7 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction(
240255
IsNotBare, IsTrans, IsSer, entryCount, IsDyn,
241256
IsDistributed, IsNotExactSelfClass,
242257
IsNotThunk, constant.getSubclassScope(),
243-
inlineStrategy, EK);
258+
inlineStrategy);
244259
F->setDebugScope(new (mod) SILDebugScope(loc, F));
245260

246261
if (constant.isGlobal())

lib/Serialization/Deserialization.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4426,9 +4426,15 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
44264426

44274427
case decls_block::Effects_DECL_ATTR: {
44284428
unsigned kind;
4429-
serialization::decls_block::EffectsDeclAttrLayout::readRecord(scratch,
4430-
kind);
4431-
Attr = new (ctx) EffectsAttr((EffectsKind)kind);
4429+
IdentifierID customStringID;
4430+
serialization::decls_block::EffectsDeclAttrLayout::
4431+
readRecord(scratch, kind, customStringID);
4432+
if (customStringID) {
4433+
assert((EffectsKind)kind == EffectsKind::Custom);
4434+
Attr = new (ctx) EffectsAttr(MF.getIdentifier(customStringID).str());
4435+
} else {
4436+
Attr = new (ctx) EffectsAttr((EffectsKind)kind);
4437+
}
44324438
break;
44334439
}
44344440
case decls_block::OriginallyDefinedIn_DECL_ATTR: {

0 commit comments

Comments
 (0)