]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: report more precise errors about untyped constants
authorMatthew Dempsky <mdempsky@google.com>
Fri, 6 Sep 2019 23:05:36 +0000 (16:05 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Mon, 9 Sep 2019 22:12:15 +0000 (22:12 +0000)
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>
src/cmd/compile/internal/gc/bimport.go
src/cmd/compile/internal/gc/const.go
src/cmd/compile/internal/gc/fmt.go
src/cmd/compile/internal/gc/iimport.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/compile/internal/gc/universe.go
src/cmd/compile/internal/types/identity.go
src/cmd/compile/internal/types/type.go
test/fixedbugs/issue20185.go
test/fixedbugs/issue21979.go [new file with mode: 0644]
test/fixedbugs/issue7310.go

index 7aabae764e7c65d5df9c6a7d9646a2624d8c8294..911ac4c0dc7b6d83e9a5c5cb1b4d5ee67d37673e 100644 (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
index 1cd8488ea1a66ab8a21bf196c699f2b8313392ea..e40c23b8efefb0200e11160b28bfe68b19e22994 100644 (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,24 +1035,29 @@ 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())
+       n.Type = idealType(v.Ctype())
+       return n
+}
 
+func idealType(ct Ctype) *types.Type {
+       switch ct {
        case CTSTR:
-               n.Type = types.Idealstring
-
+               return types.Idealstring
        case CTBOOL:
-               n.Type = types.Idealbool
-
-       case CTINT, CTRUNE, CTFLT, CTCPLX:
-               n.Type = types.Types[TIDEAL]
-
+               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:
-               n.Type = types.Types[TNIL]
+               return types.Types[TNIL]
        }
-
-       return n
+       Fatalf("unexpected Ctype: %v", ct)
+       return nil
 }
 
 // idealkind returns a constant kind like consttype
index 782e4cb8405645da55f163a80d6f3d01f3cc7c72..30b4ebd315de9fa20d7897b03ec64db2ac6c38d3 100644 (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
-               }
-               return 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 basicnames[t.Etype]
        }
 
        if mode == FDbg {
index 1d4329b4b1d47e1d4aee9ed676cb8cce9947f159..7d134f3a5f246d51d33e0be02ed6dc0650ba4bb0 100644 (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
 }
 
index e4d1cedd748604d06d6cc3b5a8f1877f4cabb3af..8518efe73ada6a22dc95fdb3dcce9384dae54da8 100644 (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
index b8260d65256f83dad3e19de3fb0b9267d2948aa5..2077c5639e7439c0a3f287613496a7128fc8ff31 100644 (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
index 7c14a03ba16dd6914372574d34240923d6ede179..a77f514df96bef11730a806da97d251435135e44 100644 (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
index 2fcd6057f3da007fef446fadf939f28fe3e36230..7b2b79c561df8c509b84d9e950e056af4d7be2cb 100644 (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)
index 00c23f64070b6a56ee0247688417c5b05bd64799..2cbb143ed00d58f306afb4697e3bcd65e385c113 100644 (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:
        }
 }
diff --git a/test/fixedbugs/issue21979.go b/test/fixedbugs/issue21979.go
new file mode 100644 (file)
index 0000000..1c02f57
--- /dev/null
@@ -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)
+}
index 5ae0f1f5282f1a292988bf4a6b0f00dac248808b..6829d5e126bf343a956ff9d3ca6c2611c0d5ef8a 100644 (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"
 }