Skip to content

Commit 2fe3ce8

Browse files
committed
Requestify the Raw Value Accessor
Make getRawValueExpr() return a checked value. This entails a strange kind of request that effectively acts like a cache warmer. In order to properly check the raw value expression for a single case, we actually need all the other cases for the autoincrementing synthesis logic. The strategy is therefore to have the request act at the level of the parent EnumDecl and check all the values at once. We also cache at the level of the EnumDecl so the cache "warms" for all enum elements simultaneously. The request also abuses TypeResolutionStage to act as an indicator for how much information to compute. In the minimal case, we will return a complete accounting of (auto-incremented) raw values. In the maximal case we will also check and record types and emit diagnostics. The minimal case is uncached to support repeated evaluation. Note that computing the interface type of an @objc enum decl *must* force this request. The enum's raw values are part of the ABI, and we should not get all the way to IRGen before discovering that we cannot possibly lay out the enum. In the future, we might want to consider moving this check earlier or have IRGen tolerate broken cases but for now we will maintain the status quo and not have IRGen emit diagnostics.
1 parent da10020 commit 2fe3ce8

19 files changed

+266
-114
lines changed

include/swift/AST/ASTTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SWIFT_TYPEID(Type)
2626
SWIFT_TYPEID(TypePair)
2727
SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr)
2828
SWIFT_TYPEID_NAMED(Decl *, Decl)
29+
SWIFT_TYPEID_NAMED(EnumDecl *, EnumDecl)
2930
SWIFT_TYPEID_NAMED(GenericParamList *, GenericParamList)
3031
SWIFT_TYPEID_NAMED(GenericTypeParamType *, GenericTypeParamType)
3132
SWIFT_TYPEID_NAMED(InfixOperatorDecl *, InfixOperatorDecl)

include/swift/AST/ASTTypeIDs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class AbstractFunctionDecl;
2525
class BraceStmt;
2626
class CustomAttr;
2727
class Decl;
28+
class EnumDecl;
2829
class GenericParamList;
2930
class GenericSignature;
3031
class GenericTypeParamType;

include/swift/AST/Decl.h

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3519,12 +3519,37 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
35193519
class EnumDecl final : public NominalTypeDecl {
35203520
SourceLoc EnumLoc;
35213521

3522+
enum SemanticInfoFlags : uint8_t {
3523+
// Is the raw type valid?
3524+
HasComputedRawType = 1 << 0,
3525+
// Is the complete set of (auto-incremented) raw values available?
3526+
HasFixedRawValues = 1 << 1,
3527+
// Is the complete set of raw values type checked?
3528+
HasFixedRawValuesAndTypes = 1 << 2,
3529+
};
3530+
35223531
struct {
35233532
/// The raw type and a bit to indicate whether the
35243533
/// raw was computed yet or not.
3525-
llvm::PointerIntPair<Type, 1, bool> RawType;
3534+
llvm::PointerIntPair<Type, 3, OptionSet<SemanticInfoFlags>> RawTypeAndFlags;
3535+
3536+
bool hasRawType() const {
3537+
return RawTypeAndFlags.getInt().contains(HasComputedRawType);
3538+
}
3539+
void cacheRawType(Type ty) {
3540+
auto flags = RawTypeAndFlags.getInt() | HasComputedRawType;
3541+
RawTypeAndFlags.setPointerAndInt(ty, flags);
3542+
}
3543+
3544+
bool hasFixedRawValues() const {
3545+
return RawTypeAndFlags.getInt().contains(HasFixedRawValues);
3546+
}
3547+
bool hasCheckedRawValues() const {
3548+
return RawTypeAndFlags.getInt().contains(HasFixedRawValuesAndTypes);
3549+
}
35263550
} LazySemanticInfo;
35273551

3552+
friend class EnumRawValuesRequest;
35283553
friend class EnumRawTypeRequest;
35293554
friend class TypeChecker;
35303555

@@ -3583,6 +3608,9 @@ class EnumDecl final : public NominalTypeDecl {
35833608
Bits.EnumDecl.Circularity = static_cast<unsigned>(circularity);
35843609
}
35853610

3611+
/// Record that this enum has had all of its raw values computed.
3612+
void setHasFixedRawValues();
3613+
35863614
// Implement isa/cast/dyncast/etc.
35873615
static bool classof(const Decl *D) {
35883616
return D->getKind() == DeclKind::Enum;
@@ -3612,9 +3640,11 @@ class EnumDecl final : public NominalTypeDecl {
36123640

36133641
/// Set the raw type of the enum from its inheritance clause.
36143642
void setRawType(Type rawType) {
3615-
LazySemanticInfo.RawType.setPointerAndInt(rawType, true);
3643+
auto flags = LazySemanticInfo.RawTypeAndFlags.getInt();
3644+
LazySemanticInfo.RawTypeAndFlags.setPointerAndInt(
3645+
rawType, flags | HasComputedRawType);
36163646
}
3617-
3647+
36183648
/// True if none of the enum cases have associated values.
36193649
///
36203650
/// Note that this is true for enums with absolutely no cases.
@@ -6354,6 +6384,8 @@ class EnumCaseDecl final : public Decl,
63546384
/// parent EnumDecl, although syntactically they are subordinate to the
63556385
/// EnumCaseDecl.
63566386
class EnumElementDecl : public DeclContext, public ValueDecl {
6387+
friend class EnumRawValuesRequest;
6388+
63576389
/// This is the type specified with the enum element, for
63586390
/// example 'Int' in 'case Y(Int)'. This is null if there is no type
63596391
/// associated with this element, as in 'case Z' or in all elements of enum
@@ -6394,9 +6426,20 @@ class EnumElementDecl : public DeclContext, public ValueDecl {
63946426

63956427
ParameterList *getParameterList() const { return Params; }
63966428

6397-
bool hasRawValueExpr() const { return RawValueExpr; }
6398-
LiteralExpr *getRawValueExpr() const { return RawValueExpr; }
6399-
void setRawValueExpr(LiteralExpr *e) { RawValueExpr = e; }
6429+
/// Retrieves a fully typechecked raw value expression associated
6430+
/// with this enum element, if it exists.
6431+
LiteralExpr *getRawValueExpr() const;
6432+
6433+
/// Retrieves a "structurally" checked raw value expression associated
6434+
/// with this enum element, if it exists.
6435+
///
6436+
/// The structural raw value may or may not have a type set, but it is
6437+
/// guaranteed to be suitable for retrieving any non-semantic information
6438+
/// like digit text for an integral raw value or user text for a string raw value.
6439+
LiteralExpr *getStructuralRawValueExpr() const;
6440+
6441+
/// Reset the raw value expression.
6442+
void setRawValueExpr(LiteralExpr *e);
64006443

64016444
/// Return the containing EnumDecl.
64026445
EnumDecl *getParentEnum() const {
@@ -6419,6 +6462,10 @@ class EnumElementDecl : public DeclContext, public ValueDecl {
64196462
bool isIndirect() const {
64206463
return getAttrs().hasAttribute<IndirectAttr>();
64216464
}
6465+
6466+
/// Do not call this!
6467+
/// It exists to let the AST walkers get the raw value without forcing a request.
6468+
LiteralExpr *getRawValueUnchecked() const { return RawValueExpr; }
64226469

64236470
static bool classof(const Decl *D) {
64246471
return D->getKind() == DeclKind::EnumElement;

include/swift/AST/TypeCheckRequests.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,31 @@ class OperatorPrecedenceGroupRequest
12301230
bool isCached() const { return true; }
12311231
};
12321232

1233+
class EnumRawValuesRequest :
1234+
public SimpleRequest<EnumRawValuesRequest,
1235+
bool (EnumDecl *, TypeResolutionStage),
1236+
CacheKind::SeparatelyCached> {
1237+
public:
1238+
using SimpleRequest::SimpleRequest;
1239+
1240+
private:
1241+
friend SimpleRequest;
1242+
1243+
// Evaluation.
1244+
llvm::Expected<bool>
1245+
evaluate(Evaluator &evaluator, EnumDecl *ED, TypeResolutionStage stage) const;
1246+
1247+
public:
1248+
// Cycle handling.
1249+
void diagnoseCycle(DiagnosticEngine &diags) const;
1250+
void noteCycleStep(DiagnosticEngine &diags) const;
1251+
1252+
// Separate caching.
1253+
bool isCached() const;
1254+
Optional<bool> getCachedResult() const;
1255+
void cacheResult(bool value) const;
1256+
};
1257+
12331258
// Allow AnyValue to compare two Type values, even though Type doesn't
12341259
// support ==.
12351260
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ SWIFT_REQUEST(TypeChecker, DefaultTypeRequest,
3636
NoLocationInfo)
3737
SWIFT_REQUEST(TypeChecker, EmittedMembersRequest, DeclRange(ClassDecl *),
3838
SeparatelyCached, NoLocationInfo)
39+
SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest,
40+
bool (EnumDecl *, TypeResolutionStage), SeparatelyCached,
41+
NoLocationInfo)
3942
SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest,
4043
Type(EnumDecl *, TypeResolutionStage), SeparatelyCached,
4144
NoLocationInfo)

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2893,7 +2893,7 @@ void PrintAST::printEnumElement(EnumElementDecl *elt) {
28932893
break;
28942894
}
28952895

2896-
auto *raw = elt->getRawValueExpr();
2896+
auto *raw = elt->getStructuralRawValueExpr();
28972897
if (!raw || raw->isImplicit())
28982898
return;
28992899

lib/AST/ASTScopeCreation.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,8 +1346,12 @@ void EnumElementScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
13461346
if (auto *pl = decl->getParameterList())
13471347
scopeCreator.constructExpandAndInsertUncheckable<ParameterListScope>(
13481348
this, pl, nullptr);
1349-
// might contain a closure
1350-
scopeCreator.addToScopeTree(decl->getRawValueExpr(), this);
1349+
// The invariant that the raw value expression can never introduce a new scope
1350+
// is checked in Parse. However, this guarantee is not future-proof. Compute
1351+
// and add the raw value expression anyways just to be defensive.
1352+
//
1353+
// FIXME: Re-enable this. It currently crashes for malformed enum cases.
1354+
// scopeCreator.addToScopeTree(decl->getStructuralRawValueExpr(), this);
13511355
}
13521356

13531357
void AbstractFunctionBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(

lib/AST/ASTWalker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
403403
visit(PL);
404404
}
405405

406-
if (auto *rawLiteralExpr = ED->getRawValueExpr()) {
406+
if (auto *rawLiteralExpr = ED->getRawValueUnchecked()) {
407407
Expr *newRawExpr = doIt(rawLiteralExpr);
408408
if (auto newRawLiteralExpr = dyn_cast<LiteralExpr>(newRawExpr))
409409
ED->setRawValueExpr(newRawLiteralExpr);

lib/AST/Decl.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4292,6 +4292,12 @@ bool EnumDecl::isEffectivelyExhaustive(ModuleDecl *M,
42924292
"these should match up");
42934293
return !isResilient(M, expansion);
42944294
}
4295+
4296+
void EnumDecl::setHasFixedRawValues() {
4297+
auto flags = LazySemanticInfo.RawTypeAndFlags.getInt() |
4298+
EnumDecl::HasFixedRawValues;
4299+
LazySemanticInfo.RawTypeAndFlags.setInt(flags);
4300+
}
42954301

42964302
ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
42974303
SourceLoc NameLoc, Identifier Name,
@@ -7197,6 +7203,32 @@ EnumCaseDecl *EnumElementDecl::getParentCase() const {
71977203

71987204
llvm_unreachable("enum element not in case of parent enum");
71997205
}
7206+
7207+
LiteralExpr *EnumElementDecl::getRawValueExpr() const {
7208+
// The return value of this request is irrelevant - it exists as
7209+
// a cache-warmer.
7210+
(void)evaluateOrDefault(
7211+
getASTContext().evaluator,
7212+
EnumRawValuesRequest{getParentEnum(), TypeResolutionStage::Interface},
7213+
true);
7214+
return RawValueExpr;
7215+
}
7216+
7217+
LiteralExpr *EnumElementDecl::getStructuralRawValueExpr() const {
7218+
// The return value of this request is irrelevant - it exists as
7219+
// a cache-warmer.
7220+
(void)evaluateOrDefault(
7221+
getASTContext().evaluator,
7222+
EnumRawValuesRequest{getParentEnum(), TypeResolutionStage::Structural},
7223+
true);
7224+
return RawValueExpr;
7225+
}
7226+
7227+
void EnumElementDecl::setRawValueExpr(LiteralExpr *e) {
7228+
assert((!RawValueExpr || e == RawValueExpr || e->getType()) &&
7229+
"Illegal mutation of raw value expr");
7230+
RawValueExpr = e;
7231+
}
72007232

72017233
SourceRange ConstructorDecl::getSourceRange() const {
72027234
if (isImplicit())

lib/AST/TypeCheckRequests.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,15 @@ bool EnumRawTypeRequest::isCached() const {
167167

168168
Optional<Type> EnumRawTypeRequest::getCachedResult() const {
169169
auto enumDecl = std::get<0>(getStorage());
170-
if (enumDecl->LazySemanticInfo.RawType.getInt())
171-
return enumDecl->LazySemanticInfo.RawType.getPointer();
170+
if (enumDecl->LazySemanticInfo.hasRawType())
171+
return enumDecl->LazySemanticInfo.RawTypeAndFlags.getPointer();
172172

173173
return None;
174174
}
175175

176176
void EnumRawTypeRequest::cacheResult(Type value) const {
177177
auto enumDecl = std::get<0>(getStorage());
178-
enumDecl->LazySemanticInfo.RawType.setPointerAndInt(value, true);
178+
enumDecl->LazySemanticInfo.cacheRawType(value);
179179
}
180180

181181
//----------------------------------------------------------------------------//
@@ -839,7 +839,7 @@ void InferredGenericSignatureRequest::noteCycleStep(DiagnosticEngine &d) const {
839839
}
840840

841841
//----------------------------------------------------------------------------//
842-
// IsImplicitlyUnwrappedOptionalRequest computation.
842+
// UnderlyingTypeRequest computation.
843843
//----------------------------------------------------------------------------//
844844

845845
Optional<Type>
@@ -854,3 +854,36 @@ void UnderlyingTypeRequest::cacheResult(Type value) const {
854854
auto *typeAlias = std::get<0>(getStorage());
855855
typeAlias->UnderlyingTy.setType(value);
856856
}
857+
858+
//----------------------------------------------------------------------------//
859+
// EnumRawValuesRequest computation.
860+
//----------------------------------------------------------------------------//
861+
862+
bool EnumRawValuesRequest::isCached() const {
863+
return std::get<1>(getStorage()) == TypeResolutionStage::Interface;
864+
}
865+
866+
Optional<bool> EnumRawValuesRequest::getCachedResult() const {
867+
auto *ED = std::get<0>(getStorage());
868+
if (ED->LazySemanticInfo.hasCheckedRawValues())
869+
return true;
870+
return None;
871+
}
872+
873+
void EnumRawValuesRequest::cacheResult(bool) const {
874+
auto *ED = std::get<0>(getStorage());
875+
auto flags = ED->LazySemanticInfo.RawTypeAndFlags.getInt() |
876+
EnumDecl::HasFixedRawValues |
877+
EnumDecl::HasFixedRawValuesAndTypes;
878+
ED->LazySemanticInfo.RawTypeAndFlags.setInt(flags);
879+
}
880+
881+
void EnumRawValuesRequest::diagnoseCycle(DiagnosticEngine &diags) const {
882+
// This request computes the raw type, and so participates in cycles involving
883+
// it. For now, the raw type provides a rich enough circularity diagnostic
884+
// that we can silence ourselves.
885+
}
886+
887+
void EnumRawValuesRequest::noteCycleStep(DiagnosticEngine &diags) const {
888+
889+
}

lib/IDE/SyntaxModel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) {
916916
EnumElemD->getName().getLength());
917917
}
918918

919-
if (auto *E = EnumElemD->getRawValueExpr()) {
919+
if (auto *E = EnumElemD->getRawValueUnchecked()) {
920920
SourceRange ElemRange = E->getSourceRange();
921921
SN.Elements.emplace_back(SyntaxStructureElementKind::InitExpr,
922922
charSourceRangeFromSourceRange(SM, ElemRange));

lib/IRGen/GenEnum.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1128,7 +1128,7 @@ namespace {
11281128
int64_t getDiscriminatorIndex(EnumElementDecl *target) const override {
11291129
// The elements are assigned discriminators ABI-compatible with their
11301130
// raw values from C.
1131-
assert(target->hasRawValueExpr()
1131+
assert(target->getRawValueExpr()
11321132
&& "c-compatible enum elt has no raw value?!");
11331133
auto intExpr = cast<IntegerLiteralExpr>(target->getRawValueExpr());
11341134
auto intType = getDiscriminatorType();

lib/PrintAsObjC/DeclAndTypePrinter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ class DeclAndTypePrinter::Implementation
393393
os << " SWIFT_COMPILE_NAME(\"" << Elt->getName() << "\")";
394394
}
395395

396-
if (auto ILE = cast_or_null<IntegerLiteralExpr>(Elt->getRawValueExpr())) {
396+
auto *RVE = Elt->getStructuralRawValueExpr();
397+
if (auto ILE = cast_or_null<IntegerLiteralExpr>(RVE)) {
397398
os << " = ";
398399
if (ILE->isNegative())
399400
os << "-";

0 commit comments

Comments
 (0)