Skip to content

Commit e04b35d

Browse files
committed
Consume: warn about no-op consumes to be fixed
As of now, SE-366 is not correctly implemented with respect to concrete, bitwise-copyable types like `Int`. Writing `consume someInt` doesn't actually consume the variable binding as it should, meaning code that should be flagged as having a use-after-consume is being silently permitted: ```swift let someInt = 10 let y = consume someInt print(someInt) // no error! ``` This has been a problem since Swift 5.9. Eventually we plan to fix this issue, which means code previously doing the above would become an error. To help people get ready for the fix, start warning people that these consumes are actually no-ops and suggest removing them until the intended behavior is actually enforced in the future. resolves rdar://127081103
1 parent 5230b19 commit e04b35d

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7749,6 +7749,8 @@ ERROR(consume_expression_needed_for_cast,none,
77497749
"implicit conversion to %0 is consuming", (Type))
77507750
NOTE(add_consume_to_silence,none,
77517751
"add 'consume' to make consumption explicit", ())
7752+
WARNING(consume_of_bitwisecopyable_noop,none,
7753+
"'consume' applied to bitwise-copyable type %0 has no effect", (Type))
77527754
ERROR(consume_expression_not_passed_lvalue,none,
77537755
"'consume' can only be applied to a local binding ('let', 'var', or parameter)", ())
77547756
ERROR(consume_expression_partial_copyable,none,

lib/Sema/MiscDiagnostics.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,56 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
437437
consumeExpr->getSubExpr());
438438
for (auto &diag : diags)
439439
diag.emit(Ctx);
440+
441+
// As of now, SE-366 is not correctly implemented (rdar://102780553),
442+
// so warn about certain consume's being no-ops today that will no longer
443+
// be a no-op in the future once we fix this.
444+
if (auto ty = consumeExpr->getType()) {
445+
bool shouldWarn = true;
446+
447+
// Look through any load.
448+
auto *expr = consumeExpr->getSubExpr();
449+
if (auto *load = dyn_cast<LoadExpr>(expr))
450+
expr = load->getSubExpr();
451+
452+
// Don't warn if explicit ownership was provided on a parameter.
453+
// Those seem to be checked just fine in SIL.
454+
if (auto *declRef = dyn_cast<DeclRefExpr>(expr)) {
455+
if (auto *decl = declRef->getDecl()) {
456+
if (auto *paramDecl = dyn_cast<ParamDecl>(decl)) {
457+
switch (paramDecl->getSpecifier()) {
458+
case ParamSpecifier::InOut:
459+
case ParamSpecifier::Borrowing:
460+
case ParamSpecifier::Consuming:
461+
case ParamSpecifier::ImplicitlyCopyableConsuming:
462+
shouldWarn = false;
463+
break;
464+
case ParamSpecifier::Default:
465+
case ParamSpecifier::LegacyShared:
466+
case ParamSpecifier::LegacyOwned:
467+
break; // warn
468+
}
469+
}
470+
}
471+
}
472+
473+
// Only warn about obviously concrete BitwiseCopyable types, since we
474+
// know those won't get checked for consumption.
475+
if (diags.empty() &&
476+
shouldWarn &&
477+
!ty->hasError() &&
478+
!ty->hasTypeParameter() &&
479+
!ty->hasUnboundGenericType() &&
480+
!ty->hasArchetype()) {
481+
auto bitCopy = Ctx.getProtocol(KnownProtocolKind::BitwiseCopyable);
482+
if (DC->getParentModule()->checkConformance(ty, bitCopy)) {
483+
Ctx.Diags.diagnose(consumeExpr->getLoc(),
484+
diag::consume_of_bitwisecopyable_noop, ty)
485+
.fixItRemoveChars(consumeExpr->getStartLoc(),
486+
consumeExpr->getSubExpr()->getStartLoc());
487+
}
488+
}
489+
}
440490
}
441491

442492
void checkCopyExpr(CopyExpr *copyExpr) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-swift-emit-sil %s -verify -sil-verify-all
2+
3+
struct Point {
4+
let x: Float
5+
let y: Float
6+
}
7+
8+
struct ConditionallyBC<T> {
9+
var t: T
10+
}
11+
extension ConditionallyBC: BitwiseCopyable where T: BitwiseCopyable {}
12+
13+
func test<T, BCG: BitwiseCopyable>(_ t: T, // expected-error {{'t' is borrowed and cannot be consumed}}
14+
_ bcg: BCG, // expected-error {{'bcg' is borrowed and cannot be consumed}}
15+
_ cbcg_generic: ConditionallyBC<BCG>, // expected-error {{'cbcg_generic' is borrowed and cannot be consumed}}
16+
_ maybeBCG: BCG?, // expected-error {{'maybeBCG' is borrowed and cannot be consumed}}
17+
_ maybeT: T?, // expected-error {{'maybeT' is borrowed and cannot be consumed}}
18+
_ anyBC: any BitwiseCopyable, // expected-error {{'anyBC' is borrowed and cannot be consumed}}
19+
_ x: Int,
20+
_ point: Point,
21+
_ cbcg_concrete: ConditionallyBC<Int>,
22+
_ maybeFloat: Float?) {
23+
_ = consume t // expected-note {{consumed here}}
24+
_ = consume bcg // expected-note {{consumed here}}
25+
_ = consume cbcg_generic // expected-note {{consumed here}}
26+
_ = consume maybeBCG // expected-note {{consumed here}}
27+
_ = consume maybeT // expected-note {{consumed here}}
28+
_ = consume anyBC // expected-note {{consumed here}}
29+
30+
_ = consume x // expected-warning {{'consume' applied to bitwise-copyable type 'Int' has no effect}}{{7-15=}}
31+
_ = consume point // expected-warning {{'consume' applied to bitwise-copyable type 'Point' has no effect}}{{7-15=}}
32+
_ = consume cbcg_concrete // expected-warning {{'consume' applied to bitwise-copyable type 'ConditionallyBC<Int>' has no effect}}{{7-16=}}
33+
_ = consume maybeFloat // expected-warning {{'consume' applied to bitwise-copyable type 'Float?' has no effect}}{{8-16=}}
34+
}
35+
36+
func proofOfUseAfterConsume() -> Int {
37+
let someInt = 10
38+
let y = consume someInt // expected-warning {{'consume' applied to bitwise-copyable type 'Int' has no effect}}
39+
print(y)
40+
return someInt // undiagnosed use-after-consume
41+
}
42+
43+
func moreProofs(_ share: __shared Int,
44+
_ own: __owned Int,
45+
_ snd: sending Int, // expected-error {{'snd' used after consume}}
46+
_ ino: inout Int, // expected-error {{'ino' used after consume}}
47+
_ brw: borrowing Int, // expected-error {{'brw' is borrowed and cannot be consumed}}
48+
_ csm: consuming Int // expected-error {{'csm' consumed more than once}}
49+
) {
50+
_ = consume share // expected-warning {{'consume' applied to bitwise-copyable type 'Int' has no effect}}
51+
_ = consume own // expected-warning {{'consume' applied to bitwise-copyable type 'Int' has no effect}}
52+
let _ = (share, own)
53+
54+
_ = consume ino // expected-note {{consumed}}
55+
_ = consume brw // expected-note {{consumed}}
56+
_ = consume csm // expected-note {{consumed}}
57+
_ = consume csm // expected-note {{consumed}}
58+
_ = consume snd // expected-note {{consumed}}
59+
_ = snd // expected-note {{used}}
60+
} // expected-note {{used here}}

0 commit comments

Comments
 (0)