Skip to content

Commit 97b1920

Browse files
committed
[moveOnly] Emit an error diagnostic if a user applies _move to a value that the compiler doesn't know how to check (non-let, non-param today).
The error diagnostic tells the user that the compiler can't check the value. It then instructs the user to make a feature request and provide the test case if they think it is reasonable. I also provided an option to disable the diagnostic to unblock people. The reason why I think this is the right thing to do is we want people to know that _move means they do not need to worry about the given binding being used later in the program in some way without having to reason. For now I am doing this by banning _move on non-lets, non-params. This is implemented by noting that: 1. _move inserts move_value [allows_diagnostics]. 2. The checker always removes [allows_diagnostics] after checking a _move. Thus we know after we check, any move_value that is still marked with [allows_diagnostic], it was a _move that we never used in any checking. I added some test cases where this known triggers. I am either going to implement some sort of support for performing _move on them or give a more specific diagnostic. This is just an initial incremental step.
1 parent 9512927 commit 97b1920

File tree

4 files changed

+61
-4
lines changed

4 files changed

+61
-4
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ NOTE(sil_movekillscopyablevalue_use_here, none,
740740
"use here", ())
741741
NOTE(sil_movekillscopyablevalue_value_consumed_in_loop, none,
742742
"cyclic move here. move will occur multiple times in the loop", ())
743+
ERROR(sil_movekillscopyablevalue_move_applied_to_unsupported_move, none,
744+
"_move applied to value that the compiler does not know how to check. "
745+
"Please file a bug or an enhancement request!", ())
743746

744747
#define UNDEFINE_DIAGNOSTIC_MACROS
745748
#include "DefineDiagnosticMacros.h"

lib/SILOptimizer/Mandatory/MoveKillsCopyableValuesChecker.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929

3030
using namespace swift;
3131

32+
static llvm::cl::opt<bool>
33+
DisableUnhandledMoveDiagnostic("sil-disable-unknown-move-diagnostic");
34+
3235
//===----------------------------------------------------------------------===//
3336
// Diagnostic Utilities
3437
//===----------------------------------------------------------------------===//
@@ -383,6 +386,12 @@ bool MoveKillsCopyableValuesChecker::check() {
383386
// Only emit diagnostics if our move value allows us to.
384387
if (!mvi->getAllowDiagnostics())
385388
continue;
389+
390+
// Now that we know we may emit diagnostics for this, unset allows
391+
// diagnostics so that we skip these when we search at the end for
392+
// unvisited move_value [allows_diagnostics].
393+
mvi->setAllowsDiagnostics(false);
394+
386395
LLVM_DEBUG(llvm::dbgs() << "Move Value: " << *mvi);
387396
if (livenessInfo.liveness.isWithinBoundary(mvi)) {
388397
LLVM_DEBUG(llvm::dbgs() << " WithinBoundary: Yes!\n");
@@ -412,6 +421,12 @@ namespace {
412421
class MoveKillsCopyableValuesCheckerPass : public SILFunctionTransform {
413422
void run() override {
414423
auto *fn = getFunction();
424+
auto &astContext = fn->getASTContext();
425+
426+
// If we do not have experimental move only enabled, do not emit
427+
// diagnostics.
428+
if (!astContext.LangOpts.EnableExperimentalMoveOnly)
429+
return;
415430

416431
// Don't rerun diagnostics on deserialized functions.
417432
if (getFunction()->wasDeserializedCanonical())
@@ -425,6 +440,26 @@ class MoveKillsCopyableValuesCheckerPass : public SILFunctionTransform {
425440
if (MoveKillsCopyableValuesChecker(getFunction()).check()) {
426441
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
427442
}
443+
444+
// Now search through our function one last time and any move_value
445+
// [allows_diagnostics] that remain are ones that we did not know how to
446+
// check so emit a diagnostic so the user doesn't assume that they have
447+
// guarantees.
448+
//
449+
// TODO: Emit specific diagnostics here (e.x.: _move of global).
450+
if (DisableUnhandledMoveDiagnostic)
451+
return;
452+
for (auto &block : *fn) {
453+
for (auto &inst : block) {
454+
if (auto *mvi = dyn_cast<MoveValueInst>(&inst)) {
455+
if (mvi->getAllowDiagnostics()) {
456+
auto diag = diag::
457+
sil_movekillscopyablevalue_move_applied_to_unsupported_move;
458+
diagnose(astContext, mvi->getLoc().getSourceLoc(), diag);
459+
}
460+
}
461+
}
462+
}
428463
}
429464
};
430465

test/SILOptimizer/move_operator_kills_copyable_values.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,27 @@ public class KlassPair {
229229
var y = Klass()
230230
}
231231

232-
// No error since we are not moving p itself, but are moving a copy of x that we
233-
// loaded from p and then destroying that.
232+
// TODO: Emit a better error here! We should state that we are applying _move to
233+
// a class field and that is illegal.
234234
public func performMoveOnOneEltOfKlassPair(_ p: KlassPair) {
235-
let _ = _move(p.x)
235+
let _ = _move(p.x) // expected-error {{_move applied to value that the compiler does not know how to check. Please file a bug or an enhancement request!}}
236236
nonConsumingUse(p.y)
237237
}
238+
239+
let myLetGlobal = Klass()
240+
var myVarGlobal = Klass()
241+
242+
public func performMoveOnVarGlobalError() {
243+
let _ = _move(myVarGlobal) // expected-error {{_move applied to value that the compiler does not know how to check. Please file a bug or an enhancement request!}}
244+
}
245+
246+
// TODO: Support vars
247+
public func performMoveOnVarError() {
248+
var k = myVarGlobal
249+
let _ = _move(k) // expected-error {{_move applied to value that the compiler does not know how to check. Please file a bug or an enhancement request!}}
250+
k = myVarGlobal
251+
}
252+
253+
public func performMoveOnLetGlobalError() {
254+
let _ = _move(myVarGlobal) // expected-error {{_move applied to value that the compiler does not know how to check. Please file a bug or an enhancement request!}}
255+
}

test/stdlib/LifetimeManagement.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ suite.test("copy") {
1515

1616
suite.test("move") {
1717
let k = Klass()
18-
expectTrue(k === _move(k))
18+
let k2 = k
19+
expectTrue(k2 === _move(k))
1920
}
2021

2122
runAllTests()

0 commit comments

Comments
 (0)