mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user