diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index d8e68bf25d..47601ecf5f 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -204,225 +204,209 @@ func trunccmplxlit(oldv *Mpcplx, t *types.Type) *Mpcplx { return cv } -// canReuseNode indicates whether it is known to be safe -// to reuse a Node. -type canReuseNode bool +// TODO(mdempsky): Replace these with better APIs. +func convlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) } +func defaultlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) } -const ( - noReuse canReuseNode = false // not necessarily safe to reuse - reuseOK canReuseNode = true // safe to reuse -) +// convlit1 converts an untyped expression n to type t. If n already +// has a type, convlit1 has no effect. +// +// For explicit conversions, t must be non-nil, and integer-to-string +// conversions are allowed. +// +// For implicit conversions (e.g., assignments), t may be nil; if so, +// n is converted to its default type. +// +// If there's an error converting n to t, context is used in the error +// message. +func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Node { + if explicit && t == nil { + Fatalf("explicit conversion missing type") + } + if t != nil && t.IsUntyped() { + Fatalf("bad conversion to untyped: %v", t) + } -// convert n, if literal, to type t. -// implicit conversion. -// The result of convlit MUST be assigned back to n, e.g. -// n.Left = convlit(n.Left, t) -func convlit(n *Node, t *types.Type) *Node { - return convlit1(n, t, false, noReuse) -} - -// convlit1 converts n, if literal, to type t. -// It returns a new node if necessary. -// The result of convlit1 MUST be assigned back to n, e.g. -// n.Left = convlit1(n.Left, t, explicit, reuse) -func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node { - if n == nil || t == nil || n.Type == nil || t.IsUntyped() || n.Type == t { + if n == nil || n.Type == nil { + // Allow sloppy callers. return n } - if !explicit && !n.Type.IsUntyped() { + if !n.Type.IsUntyped() { + // Already typed; nothing to do. return n } - if n.Op == OLITERAL && !reuse { + if n.Op == OLITERAL { // Can't always set n.Type directly on OLITERAL nodes. // See discussion on CL 20813. n = n.rawcopy() - reuse = true + } + + // Nil is technically not a constant, so handle it specially. + if n.Type.Etype == TNIL { + if t == nil { + yyerror("use of untyped nil") + n.SetDiag(true) + n.Type = nil + return n + } + + if !t.HasNil() { + // Leave for caller to handle. + return n + } + + n.Type = t + return n + } + + if t == nil || !okforconst[t.Etype] { + t = defaultType(idealkind(n)) } switch n.Op { default: - if n.Type == types.Idealbool { - if !t.IsBoolean() { - t = types.Types[TBOOL] - } - switch n.Op { - case ONOT: - n.Left = convlit(n.Left, t) - case OANDAND, OOROR: - n.Left = convlit(n.Left, t) - n.Right = convlit(n.Right, t) - } - n.Type = t - } + Fatalf("unexpected untyped expression: %v", n) - if n.Type.IsUntyped() { - if t.IsInterface() { - n.Left, n.Right = defaultlit2(n.Left, n.Right, true) - n.Type = n.Left.Type // same as n.Right.Type per defaultlit2 - } else { - n.Left = convlit(n.Left, t) - n.Right = convlit(n.Right, t) - n.Type = t - } + case OLITERAL: + v := convertVal(n.Val(), t, explicit) + if v.U == nil { + break } - + n.SetVal(v) + n.Type = t return n - // target is invalid type for a constant? leave alone. - case OLITERAL: - if !okforconst[t.Etype] && n.Type.Etype != TNIL { - return defaultlitreuse(n, nil, reuse) + case OPLUS, ONEG, OBITNOT, ONOT, OREAL, OIMAG: + ot := operandType(n.Op, t) + if ot == nil { + n = defaultlit(n, nil) + break } - case OLSH, ORSH: - n.Left = convlit1(n.Left, t, explicit && n.Left.Type.IsUntyped(), noReuse) - t = n.Left.Type - if t != nil && t.Etype == TIDEAL && n.Val().Ctype() != CTINT { - n.SetVal(toint(n.Val())) + n.Left = convlit(n.Left, ot) + if n.Left.Type == nil { + n.Type = nil + return n } - if t != nil && !t.IsInteger() { - yyerror("invalid operation: %v (shift of type %v)", n, t) - t = nil + n.Type = t + return n + + case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND, OCOMPLEX: + ot := operandType(n.Op, t) + if ot == nil { + n = defaultlit(n, nil) + break + } + + n.Left = convlit(n.Left, ot) + n.Right = convlit(n.Right, ot) + if n.Left.Type == nil || n.Right.Type == nil { + n.Type = nil + return n + } + if !types.Identical(n.Left.Type, n.Right.Type) { + yyerror("invalid operation: %v (mismatched types %v and %v)", n, n.Left.Type, n.Right.Type) + n.Type = nil + return n } n.Type = t return n - case OCOMPLEX: - if n.Type.Etype == TIDEAL { - switch t.Etype { - default: - // If trying to convert to non-complex type, - // leave as complex128 and let typechecker complain. - t = types.Types[TCOMPLEX128] - fallthrough - case types.TCOMPLEX128: - n.Type = t - n.Left = convlit(n.Left, types.Types[TFLOAT64]) - n.Right = convlit(n.Right, types.Types[TFLOAT64]) - - case TCOMPLEX64: - n.Type = t - n.Left = convlit(n.Left, types.Types[TFLOAT32]) - n.Right = convlit(n.Right, types.Types[TFLOAT32]) - } - } - - return n - } - - // avoid repeated calculations, errors - if types.Identical(n.Type, t) { - return n - } - - ct := consttype(n) - var et types.EType - if ct == 0 { - goto bad - } - - et = t.Etype - if et == TINTER { - if ct == CTNIL && n.Type == types.Types[TNIL] { - n.Type = t - return n - } - return defaultlitreuse(n, nil, reuse) - } - - switch ct { - default: - goto bad - - case CTNIL: - switch et { - default: - n.Type = nil - goto bad - - // let normal conversion code handle it - case TSTRING: - return n - - case TARRAY: - goto bad - - case TCHAN, TFUNC, TINTER, TMAP, TPTR, TSLICE, TUNSAFEPTR: + case OEQ, ONE, OLT, OLE, OGT, OGE: + if !t.IsBoolean() { break } + n.Type = t + return n - case CTSTR, CTBOOL: - if et != n.Type.Etype { - goto bad - } - - case CTINT, CTRUNE, CTFLT, CTCPLX: - if n.Type.Etype == TUNSAFEPTR && t.Etype != TUINTPTR { - goto bad - } - ct := n.Val().Ctype() - if isInt[et] { - switch ct { - default: - goto bad - - case CTCPLX, CTFLT, CTRUNE: - n.SetVal(toint(n.Val())) - fallthrough - - case CTINT: - overflow(n.Val(), t) - } - } else if isFloat[et] { - switch ct { - default: - goto bad - - case CTCPLX, CTINT, CTRUNE: - n.SetVal(toflt(n.Val())) - fallthrough - - case CTFLT: - n.SetVal(Val{truncfltlit(n.Val().U.(*Mpflt), t)}) - } - } else if isComplex[et] { - switch ct { - default: - goto bad - - case CTFLT, CTINT, CTRUNE: - n.SetVal(tocplx(n.Val())) - fallthrough - - case CTCPLX: - n.SetVal(Val{trunccmplxlit(n.Val().U.(*Mpcplx), t)}) - } - } else if et == types.TSTRING && (ct == CTINT || ct == CTRUNE) && explicit { - n.SetVal(tostr(n.Val())) - } else { - goto bad + case OLSH, ORSH: + n.Left = convlit1(n.Left, t, explicit, nil) + n.Type = n.Left.Type + if n.Type != nil && !n.Type.IsInteger() { + yyerror("invalid operation: %v (shift of type %v)", n, n.Type) + n.Type = nil } + return n } - n.Type = t - return n - -bad: if !n.Diag() { if !t.Broke() { - yyerror("cannot convert %L to type %v", n, t) + if explicit { + yyerror("cannot convert %L to type %v", n, t) + } else if context != nil { + yyerror("cannot use %L as type %v in %s", n, t, context()) + } else { + yyerror("cannot use %L as type %v", n, t) + } } n.SetDiag(true) } - - if n.Type.IsUntyped() { - n = defaultlitreuse(n, nil, reuse) - } + n.Type = nil return n } +func operandType(op Op, t *types.Type) *types.Type { + switch op { + case OCOMPLEX: + if t.IsComplex() { + return floatForComplex(t) + } + case OREAL, OIMAG: + if t.IsFloat() { + return complexForFloat(t) + } + default: + if okfor[op][t.Etype] { + return t + } + } + return nil +} + +// convertVal converts v into a representation appropriate for t. If +// no such representation exists, it returns Val{} instead. +// +// If explicit is true, then conversions from integer to string are +// also allowed. +func convertVal(v Val, t *types.Type, explicit bool) Val { + switch ct := v.Ctype(); ct { + case CTBOOL: + if t.IsBoolean() { + return v + } + + case CTSTR: + if t.IsString() { + return v + } + + case CTINT, CTRUNE: + if explicit && t.IsString() { + return tostr(v) + } + fallthrough + case CTFLT, CTCPLX: + switch { + case t.IsInteger(): + v = toint(v) + overflow(v, t) + return v + case t.IsFloat(): + v = toflt(v) + v = Val{truncfltlit(v.U.(*Mpflt), t)} + return v + case t.IsComplex(): + v = tocplx(v) + v = Val{trunccmplxlit(v.U.(*Mpcplx), t)} + return v + } + } + + return Val{} +} + func tocplx(v Val) Val { switch u := v.U.(type) { case *Mpint: @@ -609,8 +593,7 @@ func evconst(n *Node) { case OCONV: if okforconst[n.Type.Etype] && nl.Op == OLITERAL { - // TODO(mdempsky): There should be a convval function. - setconst(n, convlit1(nl, n.Type, true, false).Val()) + setconst(n, convertVal(nl.Val(), n.Type, true)) } case OCONVNOP: @@ -1128,102 +1111,6 @@ func idealkind(n *Node) Ctype { } } -// The result of defaultlit MUST be assigned back to n, e.g. -// n.Left = defaultlit(n.Left, t) -func defaultlit(n *Node, t *types.Type) *Node { - return defaultlitreuse(n, t, noReuse) -} - -// The result of defaultlitreuse MUST be assigned back to n, e.g. -// n.Left = defaultlitreuse(n.Left, t, reuse) -func defaultlitreuse(n *Node, t *types.Type, reuse canReuseNode) *Node { - if n == nil || !n.Type.IsUntyped() { - return n - } - - if n.Op == OLITERAL && !reuse { - n = n.rawcopy() - reuse = true - } - - lno := setlineno(n) - ctype := idealkind(n) - var t1 *types.Type - switch ctype { - default: - if t != nil { - n = convlit(n, t) - lineno = lno - return n - } - - switch n.Val().Ctype() { - case CTNIL: - lineno = lno - if !n.Diag() { - yyerror("use of untyped nil") - n.SetDiag(true) - } - - n.Type = nil - case CTSTR: - t1 := types.Types[TSTRING] - n = convlit1(n, t1, false, reuse) - default: - yyerror("defaultlit: unknown literal: %v", n) - } - lineno = lno - return n - - case CTxxx: - Fatalf("defaultlit: idealkind is CTxxx: %+v", n) - - case CTBOOL: - t1 := types.Types[TBOOL] - if t != nil && t.IsBoolean() { - t1 = t - } - n = convlit1(n, t1, false, reuse) - lineno = lno - return n - - case CTINT: - t1 = types.Types[TINT] - case CTRUNE: - t1 = types.Runetype - case CTFLT: - t1 = types.Types[TFLOAT64] - case CTCPLX: - t1 = types.Types[TCOMPLEX128] - } - - // Note: n.Val().Ctype() can be CTxxx (not a constant) here - // in the case of an untyped non-constant value, like 1< k { + k = rk } - - if lkind == CTRUNE || rkind == CTRUNE { - l = convlit(l, types.Runetype) - r = convlit(r, types.Runetype) - return l, r - } - - l = convlit(l, types.Types[TINT]) - r = convlit(r, types.Types[TINT]) - + t := defaultType(k) + l = convlit(l, t) + r = convlit(r, t) return l, r } +func defaultType(k Ctype) *types.Type { + switch k { + case CTBOOL: + return types.Types[TBOOL] + case CTSTR: + return types.Types[TSTRING] + case CTINT: + return types.Types[TINT] + case CTRUNE: + return types.Runetype + case CTFLT: + return types.Types[TFLOAT64] + case CTCPLX: + return types.Types[TCOMPLEX128] + } + Fatalf("bad idealkind: %v", k) + return nil +} + // strlit returns the value of a literal string Node as a string. func strlit(n *Node) string { return n.Val().U.(string) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 0706d95937..6d70cdbdd0 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -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 { diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 8c72a5928c..b4be5dcbfb 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -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 } @@ -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 } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index a18470ea98..e4d1cedd74 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -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 @@ -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() @@ -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: @@ -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 @@ -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++ @@ -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") } diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 2c8409b3b3..2fcd6057f3 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -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 } diff --git a/test/convlit.go b/test/convlit.go index 904e1e63b1..de760542da 100644 --- a/test/convlit.go +++ b/test/convlit.go @@ -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" diff --git a/test/ddd1.go b/test/ddd1.go index b4ad80b687..b582f221b7 100644 --- a/test/ddd1.go +++ b/test/ddd1.go @@ -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" ) diff --git a/test/fixedbugs/issue17645.go b/test/fixedbugs/issue17645.go index ed92c54cfa..af785eae2a 100644 --- a/test/fixedbugs/issue17645.go +++ b/test/fixedbugs/issue17645.go @@ -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" } - diff --git a/test/fixedbugs/issue7153.go b/test/fixedbugs/issue7153.go index f238f78e84..215387732b 100644 --- a/test/fixedbugs/issue7153.go +++ b/test/fixedbugs/issue7153.go @@ -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" diff --git a/test/fixedbugs/issue7310.go b/test/fixedbugs/issue7310.go index 1169fcfbf0..5ae0f1f528 100644 --- a/test/fixedbugs/issue7310.go +++ b/test/fixedbugs/issue7310.go @@ -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" } diff --git a/test/fixedbugs/issue8438.go b/test/fixedbugs/issue8438.go index b28025cdac..3a4f193b57 100644 --- a/test/fixedbugs/issue8438.go +++ b/test/fixedbugs/issue8438.go @@ -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 } diff --git a/test/rename1.go b/test/rename1.go index 568aa13d44..c49a70a263 100644 --- a/test/rename1.go +++ b/test/rename1.go @@ -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" ) } diff --git a/test/shift1.go b/test/shift1.go index 01ecbed53a..df0c032cd5 100644 --- a/test/shift1.go +++ b/test/shift1.go @@ -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" ) @@ -66,6 +66,7 @@ func _() { u2 = 1<