Skip to content

Commit

Permalink
Disallow 'var' pattern bindings in if, while, and guard statements
Browse files Browse the repository at this point in the history
Make the following patterns illegal:

  if var x = ... {
    ...
  }

  guard var x = ... else {
    ...
  }

  while var x = ... {
    ...
  }

And provide a replacement fixit 'var' -> 'let'.

rdar://problem/23172698

Swift SVN r32855
  • Loading branch information
bitjammer committed Oct 24, 2015
1 parent 6d7d45a commit 3434f96
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 157 deletions.
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,8 @@ ERROR(untyped_pattern_ellipsis,pattern_parsing,none,
"'...' cannot be applied to a subpattern which is not explicitly typed", ())
ERROR(non_func_decl_pattern_init,pattern_parsing,none,
"default argument is only permitted for a non-curried function parameter",())
ERROR(var_not_allowed_in_for_in,pattern_parsing,none,
"'var' is not allowed in a for-in statement", ())
ERROR(var_not_allowed_in_pattern,pattern_parsing, none,
"'var' is not allowed in this pattern binding", ())
ERROR(var_pattern_in_var,pattern_parsing,none,
"'%select{var|let}0' cannot appear nested inside another 'var' or "
"'let' pattern", (unsigned))
Expand Down
6 changes: 5 additions & 1 deletion lib/Parse/ParsePattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ ParserResult<Pattern> Parser::parsePattern() {
} else {
// In an always immutable context, `var` is not allowed.
if (alwaysImmutable)
diagnose(varLoc, diag::var_not_allowed_in_for_in)
diagnose(varLoc, diag::var_not_allowed_in_pattern)
.fixItRemove(varLoc);
}

Expand Down Expand Up @@ -1029,6 +1029,10 @@ ParserResult<Pattern> Parser::parseMatchingPatternAsLetOrVar(bool isLet,
if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable)
diagnose(varLoc, diag::let_pattern_in_immutable_context);

if (!isLet && InVarOrLetPattern == IVOLP_AlwaysImmutable)
diagnose(varLoc, diag::var_not_allowed_in_pattern)
.fixItReplace(varLoc, "let");

// In our recursive parse, remember that we're in a var/let pattern.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar);
Expand Down
11 changes: 8 additions & 3 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1245,9 +1245,14 @@ ParserStatus Parser::parseStmtCondition(StmtCondition &Condition,
}
} else {
// Otherwise, this is an implicit optional binding "if let".
ThePattern =
parseMatchingPatternAsLetOrVar(BindingKind == BK_Let, VarLoc,
/*isExprBasic*/ true);

// In our recursive parse, remember that we're in a var/let pattern.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, IVOLP_AlwaysImmutable);

ThePattern = parseMatchingPatternAsLetOrVar(BindingKind == BK_Let,
VarLoc,
/*isExprBasic*/ true);
// The let/var pattern is part of the statement.
if (Pattern *P = ThePattern.getPtrOrNull())
P->setImplicit();
Expand Down
30 changes: 16 additions & 14 deletions stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,10 @@ struct _ParentProcess {
}
if readfds.isset(_childStdout.fd) || errorfds.isset(_childStdout.fd) {
_childStdout.read()
while var line = _childStdout.getline() {
if let index = findSubstring(line, _stdlibUnittestStreamPrefix) {
let controlMessage = line[index..<line.endIndex]._split(";")
while let line = _childStdout.getline() {
var standardOut = line
if let index = findSubstring(standardOut, _stdlibUnittestStreamPrefix) {
let controlMessage = standardOut[index..<standardOut.endIndex]._split(";")
switch controlMessage[1] {
case "expectCrash":
stdoutSeenCrashDelimiter = true
Expand All @@ -568,23 +569,24 @@ struct _ParentProcess {
default:
fatalError("unexpected message")
}
line = line[line.startIndex..<index]
if line.isEmpty {
standardOut = standardOut[standardOut.startIndex..<index]
if standardOut.isEmpty {
continue
}
}
if stdoutSeenCrashDelimiter {
capturedCrashStdout.append(line)
capturedCrashStdout.append(standardOut)
}
print("out>>> \(line)")
print("out>>> \(standardOut)")
}
continue
}
if readfds.isset(_childStderr.fd) || errorfds.isset(_childStderr.fd) {
_childStderr.read()
while var line = _childStderr.getline() {
if let index = findSubstring(line, _stdlibUnittestStreamPrefix) {
let controlMessage = line[index..<line.endIndex]._split(";")
while let line = _childStderr.getline() {
var standardErr = line
if let index = findSubstring(standardErr, _stdlibUnittestStreamPrefix) {
let controlMessage = standardErr[index..<standardErr.endIndex]._split(";")
switch controlMessage[1] {
case "expectCrash":
stderrSeenCrashDelimiter = true
Expand All @@ -593,15 +595,15 @@ struct _ParentProcess {
default:
fatalError("unexpected message")
}
line = line[line.startIndex..<index]
if line.isEmpty {
standardErr = standardErr[standardErr.startIndex..<index]
if standardErr.isEmpty {
continue
}
}
if stderrSeenCrashDelimiter {
capturedCrashStderr.append(line)
capturedCrashStderr.append(standardErr)
}
print("err>>> \(line)")
print("err>>> \(standardErr)")
}
continue
}
Expand Down
24 changes: 16 additions & 8 deletions stdlib/public/core/SequenceAlgorithms.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,19 @@ extension SequenceType ${"" if preds else "where Generator.Element : Comparable"
% end
) ${rethrows_}-> ${GElement}? {
var g = generate()
guard var result = g.next() else { return nil }
var minResult: ${GElement}? = nil
for e in GeneratorSequence(g) {
if let currentMinResult = minResult {
% if preds:
if try isOrderedBefore(e, result) { result = e }
if try isOrderedBefore(e, currentMinResult) { minResult = e }
% else:
if e < result { result = e }
if e < currentMinResult { minResult = e }
% end
} else {
minResult = e
}
}
return result
return minResult
}

/// Returns the maximum element in `self` or `nil` if the sequence is empty.
Expand All @@ -97,15 +101,19 @@ extension SequenceType ${"" if preds else "where Generator.Element : Comparable"
% end
) ${rethrows_}-> ${GElement}? {
var g = generate()
guard var result = g.next() else { return nil }
var maxResult: ${GElement}? = nil
for e in GeneratorSequence(g) {
if let currentMaxResult = maxResult {
% if preds:
if try isOrderedBefore(result, e) { result = e }
if try isOrderedBefore(currentMaxResult, e) { maxResult = e }
% else:
if e > result { result = e }
if e > currentMaxResult { maxResult = e }
% end
} else {
maxResult = e
}
}
return result
return maxResult
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/Constraints/patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ case x ?? 42: break // match value
default: break
}

for (var x) in 0...100 {} // expected-error {{'var' is not allowed in a for-in statement}}
for var x in 0...100 {} // expected-error {{'var' is not allowed in a for-in statement}}
for (var x) in 0...100 {} // expected-error {{'var' is not allowed in this pattern binding}}
for var x in 0...100 {} // expected-error {{'var' is not allowed in this pattern binding}}
for (let x) in 0...100 {} // expected-error {{'let' pattern is already in an immutable context}}

var (let y) = 42 // expected-error {{'let' cannot appear nested inside another 'var' or 'let' pattern}}
Expand Down
17 changes: 0 additions & 17 deletions test/Interpreter/capture_top_level.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
// RUN: %target-run-simple-swift | FileCheck %s

// RUN: %target-build-swift -DVAR %s -o %t-var
// RUN: %target-run %t-var | FileCheck %s

// RUN: %target-build-swift -DVAR_UPDATE %s -o %t-var
// RUN: %target-run %t-var | FileCheck %s

// REQUIRES: executable_test

#if VAR_UPDATE
guard var x = Optional(0) else { fatalError() }
#elseif VAR
guard var x = Optional(42) else { fatalError() }
#else
guard let x = Optional(42) else { fatalError() }
#endif

_ = 0 // intervening code

Expand All @@ -30,10 +17,6 @@ defer {
print("deferred: \(x)")
}

#if VAR_UPDATE
x = 42
#endif

let closureCapture: () -> Void = { [x] in
// Must come after the assignment because of the capture by value.
print("closure-capture: \(x)")
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/foreach.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ func for_each(r: Range<Int>, iir: IntRange<Int>) {
for i r { // expected-error 2{{expected ';' in 'for' statement}} expected-error {{use of unresolved identifier 'i'}}
}
for i in r sum = sum + i; // expected-error{{expected '{' to start the body of for-each loop}}
for var x in 0..<10 {} // expected-error {{'var' is not allowed in a for-in statement}} {{7-11=}}
for var x in 0..<10 {} // expected-error {{'var' is not allowed in this pattern binding}} {{7-11=}}
for let x in 0..<10 {} // expected-error {{'let' pattern is already in an immutable context}} {{7-11=}}
}
6 changes: 3 additions & 3 deletions test/Parse/recovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -626,9 +626,9 @@ func testMultiPatternConditionRecovery(x: Int?) {
_ = z
}
// expected-error@+1 {{binding ended by previous 'where' clause; use 'var' to introduce a new one}} {{30-30=var }}
if var y = x where y == 0, z = x {
z = y; y = z
// expected-error@+1 {{binding ended by previous 'where' clause; use 'let' to introduce a new one}} {{30-30=let }}
if let y = x where y == 0, z = x {
_ = z
}
// <rdar://problem/20883210> QoI: Following a "let" condition with boolean condition spouts nonsensical errors
Expand Down
Loading

0 comments on commit 3434f96

Please sign in to comment.