Skip to content

Commit 02f3b29

Browse files
committed
resolve: allow if/for/while statements at toplevel
These constructs are still disallowed in the Bazel build language, but it is easy to check for them as a separate pass over the syntax tree, so there is no need to complicate the spec with this restriction, which is a nuisance in other dialects. Clients that set the -globalreassign flag to suppress the check need do so no longer. Change-Id: I39ddb2128e6151a878a884fdb84d7017df2d5d51
1 parent 93b8d14 commit 02f3b29

File tree

3 files changed

+14
-53
lines changed

3 files changed

+14
-53
lines changed

doc/spec.md

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ concurrency, and other such features of Python.
9797
* [Expression statements](#expression-statements)
9898
* [If statements](#if-statements)
9999
* [For loops](#for-loops)
100+
* [While loops](#while-loops)
100101
* [Break and Continue](#break-and-continue)
101102
* [Load statements](#load-statements)
102103
* [Module execution](#module-execution)
@@ -2629,14 +2630,6 @@ else:
26292630
result = 0
26302631
```
26312632
2632-
An `if` statement is permitted only within a function definition.
2633-
An `if` statement at top level results in a static error.
2634-
2635-
<b>Implementation note:</b>
2636-
The Go implementation of Starlark permits `if`-statements to appear at top-level
2637-
if the `-globalreassign` flag is enabled.
2638-
2639-
26402633
### While loops
26412634
26422635
A `while` loop evaluates an expression (the _condition_) and if the truth
@@ -2698,13 +2691,6 @@ Within the body of a `for` loop, `break` and `continue` statements may
26982691
be used to stop the execution of the loop or advance to the next
26992692
iteration.
27002693
2701-
In Starlark, a `for` loop is permitted only within a function definition.
2702-
A `for` loop at top level results in a static error.
2703-
2704-
<b>Implementation note:</b>
2705-
The Go implementation of Starlark permits loops to appear at top-level
2706-
if the `-globalreassign` flag is enabled.
2707-
27082694
27092695
### Break and Continue
27102696
@@ -2776,7 +2762,8 @@ load("module.star", "x", "y", "z") # assigns x, y, and z
27762762
load("module.star", "x", y2="y", "z") # assigns x, y2, and z
27772763
```
27782764
2779-
A load statement within a function is a static error.
2765+
Load statements must be at top level.
2766+
It is a static error if a load statement appears within a function, if-statement, or a loop.
27802767
27812768
27822769
## Module execution

resolve/resolve.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ var (
9393
AllowLambda = false // allow lambda expressions
9494
AllowFloat = false // allow floating point literals, the 'float' built-in, and x / y
9595
AllowSet = false // allow the 'set' built-in
96-
AllowGlobalReassign = false // allow reassignment to globals declared in same file (deprecated)
96+
AllowGlobalReassign = false // allow reassignment to globals declared in same file
9797
AllowBitwise = false // allow bitwise operations (&, |, ^, ~, <<, and >>)
9898
AllowRecursion = false // allow while statements and recursive functions
9999
)
@@ -211,6 +211,7 @@ type resolver struct {
211211
isPredeclared, isUniversal func(name string) bool
212212

213213
loops int // number of enclosing for loops
214+
ifs int // number of enclosing if-statements
214215

215216
errors ErrorList
216217
}
@@ -431,12 +432,11 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
431432
}
432433

433434
case *syntax.IfStmt:
434-
if !AllowGlobalReassign && r.container().function == nil {
435-
r.errorf(stmt.If, "if statement not within a function")
436-
}
437435
r.expr(stmt.Cond)
436+
r.ifs++
438437
r.stmts(stmt.True)
439438
r.stmts(stmt.False)
439+
r.ifs--
440440

441441
case *syntax.AssignStmt:
442442
if !AllowBitwise {
@@ -463,9 +463,6 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
463463
r.function(stmt.Def, stmt.Name.Name, &stmt.Function)
464464

465465
case *syntax.ForStmt:
466-
if !AllowGlobalReassign && r.container().function == nil {
467-
r.errorf(stmt.For, "for loop not within a function")
468-
}
469466
r.expr(stmt.X)
470467
const allowRebind = false
471468
r.assign(stmt.Vars, allowRebind)
@@ -477,9 +474,6 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
477474
if !AllowRecursion {
478475
r.errorf(stmt.While, doesnt+"support while loops")
479476
}
480-
if !AllowGlobalReassign && r.container().function == nil {
481-
r.errorf(stmt.While, "while loop not within a function")
482-
}
483477
r.expr(stmt.Cond)
484478
r.loops++
485479
r.stmts(stmt.Body)
@@ -494,8 +488,8 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
494488
}
495489

496490
case *syntax.LoadStmt:
497-
if r.container().function != nil {
498-
r.errorf(stmt.Load, "load statement within a function")
491+
if r.ifs > 0 || r.loops > 0 || r.container().function != nil {
492+
r.errorf(stmt.Load, "load statement not at top level")
499493
}
500494

501495
const allowRebind = false

resolve/testdata/resolve.star

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ f()
134134
load("module", "name") # ok
135135

136136
def f():
137-
load("foo", "bar") ### "load statement within a function"
137+
load("foo", "bar") ### "load statement not at top level"
138138

139139
load("foo",
140140
"", ### "load: empty identifier"
@@ -149,17 +149,8 @@ load("foo",
149149
return ### "return statement not within a function"
150150

151151
---
152-
# if-statements and for-loops at top-level are forbidden
153-
# (without globalreassign option)
154-
155-
for x in "abc": ### "for loop not within a function"
156-
pass
157-
158-
if x: ### "if statement not within a function"
159-
pass
160-
161-
---
162-
# option:globalreassign
152+
# if-statements and for-loops at top-level are OK in core Starlark,
153+
# but they are rejected by the Bazel BUILD/.bzl dialect.
163154

164155
for x in "abc": # ok
165156
pass
@@ -170,22 +161,11 @@ if x: # ok
170161
---
171162
# while loops are forbidden (without -recursion option)
172163

173-
def f():
174-
while U: ### "dialect does not support while loops"
175-
pass
176-
177-
---
178-
# option:recursion
179-
180-
def f():
181-
while U: # ok
182-
pass
183-
184-
while U: ### "while loop not within a function"
164+
while U: ### "dialect does not support while loops"
185165
pass
186166

187167
---
188-
# option:globalreassign option:recursion
168+
# option:recursion
189169

190170
while U: # ok
191171
pass

0 commit comments

Comments
 (0)