From 2a187f2606b51a30ef8d627fac550730e9eadc87 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 25 Jan 2023 12:40:29 -0800 Subject: [PATCH] SILGen: Support mutable `consuming` parameters. Emit a box like we would for a local variable, and move the parameter value into the box, as part of the prolog. --- lib/AST/Decl.cpp | 4 ++-- lib/SILGen/SILGenProlog.cpp | 20 +++++++++--------- lib/Sema/TypeCheckStorage.cpp | 6 +++--- test/SILGen/consuming_parameter.swift | 29 +++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 test/SILGen/consuming_parameter.swift diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 603442e0b47..00be89b7f5e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6387,9 +6387,9 @@ Type VarDecl::getType() const { /// is a let member in an initializer. bool VarDecl::isSettable(const DeclContext *UseDC, const DeclRefExpr *base) const { - // Only inout parameters are settable. + // Parameters are settable or not depending on their ownership convention. if (auto *PD = dyn_cast(this)) - return PD->isInOut(); + return !PD->isImmutableInFunctionBody(); // If this is a 'var' decl, then we're settable if we have storage or a // setter. diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 3245a5887dd..4221fdaad93 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -429,18 +429,20 @@ struct ArgumentInitHelper { ManagedValue argrv = makeArgument(ty, pd->isInOut(), isNoImplicitCopy, lifetimeAnnotation, parent, loc); + SILValue value = argrv.getValue(); if (pd->isInOut()) { assert(argrv.getType().isAddress() && "expected inout to be address"); - } else { - #warning "todo" - assert(pd->isImmutableInFunctionBody() - && "consuming mutable params not implemented yet"); - // If the variable is immutable, we can bind the value as is. - // Leave the cleanup on the argument, if any, in place to consume the - // argument if we're responsible for it. + } else if (!pd->isImmutableInFunctionBody()) { + // If it's a locally mutable parameter, then we need to move the argument + // value into a local box to hold the mutated value. + auto mutableBox = SGF.emitLocalVariableWithCleanup(pd, + MarkUninitializedInst::Var); + argrv.ensurePlusOne(SGF, loc).forwardInto(SGF, loc, mutableBox.get()); + return; } - SILValue value = argrv.getValue(); - #warning "todo" + // If the variable is immutable, we can bind the value as is. + // Leave the cleanup on the argument, if any, in place to consume the + // argument if we're responsible for it. SILDebugVariable varinfo(pd->isImmutableInFunctionBody(), ArgNo); if (!argrv.getType().isAddress()) { // NOTE: We setup SGF.VarLocs[pd] in updateArgumentValueForBinding. diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 8a593b36615..4b8f8f2b54b 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3358,9 +3358,9 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, AbstractStorageDecl *storage) const { if (auto *param = dyn_cast(storage)) { return StorageImplInfo::getSimpleStored( - param->isInOut() - ? StorageIsMutable - : StorageIsNotMutable); + param->isImmutableInFunctionBody() + ? StorageIsNotMutable + : StorageIsMutable); } if (auto *var = dyn_cast(storage)) { diff --git a/test/SILGen/consuming_parameter.swift b/test/SILGen/consuming_parameter.swift new file mode 100644 index 00000000000..441ca23f71a --- /dev/null +++ b/test/SILGen/consuming_parameter.swift @@ -0,0 +1,29 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +func bar(_: String) {} + +// CHECK-LABEL: sil {{.*}} @${{.*}}3foo +func foo(y: consuming String, z: String) -> () -> String { + // CHECK: bb0(%0 : @owned $String, %1 : @guaranteed $String): + // CHECK: [[BOX:%.*]] = alloc_box ${ var String } + // CHECK: [[BOX0:%.*]] = mark_uninitialized [var] [[BOX]] + // CHECK: [[BOX1:%.*]] = begin_borrow [lexical] [[BOX0]] + // CHECK: [[Y:%.*]] = project_box [[BOX1]] + // CHECK: store %0 to [init] [[Y]] + + // CHECK: [[YCAPTURE:%.*]] = copy_value [[BOX1]] + // CHECK: partial_apply {{.*}} {{%.*}}([[YCAPTURE]]) + let r = { y } + + // CHECK: [[ZCOPY:%.*]] = copy_value %1 + // CHECK: [[YACCESS:%.*]] = begin_access [modify] [unknown] [[Y]] + // CHECK: assign [[ZCOPY]] to [[YACCESS]] + y = z + + // CHECK: [[YACCESS:%.*]] = begin_access [read] [unknown] [[Y]] + // CHECK: [[YVAL:%.*]] = load [copy] [[YACCESS]] + // CHECK: apply {{%.*}}([[YVAL]] + bar(y) + + return r +}