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.
This commit is contained in:
Slava Pestov
2017-05-12 22:02:50 -07:00
parent 1d317b2d91
commit 4b25945257
3 changed files with 57 additions and 10 deletions

View File

@@ -1416,7 +1416,13 @@ public:
/// Mapping from active opaque value expressions to their values,
/// along with a bit for each indicating whether it has been consumed yet.
llvm::DenseMap<OpaqueValueExpr *, OpaqueValueState> OpaqueValues;
llvm::SmallDenseMap<OpaqueValueExpr *, OpaqueValueState>
OpaqueValues;
/// A mapping from opaque value expressions to the open-existential
/// expression that determines them, used while lowering lvalues.
llvm::SmallDenseMap<OpaqueValueExpr *, OpenExistentialExpr *>
OpaqueValueExprs;
/// RAII object that introduces a temporary binding for an opaque value.
///

View File

@@ -167,10 +167,6 @@ SILGenFunction::getUnknownEnforcement(VarDecl *var) {
class LLVM_LIBRARY_VISIBILITY SILGenLValue
: public Lowering::ExprVisitor<SILGenLValue, LValue, AccessKind>
{
/// A mapping from opaque value expressions to the open-existential
/// expression that determines them.
llvm::SmallDenseMap<OpaqueValueExpr *, OpenExistentialExpr *>
openedExistentials;
public:
SILGenFunction &SGF;
@@ -1909,11 +1905,11 @@ LValue SILGenLValue::visitDeclRefExpr(DeclRefExpr *e, AccessKind accessKind) {
LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e,
AccessKind accessKind) {
// Handle an opaque lvalue that refers to an opened existential.
auto known = openedExistentials.find(e);
if (known != openedExistentials.end()) {
auto known = SGF.OpaqueValueExprs.find(e);
if (known != SGF.OpaqueValueExprs.end()) {
// Dig the open-existential expression out of the list.
OpenExistentialExpr *opened = known->second;
openedExistentials.erase(known);
SGF.OpaqueValueExprs.erase(known);
// Do formal evaluation of the underlying existential lvalue.
LValue lv = visitRec(opened->getExistentialValue(), accessKind);
@@ -2206,7 +2202,7 @@ LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e,
// Record the fact that we're opening this existential. The actual
// opening operation will occur when we see the OpaqueValueExpr.
bool inserted = openedExistentials.insert({e->getOpaqueValue(), e}).second;
bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second;
(void)inserted;
assert(inserted && "already have this opened existential?");
@@ -2214,7 +2210,7 @@ LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e,
LValue lv = visitRec(e->getSubExpr(), accessKind);
// Sanity check that we did see the OpaqueValueExpr.
assert(openedExistentials.count(e->getOpaqueValue()) == 0 &&
assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 &&
"opened existential not removed?");
return lv;
}

View File

@@ -1111,3 +1111,48 @@ public class DerivedClassWithPublicProperty : BaseClassWithInternalProperty {
// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]([[SUPER]]) : $@convention(method) (@guaranteed BaseClassWithInternalProperty) -> ()
// CHECK-NEXT: destroy_value [[SUPER]] : $BaseClassWithInternalProperty
// CHECK: } // end sil function '_T010properties30DerivedClassWithPublicPropertyC1xytfg'
// Make sure that we can handle this AST:
// (load_expr
// (open_existential_expr
// (opaque_expr A)
// ...
// (load_expr
// (opaque_expr ))))
class ReferenceType {
var p: NonmutatingProtocol
init(p: NonmutatingProtocol) { self.p = p }
}
protocol NonmutatingProtocol {
var x: Int { get nonmutating set }
}
// sil hidden @_T010properties19overlappingLoadExpryAA13ReferenceTypeCz1c_tF : $@convention(thin) (@inout ReferenceType) -> () {
// CHECK: [[RESULT:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[UNINIT:%.*]] = mark_uninitialized [var] [[RESULT]] : $*Int
// CHECK-NEXT: [[C_INOUT:%.*]] = begin_access [read] [unknown] %0 : $*ReferenceType
// CHECK-NEXT: [[C:%.*]] = load [copy] [[C_INOUT:%.*]] : $*ReferenceType
// CHECK-NEXT: end_access [[C_INOUT]] : $*ReferenceType
// CHECK-NEXT: [[C_FIELD_BOX:%.*]] = alloc_stack $NonmutatingProtocol
// CHECK-NEXT: [[GETTER:%.*]] = class_method [[C]] : $ReferenceType, #ReferenceType.p!getter.1 : (ReferenceType) -> () -> NonmutatingProtocol, $@convention(method) (@guaranteed ReferenceType) -> @out NonmutatingProtocol
// CHECK-NEXT: apply [[GETTER]]([[C_FIELD_BOX]], [[C]]) : $@convention(method) (@guaranteed ReferenceType) -> @out NonmutatingProtocol
// CHECK-NEXT: destroy_value [[C]] : $ReferenceType
// CHECK-NEXT: [[C_FIELD_PAYLOAD:%.*]] = open_existential_addr immutable_access [[C_FIELD_BOX]] : $*NonmutatingProtocol to $*@opened("{{.*}}") NonmutatingProtocol
// CHECK-NEXT: [[C_FIELD_COPY:%.*]] = alloc_stack $@opened("{{.*}}") NonmutatingProtocol
// CHECK-NEXT: copy_addr [[C_FIELD_PAYLOAD]] to [initialization] [[C_FIELD_COPY]] : $*@opened("{{.*}}") NonmutatingProtocol
// CHECK-NEXT: destroy_addr [[C_FIELD_BOX]] : $*NonmutatingProtocol
// 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
// 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
// CHECK-NEXT: destroy_addr [[C_FIELD_COPY]] : $*@opened("{{.*}}") NonmutatingProtocol
// CHECK-NEXT: assign [[RESULT_VALUE]] to [[UNINIT]] : $*Int
// CHECK-NEXT: dealloc_stack [[C_FIELD_COPY]] : $*@opened("{{.*}}") NonmutatingProtocol
// CHECK-NEXT: dealloc_stack [[C_FIELD_BOX]] : $*NonmutatingProtocol
// CHECK-NEXT: dealloc_stack [[RESULT]] : $*Int
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
func overlappingLoadExpr(c: inout ReferenceType) {
_ = c.p.x
}