Skip to content

Commit d1554f9

Browse files
committed
Track and verify how an l-value expression is used in the AST.
I'll use this information in a follow-up commit. Swift SVN r32292
1 parent 7eb830f commit d1554f9

File tree

8 files changed

+269
-4
lines changed

8 files changed

+269
-4
lines changed

include/swift/AST/Expr.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace llvm {
3434
}
3535

3636
namespace swift {
37+
enum class AccessKind : unsigned char;
3738
class ArchetypeType;
3839
class ASTContext;
3940
class AvailabilitySpec;
@@ -118,11 +119,13 @@ class alignas(8) Expr {
118119
friend class Expr;
119120
/// The subclass of Expr that this is.
120121
unsigned Kind : 8;
122+
/// How this l-value is used, if it's an l-value.
123+
unsigned LValueAccessKind : 2;
121124
/// Whether the Expr represents something directly written in source or
122125
/// it was implicitly generated by the type-checker.
123126
unsigned Implicit : 1;
124127
};
125-
enum { NumExprBits = 10 };
128+
enum { NumExprBits = 11 };
126129
static_assert(NumExprBits <= 32, "fits in an unsigned");
127130

128131
class LiteralExprBitfields {
@@ -327,11 +330,16 @@ class alignas(8) Expr {
327330
private:
328331
/// Ty - This is the type of the expression.
329332
Type Ty;
333+
334+
void setLValueAccessKind(AccessKind accessKind) {
335+
ExprBits.LValueAccessKind = unsigned(accessKind) + 1;
336+
}
330337

331338
protected:
332339
Expr(ExprKind Kind, bool Implicit, Type Ty = Type()) : Ty(Ty) {
333340
ExprBits.Kind = unsigned(Kind);
334341
ExprBits.Implicit = Implicit;
342+
ExprBits.LValueAccessKind = 0;
335343
}
336344

337345
public:
@@ -434,6 +442,25 @@ class alignas(8) Expr {
434442
void setImplicit(bool Implicit = true) {
435443
ExprBits.Implicit = Implicit;
436444
}
445+
446+
/// getLValueAccessKind - Determines how this l-value expression is used.
447+
AccessKind getLValueAccessKind() const {
448+
assert(hasLValueAccessKind());
449+
return AccessKind(ExprBits.LValueAccessKind - 1);
450+
}
451+
bool hasLValueAccessKind() const {
452+
return ExprBits.LValueAccessKind != 0;
453+
}
454+
455+
/// Set that this l-value expression is used in the given way.
456+
///
457+
/// This information is also correctly propagated to any l-value
458+
/// sub-expressions from which this l-value is derived.
459+
///
460+
/// \param allowOverwrite - true if it's okay if an expression already
461+
/// has an access kind
462+
void propagateLValueAccessKind(AccessKind accessKind,
463+
bool allowOverwrite = false);
437464

438465
/// Determine whether this expression is 'super', possibly converted to
439466
/// a base class.

include/swift/AST/Types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,11 @@ class alignas(1 << TypeAlignInBits) TypeBase {
453453
return getRecursiveProperties().hasUnboundGeneric();
454454
}
455455

456+
/// \brief Check if this type is a valid type for the LHS of an assignment.
457+
/// This mainly means isLValueType(), but empty tuples and tuples of empty
458+
/// tuples also qualify.
459+
bool isAssignableType();
460+
456461
/// isExistentialType - Determines whether this type is an existential type,
457462
/// whose real (runtime) type is unknown but which is known to conform to
458463
/// some set of protocols. Protocol and protocol-conformance types are

lib/AST/ASTDumper.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,12 +1359,24 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
13591359
}
13601360
void printRec(TypeRepr *T);
13611361

1362+
static const char *getAccessKindString(AccessKind kind) {
1363+
switch (kind) {
1364+
case AccessKind::Read: return "read";
1365+
case AccessKind::Write: return "write";
1366+
case AccessKind::ReadWrite: return "readwrite";
1367+
}
1368+
llvm_unreachable("bad access kind");
1369+
}
1370+
13621371
raw_ostream &printCommon(Expr *E, const char *C) {
13631372
OS.indent(Indent) << '(' << C;
13641373
if (E->isImplicit())
13651374
OS << " implicit";
13661375
OS << " type='" << E->getType() << '\'';
13671376

1377+
if (E->hasLValueAccessKind())
1378+
OS << " accessKind=" << getAccessKindString(E->getLValueAccessKind());
1379+
13681380
// If we have a source range and an ASTContext, print the source range.
13691381
if (auto Ty = E->getType()) {
13701382
auto &Ctx = Ty->getASTContext();

lib/AST/Expr.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/AST/Expr.h"
1818
#include "swift/Basic/Unicode.h"
19+
#include "swift/AST/ASTVisitor.h"
1920
#include "swift/AST/Decl.h" // FIXME: Bad dependency
2021
#include "swift/AST/Stmt.h"
2122
#include "swift/AST/AST.h"
@@ -182,6 +183,148 @@ Expr *Expr::getValueProvidingExpr() {
182183
return E;
183184
}
184185

186+
/// Propagate l-value use information to children.
187+
void Expr::propagateLValueAccessKind(AccessKind accessKind,
188+
bool allowOverwrite) {
189+
/// A visitor class which walks an entire l-value expression.
190+
class PropagateAccessKind
191+
: public ExprVisitor<PropagateAccessKind, void, AccessKind> {
192+
#ifndef NDEBUG
193+
bool AllowOverwrite;
194+
#endif
195+
public:
196+
PropagateAccessKind(bool allowOverwrite)
197+
#ifndef NDEBUG
198+
: AllowOverwrite(allowOverwrite)
199+
#endif
200+
{}
201+
202+
void visit(Expr *E, AccessKind kind) {
203+
assert((AllowOverwrite || !E->hasLValueAccessKind()) &&
204+
"l-value access kind has already been set");
205+
206+
assert(E->getType()->isAssignableType() &&
207+
"setting access kind on non-l-value");
208+
E->setLValueAccessKind(kind);
209+
210+
// Propagate this to sub-expressions.
211+
ASTVisitor::visit(E, kind);
212+
}
213+
214+
#define NON_LVALUE_EXPR(KIND) \
215+
void visit##KIND##Expr(KIND##Expr *, AccessKind accessKind) { \
216+
llvm_unreachable("not an l-value"); \
217+
}
218+
#define LEAF_LVALUE_EXPR(KIND) \
219+
void visit##KIND##Expr(KIND##Expr *E, AccessKind accessKind) {}
220+
#define COMPLETE_PHYSICAL_LVALUE_EXPR(KIND, ACCESSOR) \
221+
void visit##KIND##Expr(KIND##Expr *E, AccessKind accessKind) { \
222+
visit(E->ACCESSOR, accessKind); \
223+
}
224+
#define PARTIAL_PHYSICAL_LVALUE_EXPR(KIND, ACCESSOR) \
225+
void visit##KIND##Expr(KIND##Expr *E, AccessKind accessKind) { \
226+
visit(E->ACCESSOR, getPartialAccessKind(accessKind)); \
227+
}
228+
229+
void visitMemberRefExpr(MemberRefExpr *E, AccessKind accessKind) {
230+
if (!E->getBase()->getType()->isLValueType()) return;
231+
visit(E->getBase(), getBaseAccessKind(E->getMember(), accessKind));
232+
}
233+
void visitSubscriptExpr(SubscriptExpr *E, AccessKind accessKind) {
234+
if (!E->getBase()->getType()->isLValueType()) return;
235+
visit(E->getBase(), getBaseAccessKind(E->getDecl(), accessKind));
236+
}
237+
238+
static AccessKind getPartialAccessKind(AccessKind accessKind) {
239+
return (accessKind == AccessKind::Read
240+
? accessKind : AccessKind::ReadWrite);
241+
}
242+
243+
static AccessKind getBaseAccessKind(ConcreteDeclRef member,
244+
AccessKind accessKind) {
245+
// We assume writes are partial writes, so the result is always
246+
// either Read or ReadWrite.
247+
auto memberDecl = cast<AbstractStorageDecl>(member.getDecl());
248+
249+
// If we're reading and the getter is mutating, or we're writing
250+
// and the setter is mutating, this is readwrite.
251+
if ((accessKind != AccessKind::Write &&
252+
memberDecl->isGetterMutating()) ||
253+
(accessKind != AccessKind::Read &&
254+
!memberDecl->isSetterNonMutating())) {
255+
return AccessKind::ReadWrite;
256+
}
257+
258+
return AccessKind::Read;
259+
}
260+
261+
void visitTupleExpr(TupleExpr *E, AccessKind accessKind) {
262+
for (auto elt : E->getElements()) {
263+
visit(elt, accessKind);
264+
}
265+
}
266+
267+
void visitOpenExistentialExpr(OpenExistentialExpr *E,
268+
AccessKind accessKind) {
269+
bool opaqueValueHadAK = E->getOpaqueValue()->hasLValueAccessKind();
270+
AccessKind oldOpaqueValueAK =
271+
(opaqueValueHadAK ? E->getOpaqueValue()->getLValueAccessKind()
272+
: AccessKind::Read);
273+
274+
visit(E->getSubExpr(), accessKind);
275+
276+
// Propagate the new access kind from the OVE to the original existential
277+
// if we just set or changed it on the OVE.
278+
if (E->getOpaqueValue()->hasLValueAccessKind()) {
279+
auto newOpaqueValueAK = E->getOpaqueValue()->getLValueAccessKind();
280+
if (!opaqueValueHadAK || newOpaqueValueAK != oldOpaqueValueAK)
281+
visit(E->getExistentialValue(), newOpaqueValueAK);
282+
}
283+
}
284+
285+
LEAF_LVALUE_EXPR(DeclRef)
286+
LEAF_LVALUE_EXPR(DiscardAssignment)
287+
LEAF_LVALUE_EXPR(DynamicLookup)
288+
LEAF_LVALUE_EXPR(OpaqueValue)
289+
290+
COMPLETE_PHYSICAL_LVALUE_EXPR(AnyTry, getSubExpr())
291+
PARTIAL_PHYSICAL_LVALUE_EXPR(BindOptional, getSubExpr())
292+
COMPLETE_PHYSICAL_LVALUE_EXPR(DotSyntaxBaseIgnored, getRHS());
293+
PARTIAL_PHYSICAL_LVALUE_EXPR(ForceValue, getSubExpr())
294+
COMPLETE_PHYSICAL_LVALUE_EXPR(Identity, getSubExpr())
295+
PARTIAL_PHYSICAL_LVALUE_EXPR(TupleElement, getBase())
296+
297+
NON_LVALUE_EXPR(Error)
298+
NON_LVALUE_EXPR(Literal)
299+
NON_LVALUE_EXPR(SuperRef)
300+
NON_LVALUE_EXPR(Type)
301+
NON_LVALUE_EXPR(OtherConstructorDeclRef)
302+
NON_LVALUE_EXPR(Collection)
303+
NON_LVALUE_EXPR(CaptureList)
304+
NON_LVALUE_EXPR(AbstractClosure)
305+
NON_LVALUE_EXPR(InOut)
306+
NON_LVALUE_EXPR(DynamicType)
307+
NON_LVALUE_EXPR(RebindSelfInConstructor)
308+
NON_LVALUE_EXPR(Apply)
309+
NON_LVALUE_EXPR(ImplicitConversion)
310+
NON_LVALUE_EXPR(ExplicitCast)
311+
NON_LVALUE_EXPR(OptionalEvaluation)
312+
NON_LVALUE_EXPR(If)
313+
NON_LVALUE_EXPR(Assign)
314+
NON_LVALUE_EXPR(DefaultValue)
315+
NON_LVALUE_EXPR(CodeCompletion)
316+
317+
#define UNCHECKED_EXPR(KIND, BASE) \
318+
NON_LVALUE_EXPR(KIND)
319+
#include "swift/AST/ExprNodes.def"
320+
321+
#undef PHYSICAL_LVALUE_EXPR
322+
#undef LEAF_LVALUE_EXPR
323+
#undef NON_LVALUE_EXPR
324+
};
325+
326+
PropagateAccessKind(allowOverwrite).visit(this, accessKind);
327+
}
185328

186329
/// Enumerate each immediate child expression of this node, invoking the
187330
/// specific functor on it. This ignores statements and other non-expression

lib/AST/Type.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,18 @@ bool TypeBase::isVoid() {
539539
return isEqual(getASTContext().TheEmptyTupleType);
540540
}
541541

542+
bool TypeBase::isAssignableType() {
543+
if (isLValueType()) return true;
544+
if (auto tuple = getAs<TupleType>()) {
545+
for (auto eltType : tuple->getElementTypes()) {
546+
if (!eltType->isAssignableType())
547+
return false;
548+
}
549+
return true;
550+
}
551+
return false;
552+
}
553+
542554
namespace {
543555
class GetRValueTypeVisitor : public TypeVisitor<GetRValueTypeVisitor, Type> {
544556
public:

lib/AST/Verifier.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,30 @@ struct ASTNodeBase {};
384384
/// @{
385385
/// These verification functions are run on type checked ASTs if there were
386386
/// no errors.
387-
void verifyChecked(Expr *E) { }
387+
void verifyChecked(Expr *E) {
388+
// Some imported expressions don't have types, even in checked mode.
389+
// TODO: eliminate all these
390+
if (!E->getType()) {
391+
// The raw value of an imported EnumElementDecl doesn't seem to have
392+
// a type for some reason.
393+
if (!isa<IntegerLiteralExpr>(E)) {
394+
Out << "expression has no type\n";
395+
E->print(Out);
396+
abort();
397+
}
398+
return;
399+
}
400+
401+
// Require an access kind to be set on every l-value expression.
402+
// Note that the empty tuple type is assignable but usually isn't
403+
// an l-value, so we have to be conservative there.
404+
if (E->getType()->isLValueType() != E->hasLValueAccessKind() &&
405+
!(E->hasLValueAccessKind() && E->getType()->isAssignableType())) {
406+
Out << "l-value expression does not have l-value access kind set\n";
407+
E->print(Out);
408+
abort();
409+
}
410+
}
388411
void verifyChecked(Stmt *S) {}
389412
void verifyChecked(Pattern *P) { }
390413
void verifyChecked(Decl *D) {}
@@ -1369,6 +1392,7 @@ struct ASTNodeBase {};
13691392
Out << "Unexpected types in IdentityExpr\n";
13701393
abort();
13711394
}
1395+
checkSameLValueAccessKind(E, E->getSubExpr(), "IdentityExpr");
13721396

13731397
verifyCheckedBase(E);
13741398
}
@@ -2383,6 +2407,15 @@ struct ASTNodeBase {};
23832407
abort();
23842408
}
23852409

2410+
void checkSameLValueAccessKind(Expr *LHS, Expr *RHS, const char *what) {
2411+
if (LHS->hasLValueAccessKind() != RHS->hasLValueAccessKind() ||
2412+
(LHS->hasLValueAccessKind() &&
2413+
LHS->getLValueAccessKind() != RHS->getLValueAccessKind())) {
2414+
Out << what << " has a mismatched l-value access kind\n";
2415+
abort();
2416+
}
2417+
}
2418+
23862419
// Verification utilities.
23872420
Type checkMetatypeType(Type type, const char *what) {
23882421
auto metatype = type->getAs<AnyMetatypeType>();

0 commit comments

Comments
 (0)