1
0
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:
Matthew Dempsky 2019-09-06 16:05:36 -07:00
parent 51c8d969bd
commit e710a1fb2e
11 changed files with 112 additions and 63 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:
}
}

View 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)
}

View File

@ -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"
}