Commit e710a1fb authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: report more precise errors about untyped constants

Previously, we used a single "untyped number" type for all untyped
numeric constants. This led to vague error messages like "string(1.0)"
reporting that "1 (type untyped number)" can't be converted to string,
even though "string(1)" is valid.

This CL makes cmd/compile more like go/types by utilizing
types.Ideal{int,rune,float,complex} instead of types.Types[TIDEAL],
and keeping n.Type in sync with n.Val().Ctype() during constant
folding.

Thanks to K Heller for looking into this issue, and for the included
test case.

Fixes #21979.

Change-Id: Ibfea88c05704bc3c0a502a455d018a375589754d
Reviewed-on: https://go-review.googlesource.com/c/go/+/194019Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 51c8d969
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
package gc package gc
import ( import (
"cmd/compile/internal/types"
"cmd/internal/src" "cmd/internal/src"
) )
...@@ -15,15 +14,6 @@ import ( ...@@ -15,15 +14,6 @@ import (
// the same name appears in an error message. // the same name appears in an error message.
var numImport = make(map[string]int) var numImport = make(map[string]int)
func idealType(typ *types.Type) *types.Type {
switch typ {
case types.Idealint, types.Idealrune, types.Idealfloat, types.Idealcomplex:
// canonicalize ideal types
typ = types.Types[TIDEAL]
}
return typ
}
func npos(pos src.XPos, n *Node) *Node { func npos(pos src.XPos, n *Node) *Node {
n.Pos = pos n.Pos = pos
return n return n
......
...@@ -998,6 +998,11 @@ func setconst(n *Node, v Val) { ...@@ -998,6 +998,11 @@ func setconst(n *Node, v Val) {
Xoffset: BADWIDTH, Xoffset: BADWIDTH,
} }
n.SetVal(v) n.SetVal(v)
if n.Type.IsUntyped() {
// TODO(mdempsky): Make typecheck responsible for setting
// the correct untyped type.
n.Type = idealType(v.Ctype())
}
// Check range. // Check range.
lno := setlineno(n) lno := setlineno(n)
...@@ -1030,24 +1035,29 @@ func setintconst(n *Node, v int64) { ...@@ -1030,24 +1035,29 @@ func setintconst(n *Node, v int64) {
func nodlit(v Val) *Node { func nodlit(v Val) *Node {
n := nod(OLITERAL, nil, nil) n := nod(OLITERAL, nil, nil)
n.SetVal(v) n.SetVal(v)
switch v.Ctype() { n.Type = idealType(v.Ctype())
default: return n
Fatalf("nodlit ctype %d", v.Ctype()) }
func idealType(ct Ctype) *types.Type {
switch ct {
case CTSTR: case CTSTR:
n.Type = types.Idealstring return types.Idealstring
case CTBOOL: case CTBOOL:
n.Type = types.Idealbool return types.Idealbool
case CTINT:
case CTINT, CTRUNE, CTFLT, CTCPLX: return types.Idealint
n.Type = types.Types[TIDEAL] case CTRUNE:
return types.Idealrune
case CTFLT:
return types.Idealfloat
case CTCPLX:
return types.Idealcomplex
case CTNIL: case CTNIL:
n.Type = types.Types[TNIL] return types.Types[TNIL]
} }
Fatalf("unexpected Ctype: %v", ct)
return n return nil
} }
// idealkind returns a constant kind like consttype // idealkind returns a constant kind like consttype
......
...@@ -686,11 +686,21 @@ func typefmt(t *types.Type, flag FmtFlag, mode fmtMode, depth int) string { ...@@ -686,11 +686,21 @@ func typefmt(t *types.Type, flag FmtFlag, mode fmtMode, depth int) string {
} }
if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" { if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
name := basicnames[t.Etype] switch t {
if t == types.Idealbool || t == types.Idealstring { case types.Idealbool:
name = "untyped " + name return "untyped bool"
} case types.Idealstring:
return name return "untyped string"
case types.Idealint:
return "untyped int"
case types.Idealrune:
return "untyped rune"
case types.Idealfloat:
return "untyped float"
case types.Idealcomplex:
return "untyped complex"
}
return basicnames[t.Etype]
} }
if mode == FDbg { if mode == FDbg {
......
...@@ -374,8 +374,6 @@ func (p *importReader) value() (typ *types.Type, v Val) { ...@@ -374,8 +374,6 @@ func (p *importReader) value() (typ *types.Type, v Val) {
p.float(&x.Imag, typ) p.float(&x.Imag, typ)
v.U = x v.U = x
} }
typ = idealType(typ)
return return
} }
......
...@@ -1407,20 +1407,18 @@ func typecheck1(n *Node, top int) (res *Node) { ...@@ -1407,20 +1407,18 @@ func typecheck1(n *Node, top int) (res *Node) {
} }
// Determine result type. // Determine result type.
et := t.Etype switch t.Etype {
switch et {
case TIDEAL: case TIDEAL:
// result is ideal n.Type = types.Idealfloat
case TCOMPLEX64: case TCOMPLEX64:
et = TFLOAT32 n.Type = types.Types[TFLOAT32]
case TCOMPLEX128: case TCOMPLEX128:
et = TFLOAT64 n.Type = types.Types[TFLOAT64]
default: default:
yyerror("invalid argument %L for %v", l, n.Op) yyerror("invalid argument %L for %v", l, n.Op)
n.Type = nil n.Type = nil
return n return n
} }
n.Type = types.Types[et]
case OCOMPLEX: case OCOMPLEX:
ok |= ctxExpr ok |= ctxExpr
...@@ -1457,7 +1455,7 @@ func typecheck1(n *Node, top int) (res *Node) { ...@@ -1457,7 +1455,7 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
case TIDEAL: case TIDEAL:
t = types.Types[TIDEAL] t = types.Idealcomplex
case TFLOAT32: case TFLOAT32:
t = types.Types[TCOMPLEX64] t = types.Types[TCOMPLEX64]
...@@ -2683,20 +2681,20 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string { ...@@ -2683,20 +2681,20 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
// e.g in error messages about wrong arguments to return. // e.g in error messages about wrong arguments to return.
func sigrepr(t *types.Type) string { func sigrepr(t *types.Type) string {
switch t { switch t {
default:
return t.String()
case types.Types[TIDEAL]:
// "untyped number" is not commonly used
// outside of the compiler, so let's use "number".
return "number"
case types.Idealstring: case types.Idealstring:
return "string" return "string"
case types.Idealbool: case types.Idealbool:
return "bool" return "bool"
} }
if t.Etype == TIDEAL {
// "untyped number" is not commonly used
// outside of the compiler, so let's use "number".
// TODO(mdempsky): Revisit this.
return "number"
}
return t.String()
} }
// retsigerr returns the signature of the types // retsigerr returns the signature of the types
......
...@@ -334,14 +334,7 @@ func typeinit() { ...@@ -334,14 +334,7 @@ func typeinit() {
maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64] maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64]
minfltval[TCOMPLEX128] = minfltval[TFLOAT64] minfltval[TCOMPLEX128] = minfltval[TFLOAT64]
// for walk to use in error messages types.Types[TINTER] = types.New(TINTER) // empty interface
types.Types[TFUNC] = functype(nil, nil, nil)
// types used in front end
// types.Types[TNIL] got set early in lexinit
types.Types[TIDEAL] = types.New(TIDEAL)
types.Types[TINTER] = types.New(TINTER)
// simple aliases // simple aliases
simtype[TMAP] = TPTR simtype[TMAP] = TPTR
......
...@@ -53,6 +53,13 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b ...@@ -53,6 +53,13 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
assumedEqual[typePair{t1, t2}] = struct{}{} assumedEqual[typePair{t1, t2}] = struct{}{}
switch t1.Etype { switch t1.Etype {
case TIDEAL:
// Historically, cmd/compile used a single "untyped
// number" type, so all untyped number types were
// identical. Match this behavior.
// TODO(mdempsky): Revisit this.
return true
case TINTER: case TINTER:
if t1.NumFields() != t2.NumFields() { if t1.NumFields() != t2.NumFields() {
return false return false
......
...@@ -57,7 +57,7 @@ const ( ...@@ -57,7 +57,7 @@ const (
TUNSAFEPTR TUNSAFEPTR
// pseudo-types for literals // pseudo-types for literals
TIDEAL TIDEAL // untyped numeric constants
TNIL TNIL
TBLANK TBLANK
...@@ -94,7 +94,6 @@ const ( ...@@ -94,7 +94,6 @@ const (
// It also stores pointers to several special types: // It also stores pointers to several special types:
// - Types[TANY] is the placeholder "any" type recognized by substArgTypes. // - Types[TANY] is the placeholder "any" type recognized by substArgTypes.
// - Types[TBLANK] represents the blank variable's type. // - Types[TBLANK] represents the blank variable's type.
// - Types[TIDEAL] represents untyped numeric constants.
// - Types[TNIL] represents the predeclared "nil" value's type. // - Types[TNIL] represents the predeclared "nil" value's type.
// - Types[TUNSAFEPTR] is package unsafe's Pointer type. // - Types[TUNSAFEPTR] is package unsafe's Pointer type.
var Types [NTYPE]*Type var Types [NTYPE]*Type
...@@ -112,8 +111,6 @@ var ( ...@@ -112,8 +111,6 @@ var (
Idealbool *Type Idealbool *Type
// Types to represent untyped numeric constants. // Types to represent untyped numeric constants.
// Note: Currently these are only used within the binary export
// data format. The rest of the compiler only uses Types[TIDEAL].
Idealint = New(TIDEAL) Idealint = New(TIDEAL)
Idealrune = New(TIDEAL) Idealrune = New(TIDEAL)
Idealfloat = New(TIDEAL) Idealfloat = New(TIDEAL)
......
...@@ -19,7 +19,7 @@ func F() { ...@@ -19,7 +19,7 @@ func F() {
const x = 1 const x = 1
func G() { func G() {
switch t := x.(type) { // ERROR "cannot type switch on non-interface value x \(type untyped number\)" switch t := x.(type) { // ERROR "cannot type switch on non-interface value x \(type untyped int\)"
default: default:
} }
} }
// errorcheck
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func f() {
_ = bool("") // ERROR "cannot convert .. \(type untyped string\) to type bool"
_ = bool(1) // ERROR "cannot convert 1 \(type untyped int\) to type bool"
_ = bool(1.0) // ERROR "cannot convert 1 \(type untyped float\) to type bool"
_ = bool(-4 + 2i) // ERROR "cannot convert -4 \+ 2i \(type untyped complex\) to type bool"
_ = string(true) // ERROR "cannot convert true \(type untyped bool\) to type string"
_ = string(-1)
_ = string(1.0) // ERROR "cannot convert 1 \(type untyped float\) to type string"
_ = string(-4 + 2i) // ERROR "cannot convert -4 \+ 2i \(type untyped complex\) to type string"
_ = int("") // ERROR "cannot convert .. \(type untyped string\) to type int"
_ = int(true) // ERROR "cannot convert true \(type untyped bool\) to type int"
_ = int(-1)
_ = int(1)
_ = int(1.0)
_ = int(-4 + 2i) // ERROR "truncated to integer"
_ = uint("") // ERROR "cannot convert .. \(type untyped string\) to type uint"
_ = uint(true) // ERROR "cannot convert true \(type untyped bool\) to type uint"
_ = uint(-1) // ERROR "constant -1 overflows uint"
_ = uint(1)
_ = uint(1.0)
_ = uint(-4 + 2i) // ERROR "constant -4 overflows uint" "truncated to integer"
_ = float64("") // ERROR "cannot convert .. \(type untyped string\) to type float64"
_ = float64(true) // ERROR "cannot convert true \(type untyped bool\) to type float64"
_ = float64(-1)
_ = float64(1)
_ = float64(1.0)
_ = float64(-4 + 2i) // ERROR "truncated to real"
_ = complex128("") // ERROR "cannot convert .. \(type untyped string\) to type complex128"
_ = complex128(true) // ERROR "cannot convert true \(type untyped bool\) to type complex128"
_ = complex128(-1)
_ = complex128(1)
_ = complex128(1.0)
}
...@@ -11,5 +11,5 @@ package main ...@@ -11,5 +11,5 @@ package main
func main() { func main() {
_ = copy(nil, []int{}) // ERROR "use of untyped nil" _ = copy(nil, []int{}) // ERROR "use of untyped nil"
_ = copy([]int{}, nil) // ERROR "use of untyped nil" _ = copy([]int{}, nil) // ERROR "use of untyped nil"
_ = 1 + true // ERROR "mismatched types untyped number and untyped bool" _ = 1 + true // ERROR "mismatched types untyped int and untyped bool"
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment