Skip to content

Commit 969ab34

Browse files
griesemergopherbot
authored andcommitted
go/types, types2: don't panic for invalid assignments of comma-ok expressions
The relevant code was broken with CL 478218. Before that CL, Checker.assignVar used to return the assigned type, or nil, in case of failure. Checker.recordCommaOkTypes used to take two types (not two operands), and if one of those types was nil, it would simply not record. CL 478218, lost that (nil) signal. This change consistently reports an assignment check failure by setting x.mode to invalid for initVar and assignVar and then tests if x.mode != invalid before recording a comma-ok expression. Fixes #59371. Change-Id: I193815ff3e4b43e3e510fe25bd0e72e0a6a816c6 Reviewed-on: https://go-review.googlesource.com/c/go/+/486135 Reviewed-by: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> Auto-Submit: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 7e66a8a commit 969ab34

File tree

3 files changed

+51
-12
lines changed

3 files changed

+51
-12
lines changed

src/cmd/compile/internal/types2/assignments.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
// if necessary by attempting to convert untyped values to the appropriate
1818
// type. context describes the context in which the assignment takes place.
1919
// Use T == nil to indicate assignment to an untyped blank identifier.
20-
// x.mode is set to invalid if the assignment failed.
20+
// If the assignment check fails, x.mode is set to invalid.
2121
func (check *Checker) assignment(x *operand, T Type, context string) {
2222
check.singleValue(x)
2323

@@ -135,11 +135,14 @@ func (check *Checker) initConst(lhs *Const, x *operand) {
135135
// initVar checks the initialization lhs = x in a variable declaration.
136136
// If lhs doesn't have a type yet, it is given the type of x,
137137
// or Typ[Invalid] in case of an error.
138+
// If the initialization check fails, x.mode is set to invalid.
138139
func (check *Checker) initVar(lhs *Var, x *operand, context string) {
139140
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
140141
if lhs.typ == nil {
141142
lhs.typ = Typ[Invalid]
142143
}
144+
x.mode = invalid
145+
return
143146
}
144147

145148
// If lhs doesn't have a type yet, use the type of x.
@@ -150,6 +153,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) {
150153
if typ == Typ[UntypedNil] {
151154
check.errorf(x, UntypedNilUse, "use of untyped nil in %s", context)
152155
lhs.typ = Typ[Invalid]
156+
x.mode = invalid
153157
return
154158
}
155159
typ = Default(typ)
@@ -227,20 +231,21 @@ func (check *Checker) lhsVar(lhs syntax.Expr) Type {
227231

228232
// assignVar checks the assignment lhs = rhs (if x == nil), or lhs = x (if x != nil).
229233
// If x != nil, it must be the evaluation of rhs (and rhs will be ignored).
234+
// If the assignment check fails and x != nil, x.mode is set to invalid.
230235
func (check *Checker) assignVar(lhs, rhs syntax.Expr, x *operand) {
231236
T := check.lhsVar(lhs) // nil if lhs is _
232237
if T == Typ[Invalid] {
233238
check.use(rhs)
239+
if x != nil {
240+
x.mode = invalid
241+
}
234242
return
235243
}
236244

237245
if x == nil {
238246
x = new(operand)
239247
check.expr(T, x, rhs)
240248
}
241-
if x.mode == invalid {
242-
return
243-
}
244249

245250
context := "assignment"
246251
if T == nil {
@@ -396,7 +401,9 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt sy
396401
for i, lhs := range lhs {
397402
check.initVar(lhs, rhs[i], context)
398403
}
399-
if commaOk {
404+
// Only record comma-ok expression if both initializations succeeded
405+
// (go.dev/issue/59371).
406+
if commaOk && rhs[0].mode != invalid && rhs[1].mode != invalid {
400407
check.recordCommaOkTypes(orig_rhs[0], rhs)
401408
}
402409
return
@@ -458,7 +465,9 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
458465
for i, lhs := range lhs {
459466
check.assignVar(lhs, nil, rhs[i])
460467
}
461-
if commaOk {
468+
// Only record comma-ok expression if both assignments succeeded
469+
// (go.dev/issue/59371).
470+
if commaOk && rhs[0].mode != invalid && rhs[1].mode != invalid {
462471
check.recordCommaOkTypes(orig_rhs[0], rhs)
463472
}
464473
return

src/go/types/assignments.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
// if necessary by attempting to convert untyped values to the appropriate
1818
// type. context describes the context in which the assignment takes place.
1919
// Use T == nil to indicate assignment to an untyped blank identifier.
20-
// x.mode is set to invalid if the assignment failed.
20+
// If the assignment check fails, x.mode is set to invalid.
2121
func (check *Checker) assignment(x *operand, T Type, context string) {
2222
check.singleValue(x)
2323

@@ -73,6 +73,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
7373
check.updateExprType(x.expr, newType, false)
7474
}
7575
}
76+
// x.typ is typed
7677

7778
// A generic (non-instantiated) function value cannot be assigned to a variable.
7879
if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
@@ -133,11 +134,14 @@ func (check *Checker) initConst(lhs *Const, x *operand) {
133134
// initVar checks the initialization lhs = x in a variable declaration.
134135
// If lhs doesn't have a type yet, it is given the type of x,
135136
// or Typ[Invalid] in case of an error.
137+
// If the initialization check fails, x.mode is set to invalid.
136138
func (check *Checker) initVar(lhs *Var, x *operand, context string) {
137139
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
138140
if lhs.typ == nil {
139141
lhs.typ = Typ[Invalid]
140142
}
143+
x.mode = invalid
144+
return
141145
}
142146

143147
// If lhs doesn't have a type yet, use the type of x.
@@ -148,6 +152,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) {
148152
if typ == Typ[UntypedNil] {
149153
check.errorf(x, UntypedNilUse, "use of untyped nil in %s", context)
150154
lhs.typ = Typ[Invalid]
155+
x.mode = invalid
151156
return
152157
}
153158
typ = Default(typ)
@@ -225,20 +230,21 @@ func (check *Checker) lhsVar(lhs ast.Expr) Type {
225230

226231
// assignVar checks the assignment lhs = rhs (if x == nil), or lhs = x (if x != nil).
227232
// If x != nil, it must be the evaluation of rhs (and rhs will be ignored).
233+
// If the assignment check fails and x != nil, x.mode is set to invalid.
228234
func (check *Checker) assignVar(lhs, rhs ast.Expr, x *operand) {
229235
T := check.lhsVar(lhs) // nil if lhs is _
230236
if T == Typ[Invalid] {
231237
check.use(rhs)
238+
if x != nil {
239+
x.mode = invalid
240+
}
232241
return
233242
}
234243

235244
if x == nil {
236245
x = new(operand)
237246
check.expr(T, x, rhs)
238247
}
239-
if x.mode == invalid {
240-
return
241-
}
242248

243249
context := "assignment"
244250
if T == nil {
@@ -394,7 +400,9 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []ast.Expr, returnStmt ast.S
394400
for i, lhs := range lhs {
395401
check.initVar(lhs, rhs[i], context)
396402
}
397-
if commaOk {
403+
// Only record comma-ok expression if both initializations succeeded
404+
// (go.dev/issue/59371).
405+
if commaOk && rhs[0].mode != invalid && rhs[1].mode != invalid {
398406
check.recordCommaOkTypes(orig_rhs[0], rhs)
399407
}
400408
return
@@ -456,7 +464,9 @@ func (check *Checker) assignVars(lhs, orig_rhs []ast.Expr) {
456464
for i, lhs := range lhs {
457465
check.assignVar(lhs, nil, rhs[i])
458466
}
459-
if commaOk {
467+
// Only record comma-ok expression if both assignments succeeded
468+
// (go.dev/issue/59371).
469+
if commaOk && rhs[0].mode != invalid && rhs[1].mode != invalid {
460470
check.recordCommaOkTypes(orig_rhs[0], rhs)
461471
}
462472
return
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
var m map[int]int
8+
9+
func _() {
10+
_, ok /* ERROR "undefined: ok" */ = m[0] // must not crash
11+
}
12+
13+
func _() {
14+
var ok = undef /* ERROR "undefined: undef" */
15+
x, ok := m[0] // must not crash
16+
_ = x
17+
// The next line is only needed for go/types, not types2.
18+
// TODO(gri) find cause and fix
19+
_ = ok
20+
}

0 commit comments

Comments
 (0)