Skip to content

Commit f74d1ea

Browse files
authored
interp: detect invalid uses of _ as value
We now detect the use of special identifier _ (blank) during parsing in order to abort compiling early. It allows to not panic later during execution. We must catch all the cases where blank is used as a value, but still preserve the cases where it is assigned, used as a struct field or for import side effects. Fixes #1386.
1 parent 606b4c3 commit f74d1ea

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

interp/cfg.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
319319
}
320320
// Propagate type to children, to handle implicit types
321321
for _, c := range child {
322+
if isBlank(c) {
323+
err = n.cfgErrorf("cannot use _ as value")
324+
return false
325+
}
322326
switch c.kind {
323327
case binaryExpr, unaryExpr, compositeLitExpr:
324328
// Do not attempt to propagate composite type to operator expressions,
@@ -478,6 +482,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
478482

479483
switch n.kind {
480484
case addressExpr:
485+
if isBlank(n.child[0]) {
486+
err = n.cfgErrorf("cannot use _ as value")
487+
break
488+
}
481489
wireChild(n)
482490

483491
err = check.addressExpr(n)
@@ -513,6 +521,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
513521
updateSym := false
514522
var sym *symbol
515523
var level int
524+
525+
if isBlank(src) {
526+
err = n.cfgErrorf("cannot use _ as value")
527+
break
528+
}
516529
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
517530
if atyp != nil {
518531
dest.typ = atyp
@@ -645,6 +658,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
645658
}
646659

647660
case incDecStmt:
661+
err = check.unaryExpr(n)
662+
if err != nil {
663+
break
664+
}
648665
wireChild(n)
649666
n.findex = n.child[0].findex
650667
n.level = n.child[0].level
@@ -763,6 +780,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
763780
}
764781

765782
case indexExpr:
783+
if isBlank(n.child[0]) {
784+
err = n.cfgErrorf("cannot use _ as value")
785+
break
786+
}
766787
wireChild(n)
767788
t := n.child[0].typ
768789
switch t.cat {
@@ -877,6 +898,12 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
877898
gotoLabel(n.sym)
878899

879900
case callExpr:
901+
for _, c := range n.child {
902+
if isBlank(c) {
903+
err = n.cfgErrorf("cannot use _ as value")
904+
return
905+
}
906+
}
880907
wireChild(n)
881908
switch {
882909
case isBuiltinCall(n, sc):
@@ -1392,9 +1419,17 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
13921419
sc = sc.pop()
13931420

13941421
case keyValueExpr:
1422+
if isBlank(n.child[1]) {
1423+
err = n.cfgErrorf("cannot use _ as value")
1424+
break
1425+
}
13951426
wireChild(n)
13961427

13971428
case landExpr:
1429+
if isBlank(n.child[0]) || isBlank(n.child[1]) {
1430+
err = n.cfgErrorf("cannot use _ as value")
1431+
break
1432+
}
13981433
n.start = n.child[0].start
13991434
n.child[0].tnext = n.child[1].start
14001435
setFNext(n.child[0], n)
@@ -1406,6 +1441,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
14061441
}
14071442

14081443
case lorExpr:
1444+
if isBlank(n.child[0]) || isBlank(n.child[1]) {
1445+
err = n.cfgErrorf("cannot use _ as value")
1446+
break
1447+
}
14091448
n.start = n.child[0].start
14101449
n.child[0].tnext = n
14111450
setFNext(n.child[0], n.child[1].start)
@@ -1451,6 +1490,12 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
14511490
err = n.cfgErrorf("too many arguments to return")
14521491
break
14531492
}
1493+
for _, c := range n.child {
1494+
if isBlank(c) {
1495+
err = n.cfgErrorf("cannot use _ as value")
1496+
return
1497+
}
1498+
}
14541499
returnSig := sc.def.child[2]
14551500
if mustReturnValue(returnSig) {
14561501
nret := len(n.child)
@@ -1742,6 +1787,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
17421787
}
17431788

17441789
case starExpr:
1790+
if isBlank(n.child[0]) {
1791+
err = n.cfgErrorf("cannot use _ as value")
1792+
break
1793+
}
17451794
switch {
17461795
case n.anc.kind == defineStmt && len(n.anc.child) == 3 && n.anc.child[1] == n:
17471796
// pointer type expression in a var definition
@@ -1887,6 +1936,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
18871936

18881937
wireChild(n)
18891938
c0, c1 := n.child[0], n.child[1]
1939+
if isBlank(c0) || isBlank(c1) {
1940+
err = n.cfgErrorf("cannot use _ as value")
1941+
break
1942+
}
18901943
if c1.typ == nil {
18911944
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
18921945
return
@@ -2735,3 +2788,10 @@ func isBoolAction(n *node) bool {
27352788
}
27362789
return false
27372790
}
2791+
2792+
func isBlank(n *node) bool {
2793+
if n.kind == parenExpr && len(n.child) > 0 {
2794+
return isBlank(n.child[0])
2795+
}
2796+
return n.ident == "_"
2797+
}

interp/gta.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
5959

6060
for i := 0; i < n.nleft; i++ {
6161
dest, src := n.child[i], n.child[sbase+i]
62+
if isBlank(src) {
63+
err = n.cfgErrorf("cannot use _ as value")
64+
}
6265
val := src.rval
6366
if n.anc.kind == constDecl {
6467
if _, err2 := interp.cfg(n, sc, importPath, pkgName); err2 != nil {
@@ -274,6 +277,10 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
274277
}
275278

276279
case typeSpec, typeSpecAssign:
280+
if isBlank(n.child[0]) {
281+
err = n.cfgErrorf("cannot use _ as value")
282+
return false
283+
}
277284
typeName := n.child[0].ident
278285
var typ *itype
279286
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {

interp/interp_eval_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ func TestEvalAssign(t *testing.T) {
128128
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
129129
{src: "i64 := testpkg.val; i64 == 11", res: "true"},
130130
{pre: func() { eval(t, i, "k := 1") }, src: `k := "Hello world"`, res: "Hello world"}, // allow reassignment in subsequent evaluations
131+
{src: "_ = _", err: "1:28: cannot use _ as value"},
132+
{src: "j := true || _", err: "1:33: cannot use _ as value"},
133+
{src: "j := true && _", err: "1:33: cannot use _ as value"},
134+
{src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"},
131135
})
132136
}
133137

@@ -178,6 +182,7 @@ func TestEvalBuiltin(t *testing.T) {
178182
{src: `t := map[int]int{}; t[123]--; t`, res: "map[123:-1]"},
179183
{src: `t := map[int]int{}; t[123] += 1; t`, res: "map[123:1]"},
180184
{src: `t := map[int]int{}; t[123] -= 1; t`, res: "map[123:-1]"},
185+
{src: `println("hello", _)`, err: "1:28: cannot use _ as value"},
181186
})
182187
}
183188

@@ -202,6 +207,14 @@ func TestEvalDeclWithExpr(t *testing.T) {
202207
})
203208
}
204209

210+
func TestEvalTypeSpec(t *testing.T) {
211+
i := interp.New(interp.Options{})
212+
runTests(t, i, []testCase{
213+
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
214+
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
215+
})
216+
}
217+
205218
func TestEvalFunc(t *testing.T) {
206219
i := interp.New(interp.Options{})
207220
runTests(t, i, []testCase{
@@ -210,6 +223,8 @@ func TestEvalFunc(t *testing.T) {
210223
{src: `(func () int {f := func() (a, b int) {a, b = 3, 4; return}; x, y := f(); return x+y})()`, res: "7"},
211224
{src: `(func () int {f := func() (a int, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
212225
{src: `(func () int {f := func() (a, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
226+
{src: `func f() int { return _ }`, err: "1:29: cannot use _ as value"},
227+
{src: `(func (x int) {})(_)`, err: "1:28: cannot use _ as value"},
213228
})
214229
}
215230

@@ -432,6 +447,8 @@ func TestEvalComparison(t *testing.T) {
432447
`,
433448
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
434449
},
450+
{src: `1 > _`, err: "1:28: cannot use _ as value"},
451+
{src: `(_) > 1`, err: "1:28: cannot use _ as value"},
435452
})
436453
}
437454

@@ -477,6 +494,8 @@ func TestEvalCompositeStruct(t *testing.T) {
477494
{src: `a := struct{A,B,C int}{A:1,A:2,C:3}`, err: "1:55: duplicate field name A in struct literal"},
478495
{src: `a := struct{A,B,C int}{A:1,B:2.2,C:3}`, err: "1:57: 11/5 truncated to int"},
479496
{src: `a := struct{A,B,C int}{A:1,2,C:3}`, err: "1:55: mixture of field:value and value elements in struct literal"},
497+
{src: `a := struct{A,B,C int}{1,2,_}`, err: "1:33: cannot use _ as value"},
498+
{src: `a := struct{A,B,C int}{B: _}`, err: "1:51: cannot use _ as value"},
480499
})
481500
}
482501

@@ -501,6 +520,8 @@ func TestEvalSliceExpression(t *testing.T) {
501520
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
502521
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},
503522
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
523+
{src: `_[12]`, err: "1:28: cannot use _ as value"},
524+
{src: `b := []int{0,1,2}[_:4]`, err: "1:33: cannot use _ as value"},
504525
})
505526
}
506527

@@ -511,6 +532,7 @@ func TestEvalConversion(t *testing.T) {
511532
{src: `i := 1.1; a := uint64(i)`, res: "1"},
512533
{src: `b := string(49)`, res: "1"},
513534
{src: `c := uint64(1.1)`, err: "1:40: cannot convert expression of type untyped float to type uint64"},
535+
{src: `int(_)`, err: "1:28: cannot use _ as value"},
514536
})
515537
}
516538

@@ -520,6 +542,9 @@ func TestEvalUnary(t *testing.T) {
520542
{src: "a := -1", res: "-1"},
521543
{src: "b := +1", res: "1", skip: "BUG"},
522544
{src: "c := !false", res: "true"},
545+
{src: "_ = 2; _++", err: "1:35: cannot use _ as value"},
546+
{src: "_ = false; !_ == true", err: "1:39: cannot use _ as value"},
547+
{src: "!((((_))))", err: "1:28: cannot use _ as value"},
523548
})
524549
}
525550

interp/typecheck.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ func (check typecheck) starExpr(n *node) error {
125125
}
126126

127127
var unaryOpPredicates = opPredicates{
128+
aInc: isNumber,
129+
aDec: isNumber,
128130
aPos: isNumber,
129131
aNeg: isNumber,
130132
aBitNot: isInt,
@@ -134,6 +136,9 @@ var unaryOpPredicates = opPredicates{
134136
// unaryExpr type checks a unary expression.
135137
func (check typecheck) unaryExpr(n *node) error {
136138
c0 := n.child[0]
139+
if isBlank(c0) {
140+
return n.cfgErrorf("cannot use _ as value")
141+
}
137142
t0 := c0.typ.TypeOf()
138143

139144
if n.action == aRecv {
@@ -222,6 +227,10 @@ var binaryOpPredicates = opPredicates{
222227
func (check typecheck) binaryExpr(n *node) error {
223228
c0, c1 := n.child[0], n.child[1]
224229

230+
if isBlank(c0) || isBlank(c1) {
231+
return n.cfgErrorf("cannot use _ as value")
232+
}
233+
225234
a := n.action
226235
if isAssignAction(a) {
227236
a--
@@ -477,6 +486,12 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
477486

478487
// sliceExpr type checks a slice expression.
479488
func (check typecheck) sliceExpr(n *node) error {
489+
for _, c := range n.child {
490+
if isBlank(c) {
491+
return n.cfgErrorf("cannot use _ as value")
492+
}
493+
}
494+
480495
c, child := n.child[0], n.child[1:]
481496

482497
t := c.typ.TypeOf()

0 commit comments

Comments
 (0)