mirror of
https://github.com/golang/go
synced 2024-11-11 21:50:21 -07:00
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/+/194019 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
51c8d969bd
commit
e710a1fb2e
@ -5,7 +5,6 @@
|
||||
package gc
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
|
||||
@ -15,15 +14,6 @@ import (
|
||||
// the same name appears in an error message.
|
||||
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 {
|
||||
n.Pos = pos
|
||||
return n
|
||||
|
@ -998,6 +998,11 @@ func setconst(n *Node, v Val) {
|
||||
Xoffset: BADWIDTH,
|
||||
}
|
||||
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.
|
||||
lno := setlineno(n)
|
||||
@ -1030,26 +1035,31 @@ func setintconst(n *Node, v int64) {
|
||||
func nodlit(v Val) *Node {
|
||||
n := nod(OLITERAL, nil, nil)
|
||||
n.SetVal(v)
|
||||
switch v.Ctype() {
|
||||
default:
|
||||
Fatalf("nodlit ctype %d", v.Ctype())
|
||||
|
||||
case CTSTR:
|
||||
n.Type = types.Idealstring
|
||||
|
||||
case CTBOOL:
|
||||
n.Type = types.Idealbool
|
||||
|
||||
case CTINT, CTRUNE, CTFLT, CTCPLX:
|
||||
n.Type = types.Types[TIDEAL]
|
||||
|
||||
case CTNIL:
|
||||
n.Type = types.Types[TNIL]
|
||||
}
|
||||
|
||||
n.Type = idealType(v.Ctype())
|
||||
return n
|
||||
}
|
||||
|
||||
func idealType(ct Ctype) *types.Type {
|
||||
switch ct {
|
||||
case CTSTR:
|
||||
return types.Idealstring
|
||||
case CTBOOL:
|
||||
return types.Idealbool
|
||||
case CTINT:
|
||||
return types.Idealint
|
||||
case CTRUNE:
|
||||
return types.Idealrune
|
||||
case CTFLT:
|
||||
return types.Idealfloat
|
||||
case CTCPLX:
|
||||
return types.Idealcomplex
|
||||
case CTNIL:
|
||||
return types.Types[TNIL]
|
||||
}
|
||||
Fatalf("unexpected Ctype: %v", ct)
|
||||
return nil
|
||||
}
|
||||
|
||||
// idealkind returns a constant kind like consttype
|
||||
// but for an arbitrary "ideal" (untyped constant) expression.
|
||||
func idealkind(n *Node) Ctype {
|
||||
|
@ -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] != "" {
|
||||
name := basicnames[t.Etype]
|
||||
if t == types.Idealbool || t == types.Idealstring {
|
||||
name = "untyped " + name
|
||||
switch t {
|
||||
case types.Idealbool:
|
||||
return "untyped bool"
|
||||
case types.Idealstring:
|
||||
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 name
|
||||
return basicnames[t.Etype]
|
||||
}
|
||||
|
||||
if mode == FDbg {
|
||||
|
@ -374,8 +374,6 @@ func (p *importReader) value() (typ *types.Type, v Val) {
|
||||
p.float(&x.Imag, typ)
|
||||
v.U = x
|
||||
}
|
||||
|
||||
typ = idealType(typ)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1407,20 +1407,18 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||
}
|
||||
|
||||
// Determine result type.
|
||||
et := t.Etype
|
||||
switch et {
|
||||
switch t.Etype {
|
||||
case TIDEAL:
|
||||
// result is ideal
|
||||
n.Type = types.Idealfloat
|
||||
case TCOMPLEX64:
|
||||
et = TFLOAT32
|
||||
n.Type = types.Types[TFLOAT32]
|
||||
case TCOMPLEX128:
|
||||
et = TFLOAT64
|
||||
n.Type = types.Types[TFLOAT64]
|
||||
default:
|
||||
yyerror("invalid argument %L for %v", l, n.Op)
|
||||
n.Type = nil
|
||||
return n
|
||||
}
|
||||
n.Type = types.Types[et]
|
||||
|
||||
case OCOMPLEX:
|
||||
ok |= ctxExpr
|
||||
@ -1457,7 +1455,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||
return n
|
||||
|
||||
case TIDEAL:
|
||||
t = types.Types[TIDEAL]
|
||||
t = types.Idealcomplex
|
||||
|
||||
case TFLOAT32:
|
||||
t = types.Types[TCOMPLEX64]
|
||||
@ -2683,20 +2681,20 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
|
||||
// e.g in error messages about wrong arguments to return.
|
||||
func sigrepr(t *types.Type) string {
|
||||
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:
|
||||
return "string"
|
||||
|
||||
case types.Idealbool:
|
||||
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
|
||||
|
@ -334,14 +334,7 @@ func typeinit() {
|
||||
maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64]
|
||||
minfltval[TCOMPLEX128] = minfltval[TFLOAT64]
|
||||
|
||||
// for walk to use in error messages
|
||||
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)
|
||||
types.Types[TINTER] = types.New(TINTER) // empty interface
|
||||
|
||||
// simple aliases
|
||||
simtype[TMAP] = TPTR
|
||||
|
@ -53,6 +53,13 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
|
||||
assumedEqual[typePair{t1, t2}] = struct{}{}
|
||||
|
||||
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:
|
||||
if t1.NumFields() != t2.NumFields() {
|
||||
return false
|
||||
|
@ -57,7 +57,7 @@ const (
|
||||
TUNSAFEPTR
|
||||
|
||||
// pseudo-types for literals
|
||||
TIDEAL
|
||||
TIDEAL // untyped numeric constants
|
||||
TNIL
|
||||
TBLANK
|
||||
|
||||
@ -94,7 +94,6 @@ const (
|
||||
// It also stores pointers to several special types:
|
||||
// - Types[TANY] is the placeholder "any" type recognized by substArgTypes.
|
||||
// - Types[TBLANK] represents the blank variable's type.
|
||||
// - Types[TIDEAL] represents untyped numeric constants.
|
||||
// - Types[TNIL] represents the predeclared "nil" value's type.
|
||||
// - Types[TUNSAFEPTR] is package unsafe's Pointer type.
|
||||
var Types [NTYPE]*Type
|
||||
@ -112,8 +111,6 @@ var (
|
||||
Idealbool *Type
|
||||
|
||||
// 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)
|
||||
Idealrune = New(TIDEAL)
|
||||
Idealfloat = New(TIDEAL)
|
||||
|
@ -19,7 +19,7 @@ func F() {
|
||||
const x = 1
|
||||
|
||||
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:
|
||||
}
|
||||
}
|
||||
|
46
test/fixedbugs/issue21979.go
Normal file
46
test/fixedbugs/issue21979.go
Normal file
@ -0,0 +1,46 @@
|
||||
// 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
|
||||
func main() {
|
||||
_ = copy(nil, []int{}) // 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"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user