Skip to content

Commit 361562a

Browse files
committed
[consume-operator] Emit a better error message when failing to consume globals or escaping captures.
Specifically, we previously emitted a "compiler doesn't understand error", so we were always emitting an error appropriately. This just gives a better error message saying instead that the compiler did understand what happened and that one cannot apply consume to globals or escaping captures. #67755 rdar://112561671
1 parent 9f66d03 commit 361562a

6 files changed

+148
-15
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,9 @@ ERROR(sil_movechecking_bug_exclusivity_violation, none,
840840
ERROR(sil_movekillscopyablevalue_move_applied_to_unsupported_move, none,
841841
"'consume' applied to value that the compiler does not support. This is a compiler bug. Please file a bug with a small example of the bug",
842842
())
843+
ERROR(sil_movekillscopyable_move_applied_to_nonlocal_memory, none,
844+
"'consume' cannot be applied to %select{globals|escaping captures}0",
845+
(unsigned))
843846

844847

845848
// Implicit inout-to-UnsafeRawPointer conversions

lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableAddressesChecker.cpp

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,6 +2387,70 @@ bool ConsumeOperatorCopyableAddressesChecker::check(SILValue address) {
23872387
return result;
23882388
}
23892389

2390+
//===----------------------------------------------------------------------===//
2391+
// MARK: Unsupported Use Case Errors
2392+
//===----------------------------------------------------------------------===//
2393+
2394+
namespace {
2395+
2396+
struct UnsupportedUseCaseDiagnosticEmitter {
2397+
MarkUnresolvedMoveAddrInst *mai;
2398+
2399+
~UnsupportedUseCaseDiagnosticEmitter() {
2400+
assert(!mai && "Didn't call cleanup!\n");
2401+
}
2402+
2403+
bool cleanup() && {
2404+
// Now that we have emitted the error, replace the move_addr with a
2405+
// copy_addr so that future passes never see it. We mark it as a
2406+
// copy_addr [init].
2407+
SILBuilderWithScope builder(mai);
2408+
builder.createCopyAddr(mai->getLoc(), mai->getSrc(), mai->getDest(),
2409+
IsNotTake, IsInitialization);
2410+
mai->eraseFromParent();
2411+
mai = nullptr;
2412+
return true;
2413+
}
2414+
2415+
ASTContext &getASTContext() const { return mai->getModule().getASTContext(); }
2416+
2417+
void emitUnsupportedUseCaseError() const {
2418+
auto diag =
2419+
diag::sil_movekillscopyablevalue_move_applied_to_unsupported_move;
2420+
diagnose(getASTContext(), mai->getLoc().getSourceLoc(), diag);
2421+
}
2422+
2423+
/// Try to pattern match if we were trying to move a global. In such a case,
2424+
/// emit a better error.
2425+
bool tryEmitCannotConsumeNonLocalMemoryError() const {
2426+
auto src = stripAccessMarkers(mai->getSrc());
2427+
2428+
if (auto *gai = dyn_cast<GlobalAddrInst>(src)) {
2429+
auto diag = diag::sil_movekillscopyable_move_applied_to_nonlocal_memory;
2430+
diagnose(getASTContext(), mai->getLoc().getSourceLoc(), diag, 0);
2431+
return true;
2432+
}
2433+
2434+
// If we have a project_box, then we must have an escaping capture. It is
2435+
// the only case that allocbox to stack doesn't handle today.
2436+
if (isa<ProjectBoxInst>(src)) {
2437+
auto diag = diag::sil_movekillscopyable_move_applied_to_nonlocal_memory;
2438+
diagnose(getASTContext(), mai->getLoc().getSourceLoc(), diag, 1);
2439+
return true;
2440+
}
2441+
2442+
return false;
2443+
}
2444+
2445+
void emit() const {
2446+
if (tryEmitCannotConsumeNonLocalMemoryError())
2447+
return;
2448+
emitUnsupportedUseCaseError();
2449+
}
2450+
};
2451+
2452+
} // namespace
2453+
23902454
//===----------------------------------------------------------------------===//
23912455
// Top Level Entrypoint
23922456
//===----------------------------------------------------------------------===//
@@ -2490,17 +2554,9 @@ class ConsumeOperatorCopyableAddressesCheckerPass
24902554
++ii;
24912555

24922556
if (auto *mai = dyn_cast<MarkUnresolvedMoveAddrInst>(inst)) {
2493-
auto diag =
2494-
diag::sil_movekillscopyablevalue_move_applied_to_unsupported_move;
2495-
diagnose(astContext, mai->getLoc().getSourceLoc(), diag);
2496-
2497-
// Now that we have emitted the error, replace the move_addr with a
2498-
// copy_addr so that future passes never see it. We mark it as a
2499-
// copy_addr [init].
2500-
SILBuilderWithScope builder(mai);
2501-
builder.createCopyAddr(mai->getLoc(), mai->getSrc(), mai->getDest(),
2502-
IsNotTake, IsInitialization);
2503-
mai->eraseFromParent();
2557+
UnsupportedUseCaseDiagnosticEmitter emitter{mai};
2558+
emitter.emit();
2559+
std::move(emitter).cleanup();
25042560
lateMadeChange = true;
25052561
}
25062562
}

lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableValuesChecker.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ bool ConsumeOperatorCopyableValuesChecker::check() {
507507
}
508508

509509
//===----------------------------------------------------------------------===//
510-
// Unsupported Use Case Errors
510+
// MARK: Unsupported Use Case Errors
511511
//===----------------------------------------------------------------------===//
512512

513513
static void emitUnsupportedUseCaseError(MoveValueInst *mvi) {
@@ -517,6 +517,25 @@ static void emitUnsupportedUseCaseError(MoveValueInst *mvi) {
517517
mvi->setAllowsDiagnostics(false);
518518
}
519519

520+
/// Try to pattern match if we were trying to move a global. In such a case,
521+
/// emit a better error.
522+
static bool tryEmitCannotConsumeNonLocalMemoryError(MoveValueInst *mvi) {
523+
auto *li = dyn_cast<LoadInst>(mvi->getOperand());
524+
if (!li)
525+
return false;
526+
527+
auto &astContext = mvi->getModule().getASTContext();
528+
if (auto *gai =
529+
dyn_cast<GlobalAddrInst>(stripAccessMarkers(li->getOperand()))) {
530+
auto diag = diag::sil_movekillscopyable_move_applied_to_nonlocal_memory;
531+
diagnose(astContext, mvi->getLoc().getSourceLoc(), diag, 0);
532+
mvi->setAllowsDiagnostics(false);
533+
return true;
534+
}
535+
536+
return false;
537+
}
538+
520539
//===----------------------------------------------------------------------===//
521540
// Top Level Entrypoint
522541
//===----------------------------------------------------------------------===//
@@ -579,6 +598,10 @@ class ConsumeOperatorCopyableValuesCheckerPass : public SILFunctionTransform {
579598
continue;
580599
}
581600

601+
// Try to emit a better error if we try to consume a global.
602+
if (tryEmitCannotConsumeNonLocalMemoryError(mvi))
603+
continue;
604+
582605
if (!DisableUnhandledMoveDiagnostic)
583606
emitUnsupportedUseCaseError(mvi);
584607
}

test/SILOptimizer/consume_operator_kills_copyable_addressonly_vars.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ public func nonEscapingpartialApplyTest<T : P>(_ x: __owned T) {
588588
public func partialApplyTest<T : P>(_ x: __owned T) -> () -> () {
589589
var x2 = x
590590
x2 = x
591-
let _ = consume x2 // expected-error {{'consume' applied to value that the compiler does not support}}
591+
let _ = consume x2 // expected-error {{'consume' cannot be applied to escaping captures}}
592592
let f = {
593593
print(x2)
594594
}

test/SILOptimizer/consume_operator_kills_copyable_values.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,11 @@ let myLetGlobal = Klass()
260260
var myVarGlobal = Klass()
261261

262262
public func performMoveOnVarGlobalError() {
263-
let _ = consume myVarGlobal // expected-error {{'consume' applied to value that the compiler does not support}}
263+
let _ = consume myVarGlobal // expected-error {{'consume' cannot be applied to globals}}
264264
}
265265

266266
public func performMoveOnLetGlobalError() {
267-
let _ = consume myVarGlobal // expected-error {{'consume' applied to value that the compiler does not support}}
267+
let _ = consume myVarGlobal // expected-error {{'consume' cannot be applied to globals}}
268268
}
269269

270270
public func multipleVarsWithSubsequentBorrows() -> Bool {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %target-swift-frontend -verify %s -emit-sil -o /dev/null
2+
3+
// This file tests that we emit good errors for global lets/vars,
4+
5+
////////////////////////
6+
// MARK: Declarations //
7+
////////////////////////
8+
9+
class Klass {
10+
var s1 = "123"
11+
let s2 = "123"
12+
}
13+
struct LoadableStruct { var k = Klass() }
14+
15+
struct AddressOnlyStruct<T> { var t: T? = nil }
16+
17+
////////////////////////
18+
// MARK: Global Tests //
19+
////////////////////////
20+
21+
let globalLoadableLet = LoadableStruct()
22+
let _ = consume globalLoadableLet // expected-error {{'consume' cannot be applied to globals}}
23+
24+
let globalAddressOnlyLet = AddressOnlyStruct<Any>()
25+
let _ = consume globalAddressOnlyLet // expected-error {{'consume' cannot be applied to globals}}
26+
27+
var globalLoadableVar = LoadableStruct()
28+
let _ = consume globalLoadableVar // expected-error {{'consume' cannot be applied to globals}}
29+
30+
var globalAddressOnlyVar = AddressOnlyStruct<Any>()
31+
let _ = consume globalAddressOnlyVar // expected-error {{'consume' cannot be applied to globals}}
32+
33+
////////////////////////////
34+
// MARK: Mutable Captures //
35+
////////////////////////////
36+
37+
func accessMutableCapture() -> (() -> ()) {
38+
func useKlassInOut(_ x: inout Klass) {}
39+
func useAddressOnlyStructInOut<T>(_ x: inout AddressOnlyStruct<T>) {}
40+
41+
var x = Klass()
42+
var x2 = AddressOnlyStruct<Any>()
43+
var f: () -> () = {}
44+
f = {
45+
useKlassInOut(&x)
46+
useAddressOnlyStructInOut(&x2)
47+
}
48+
let _ = consume x // expected-error {{'consume' cannot be applied to escaping captures}}
49+
let _ = consume x2 // expected-error {{'consume' cannot be applied to escaping captures}}
50+
return f
51+
}

0 commit comments

Comments
 (0)