Skip to content

Commit 4b25945

Browse files
committed
SILGen: Fix overlapping LoadExprs
When lowering a LoadExpr, SILGen constructs an LValue and loads from it to produce an RValue. If a LoadExpr contains another LoadExpr, the innermost LoadExpr builds its own LValue, which is then loaded to an RValue, and turned back into an LValue by creating a single ValueComponent. When evaluating an OpenExistentialExpr inside an LValue, we record the base expression and evaluate it as an LValue later when we encounter the corresponding OpaqueValueExpr. The problem is when this is combined with a nested LoadExpr, we might be inside of a different LValue than the original LValue that contained the OpenExistentialExpr. This would trigger an assertion, because the mapping from OpaqueValueExprs to their base expressions was per-LValue; instead, it needs to be per-SILGenFunction.
1 parent 1d317b2 commit 4b25945

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

lib/SILGen/SILGenFunction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1416,7 +1416,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
14161416

14171417
/// Mapping from active opaque value expressions to their values,
14181418
/// along with a bit for each indicating whether it has been consumed yet.
1419-
llvm::DenseMap<OpaqueValueExpr *, OpaqueValueState> OpaqueValues;
1419+
llvm::SmallDenseMap<OpaqueValueExpr *, OpaqueValueState>
1420+
OpaqueValues;
1421+
1422+
/// A mapping from opaque value expressions to the open-existential
1423+
/// expression that determines them, used while lowering lvalues.
1424+
llvm::SmallDenseMap<OpaqueValueExpr *, OpenExistentialExpr *>
1425+
OpaqueValueExprs;
14201426

14211427
/// RAII object that introduces a temporary binding for an opaque value.
14221428
///

lib/SILGen/SILGenLValue.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,6 @@ SILGenFunction::getUnknownEnforcement(VarDecl *var) {
167167
class LLVM_LIBRARY_VISIBILITY SILGenLValue
168168
: public Lowering::ExprVisitor<SILGenLValue, LValue, AccessKind>
169169
{
170-
/// A mapping from opaque value expressions to the open-existential
171-
/// expression that determines them.
172-
llvm::SmallDenseMap<OpaqueValueExpr *, OpenExistentialExpr *>
173-
openedExistentials;
174170

175171
public:
176172
SILGenFunction &SGF;
@@ -1909,11 +1905,11 @@ LValue SILGenLValue::visitDeclRefExpr(DeclRefExpr *e, AccessKind accessKind) {
19091905
LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e,
19101906
AccessKind accessKind) {
19111907
// Handle an opaque lvalue that refers to an opened existential.
1912-
auto known = openedExistentials.find(e);
1913-
if (known != openedExistentials.end()) {
1908+
auto known = SGF.OpaqueValueExprs.find(e);
1909+
if (known != SGF.OpaqueValueExprs.end()) {
19141910
// Dig the open-existential expression out of the list.
19151911
OpenExistentialExpr *opened = known->second;
1916-
openedExistentials.erase(known);
1912+
SGF.OpaqueValueExprs.erase(known);
19171913

19181914
// Do formal evaluation of the underlying existential lvalue.
19191915
LValue lv = visitRec(opened->getExistentialValue(), accessKind);
@@ -2206,15 +2202,15 @@ LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e,
22062202

22072203
// Record the fact that we're opening this existential. The actual
22082204
// opening operation will occur when we see the OpaqueValueExpr.
2209-
bool inserted = openedExistentials.insert({e->getOpaqueValue(), e}).second;
2205+
bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second;
22102206
(void)inserted;
22112207
assert(inserted && "already have this opened existential?");
22122208

22132209
// Visit the subexpression.
22142210
LValue lv = visitRec(e->getSubExpr(), accessKind);
22152211

22162212
// Sanity check that we did see the OpaqueValueExpr.
2217-
assert(openedExistentials.count(e->getOpaqueValue()) == 0 &&
2213+
assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 &&
22182214
"opened existential not removed?");
22192215
return lv;
22202216
}

test/SILGen/properties.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,3 +1111,48 @@ public class DerivedClassWithPublicProperty : BaseClassWithInternalProperty {
11111111
// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]([[SUPER]]) : $@convention(method) (@guaranteed BaseClassWithInternalProperty) -> ()
11121112
// CHECK-NEXT: destroy_value [[SUPER]] : $BaseClassWithInternalProperty
11131113
// CHECK: } // end sil function '_T010properties30DerivedClassWithPublicPropertyC1xytfg'
1114+
1115+
// Make sure that we can handle this AST:
1116+
// (load_expr
1117+
// (open_existential_expr
1118+
// (opaque_expr A)
1119+
// ...
1120+
// (load_expr
1121+
// (opaque_expr ))))
1122+
1123+
class ReferenceType {
1124+
var p: NonmutatingProtocol
1125+
init(p: NonmutatingProtocol) { self.p = p }
1126+
}
1127+
1128+
protocol NonmutatingProtocol {
1129+
var x: Int { get nonmutating set }
1130+
}
1131+
1132+
// sil hidden @_T010properties19overlappingLoadExpryAA13ReferenceTypeCz1c_tF : $@convention(thin) (@inout ReferenceType) -> () {
1133+
// CHECK: [[RESULT:%.*]] = alloc_stack $Int
1134+
// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [var] [[RESULT]] : $*Int
1135+
// CHECK-NEXT: [[C_INOUT:%.*]] = begin_access [read] [unknown] %0 : $*ReferenceType
1136+
// CHECK-NEXT: [[C:%.*]] = load [copy] [[C_INOUT:%.*]] : $*ReferenceType
1137+
// CHECK-NEXT: end_access [[C_INOUT]] : $*ReferenceType
1138+
// CHECK-NEXT: [[C_FIELD_BOX:%.*]] = alloc_stack $NonmutatingProtocol
1139+
// CHECK-NEXT: [[GETTER:%.*]] = class_method [[C]] : $ReferenceType, #ReferenceType.p!getter.1 : (ReferenceType) -> () -> NonmutatingProtocol, $@convention(method) (@guaranteed ReferenceType) -> @out NonmutatingProtocol
1140+
// CHECK-NEXT: apply [[GETTER]]([[C_FIELD_BOX]], [[C]]) : $@convention(method) (@guaranteed ReferenceType) -> @out NonmutatingProtocol
1141+
// CHECK-NEXT: destroy_value [[C]] : $ReferenceType
1142+
// CHECK-NEXT: [[C_FIELD_PAYLOAD:%.*]] = open_existential_addr immutable_access [[C_FIELD_BOX]] : $*NonmutatingProtocol to $*@opened("{{.*}}") NonmutatingProtocol
1143+
// CHECK-NEXT: [[C_FIELD_COPY:%.*]] = alloc_stack $@opened("{{.*}}") NonmutatingProtocol
1144+
// CHECK-NEXT: copy_addr [[C_FIELD_PAYLOAD]] to [initialization] [[C_FIELD_COPY]] : $*@opened("{{.*}}") NonmutatingProtocol
1145+
// CHECK-NEXT: destroy_addr [[C_FIELD_BOX]] : $*NonmutatingProtocol
1146+
// CHECK-NEXT: [[GETTER:%.*]] = witness_method $@opened("{{.*}}") NonmutatingProtocol, #NonmutatingProtocol.x!getter.1 : <Self where Self : NonmutatingProtocol> (Self) -> () -> Int, %11 : $*@opened("{{.*}}") NonmutatingProtocol : $@convention(witness_method) <τ_0_0 where τ_0_0 : NonmutatingProtocol> (@in_guaranteed τ_0_0) -> Int
1147+
// CHECK-NEXT: [[RESULT_VALUE:%.*]] = apply [[GETTER]]<@opened("{{.*}}") NonmutatingProtocol>([[C_FIELD_COPY]]) : $@convention(witness_method) <τ_0_0 where τ_0_0 : NonmutatingProtocol> (@in_guaranteed τ_0_0) -> Int
1148+
// CHECK-NEXT: destroy_addr [[C_FIELD_COPY]] : $*@opened("{{.*}}") NonmutatingProtocol
1149+
// CHECK-NEXT: assign [[RESULT_VALUE]] to [[UNINIT]] : $*Int
1150+
// CHECK-NEXT: dealloc_stack [[C_FIELD_COPY]] : $*@opened("{{.*}}") NonmutatingProtocol
1151+
// CHECK-NEXT: dealloc_stack [[C_FIELD_BOX]] : $*NonmutatingProtocol
1152+
// CHECK-NEXT: dealloc_stack [[RESULT]] : $*Int
1153+
// CHECK-NEXT: tuple ()
1154+
// CHECK-NEXT: return
1155+
1156+
func overlappingLoadExpr(c: inout ReferenceType) {
1157+
_ = c.p.x
1158+
}

0 commit comments

Comments
 (0)