Skip to content

Commit

Permalink
cmd/compile: rewrite untyped constant conversion logic
Browse files Browse the repository at this point in the history
This CL detangles the hairy mess that was convlit+defaultlit. In
particular, it makes the following changes:

1. convlit1 now follows the standard typecheck behavior of setting
"n.Type = nil" if there's an error. Notably, this means for a lot of
test cases, we now avoid reporting useless follow-on error messages.
For example, after reporting that "1 << s + 1.0" has an invalid shift,
we no longer also report that it can't be assigned to string.

2. Previously, assignconvfn had some extra logic for trying to
suppress errors from convlit/defaultlit so that it could provide its
own errors with better context information. Instead, this extra
context information is now passed down into convlit1 directly.

3. Relatedly, this CL also removes redundant calls to defaultlit prior
to assignconv. As a consequence, when an expression doesn't make sense
for a particular assignment (e.g., assigning an untyped string to an
integer), the error messages now say "untyped string" instead of just
"string". This is more consistent with go/types behavior.

4. defaultlit2 is now smarter about only trying to convert pairs of
untyped constants when it's likely to succeed. This allows us to
report better error messages for things like 3+"x"; instead of "cannot
convert 3 to string" we now report "mismatched types untyped number
and untyped string".

Passes toolstash-check.

Change-Id: I26822a02dc35855bd0ac774907b1cf5737e91882
Reviewed-on: https://go-review.googlesource.com/c/go/+/187657
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
  • Loading branch information
mdempsky committed Sep 6, 2019
1 parent ad1f2c9 commit 581526c
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 325 deletions.
472 changes: 184 additions & 288 deletions src/cmd/compile/internal/gc/const.go

Large diffs are not rendered by default.

18 changes: 16 additions & 2 deletions src/cmd/compile/internal/gc/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -1549,11 +1549,25 @@ func (s *state) ssaOp(op Op, t *types.Type) ssa.Op {
}

func floatForComplex(t *types.Type) *types.Type {
if t.Size() == 8 {
switch t.Etype {
case TCOMPLEX64:
return types.Types[TFLOAT32]
} else {
case TCOMPLEX128:
return types.Types[TFLOAT64]
}
Fatalf("unexpected type: %v", t)
return nil
}

func complexForFloat(t *types.Type) *types.Type {
switch t.Etype {
case TFLOAT32:
return types.Types[TCOMPLEX64]
case TFLOAT64:
return types.Types[TCOMPLEX128]
}
Fatalf("unexpected type: %v", t)
return nil
}

type opAndTwoTypes struct {
Expand Down
13 changes: 5 additions & 8 deletions src/cmd/compile/internal/gc/subr.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,11 +798,10 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
yyerror("use of untyped nil")
}

old := n
od := old.Diag()
old.SetDiag(true) // silence errors about n; we'll issue one below
n = defaultlit(n, t)
old.SetDiag(od)
n = convlit1(n, t, false, context)
if n.Type == nil {
return n
}
if t.Etype == TBLANK {
return n
}
Expand All @@ -826,9 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
var why string
op := assignop(n.Type, t, &why)
if op == 0 {
if !old.Diag() {
yyerror("cannot use %L as type %v in %s%s", n, t, context(), why)
}
yyerror("cannot use %L as type %v in %s%s", n, t, context(), why)
op = OCONV
}

Expand Down
22 changes: 9 additions & 13 deletions src/cmd/compile/internal/gc/typecheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,11 @@ func typecheck1(n *Node, top int) (res *Node) {

if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
l, r = defaultlit2(l, r, true)
if r.Type.IsInterface() == l.Type.IsInterface() || aop == 0 {
if l.Type == nil || r.Type == nil {
n.Type = nil
return n
}
if l.Type.IsInterface() == r.Type.IsInterface() || aop == 0 {
yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
n.Type = nil
return n
Expand Down Expand Up @@ -1049,10 +1053,7 @@ func typecheck1(n *Node, top int) (res *Node) {
}

case TMAP:
n.Right = defaultlit(n.Right, t.Key())
if n.Right.Type != nil {
n.Right = assignconv(n.Right, t.Key(), "map index")
}
n.Right = assignconv(n.Right, t.Key(), "map index")
n.Type = t.Elem()
n.Op = OINDEXMAP
n.ResetAux()
Expand Down Expand Up @@ -1104,13 +1105,11 @@ func typecheck1(n *Node, top int) (res *Node) {
return n
}

n.Right = defaultlit(n.Right, t.Elem())
r := n.Right
if r.Type == nil {
n.Right = assignconv(n.Right, t.Elem(), "send")
if n.Right.Type == nil {
n.Type = nil
return n
}
n.Right = assignconv(r, t.Elem(), "send")
n.Type = nil

case OSLICEHEADER:
Expand Down Expand Up @@ -1638,7 +1637,7 @@ func typecheck1(n *Node, top int) (res *Node) {
ok |= ctxExpr
checkwidth(n.Type) // ensure width is calculated for backend
n.Left = typecheck(n.Left, ctxExpr)
n.Left = convlit1(n.Left, n.Type, true, noReuse)
n.Left = convlit1(n.Left, n.Type, true, nil)
t := n.Left.Type
if t == nil || n.Type == nil {
n.Type = nil
Expand Down Expand Up @@ -2862,7 +2861,6 @@ func typecheckcomplit(n *Node) (res *Node) {
r := *vp
pushtype(r, t.Elem())
r = typecheck(r, ctxExpr)
r = defaultlit(r, t.Elem())
*vp = assignconv(r, t.Elem(), "array or slice literal")

i++
Expand Down Expand Up @@ -2900,14 +2898,12 @@ func typecheckcomplit(n *Node) (res *Node) {
r := l.Left
pushtype(r, t.Key())
r = typecheck(r, ctxExpr)
r = defaultlit(r, t.Key())
l.Left = assignconv(r, t.Key(), "map key")
cs.add(lineno, l.Left, "key", "map literal")

r = l.Right
pushtype(r, t.Elem())
r = typecheck(r, ctxExpr)
r = defaultlit(r, t.Elem())
l.Right = assignconv(r, t.Elem(), "map value")
}

Expand Down
9 changes: 9 additions & 0 deletions src/cmd/compile/internal/types/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,15 @@ func (t *Type) IsPtrShaped() bool {
t.Etype == TMAP || t.Etype == TCHAN || t.Etype == TFUNC
}

// HasNil reports whether the set of values determined by t includes nil.
func (t *Type) HasNil() bool {
switch t.Etype {
case TCHAN, TFUNC, TINTER, TMAP, TPTR, TSLICE, TUNSAFEPTR:
return true
}
return false
}

func (t *Type) IsString() bool {
return t.Etype == TSTRING
}
Expand Down
4 changes: 2 additions & 2 deletions test/convlit.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ var _ = int(unsafe.Pointer(uintptr(65))) // ERROR "convert"
// implicit conversions merit scrutiny
var s string
var bad1 string = 1 // ERROR "conver|incompatible|invalid|cannot"
var bad2 = s + 1 // ERROR "conver|incompatible|invalid"
var bad3 = s + 'a' // ERROR "conver|incompatible|invalid"
var bad2 = s + 1 // ERROR "conver|incompatible|invalid|cannot"
var bad3 = s + 'a' // ERROR "conver|incompatible|invalid|cannot"
var bad4 = "a" + 1 // ERROR "literals|incompatible|convert|invalid"
var bad5 = "a" + 'a' // ERROR "literals|incompatible|convert|invalid"

Expand Down
2 changes: 1 addition & 1 deletion test/ddd1.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var (
_ = sum()
_ = sum(1.0, 2.0)
_ = sum(1.5) // ERROR "integer"
_ = sum("hello") // ERROR ".hello. .type string. as type int|incompatible"
_ = sum("hello") // ERROR ".hello. .type untyped string. as type int|incompatible"
_ = sum([]int{1}) // ERROR "\[\]int literal.*as type int|incompatible"
)

Expand Down
3 changes: 1 addition & 2 deletions test/fixedbugs/issue17645.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ type Foo struct {

func main() {
var s []int
var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type string\) as type int in field value" "cannot use Foo literal \(type Foo\) as type int in append" "cannot use append\(s\, Foo literal\) \(type \[\]int\) as type string in assignment"
var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value" "cannot use Foo literal \(type Foo\) as type int in append" "cannot use append\(s\, Foo literal\) \(type \[\]int\) as type string in assignment"
}

2 changes: 1 addition & 1 deletion test/fixedbugs/issue7153.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

package p

var _ = []int{a: true, true} // ERROR "undefined: a" "cannot use true \(type bool\) as type int in array or slice literal"
var _ = []int{a: true, true} // ERROR "undefined: a" "cannot use true \(type untyped bool\) as type int in array or slice literal"
2 changes: 1 addition & 1 deletion test/fixedbugs/issue7310.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ package main
func main() {
_ = copy(nil, []int{}) // ERROR "use of untyped nil"
_ = copy([]int{}, nil) // ERROR "use of untyped nil"
_ = 1+true // ERROR "cannot convert true" "mismatched types int and bool"
_ = 1 + true // ERROR "mismatched types untyped number and untyped bool"
}
6 changes: 3 additions & 3 deletions test/fixedbugs/issue8438.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
package main

func main() {
_ = []byte{"foo"} // ERROR "cannot convert"
_ = []int{"foo"} // ERROR "cannot convert"
_ = []rune{"foo"} // ERROR "cannot convert"
_ = []byte{"foo"} // ERROR "cannot use"
_ = []int{"foo"} // ERROR "cannot use"
_ = []rune{"foo"} // ERROR "cannot use"
_ = []string{"foo"} // OK
}
2 changes: 1 addition & 1 deletion test/rename1.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func main() {
var n byte // ERROR "not a type|expected type"
var y = float32(0) // ERROR "cannot call|expected function"
const (
a = 1 + iota // ERROR "invalid operation|incompatible types" "cannot convert iota"
a = 1 + iota // ERROR "invalid operation|incompatible types"
)

}
Expand Down
7 changes: 4 additions & 3 deletions test/shift1.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ func h(x float64) int { return 0 }
var (
s uint = 33
u = 1.0 << s // ERROR "invalid operation|shift of non-integer operand"
v float32 = 1 << s // ERROR "invalid" "as type float32"
v float32 = 1 << s // ERROR "invalid"
)

// non-constant shift expressions
var (
e1 = g(2.0 << s) // ERROR "invalid|shift of non-integer operand" "as type interface"
f1 = h(2 << s) // ERROR "invalid" "as type float64"
e1 = g(2.0 << s) // ERROR "invalid|shift of non-integer operand"
f1 = h(2 << s) // ERROR "invalid"
g1 int64 = 1.1 << s // ERROR "truncated"
)

Expand Down Expand Up @@ -66,6 +66,7 @@ func _() {
u2 = 1<<s != 1.0 // ERROR "non-integer|float64"
v float32 = 1 << s // ERROR "non-integer|float32"
w int64 = 1.0 << 33 // 1.0<<33 is a constant shift expression

_, _, _, _, _, _, _, _, _, _ = j, k, m, n, o, u, u1, u2, v, w
)

Expand Down

0 comments on commit 581526c

Please sign in to comment.