mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: rewrite untyped constant conversion logic
This CL detangles the hairy mess that was convlit+defaultlit. In particular, it makes the following changes: 1. convlit1 now follows the standard typecheck behavior of setting "n.Type = nil" if there's an error. Notably, this means for a lot of test cases, we now avoid reporting useless follow-on error messages. For example, after reporting that "1 << s + 1.0" has an invalid shift, we no longer also report that it can't be assigned to string. 2. Previously, assignconvfn had some extra logic for trying to suppress errors from convlit/defaultlit so that it could provide its own errors with better context information. Instead, this extra context information is now passed down into convlit1 directly. 3. Relatedly, this CL also removes redundant calls to defaultlit prior to assignconv. As a consequence, when an expression doesn't make sense for a particular assignment (e.g., assigning an untyped string to an integer), the error messages now say "untyped string" instead of just "string". This is more consistent with go/types behavior. 4. defaultlit2 is now smarter about only trying to convert pairs of untyped constants when it's likely to succeed. This allows us to report better error messages for things like 3+"x"; instead of "cannot convert 3 to string" we now report "mismatched types untyped number and untyped string". Passes toolstash-check. Change-Id: I26822a02dc35855bd0ac774907b1cf5737e91882 Reviewed-on: https://go-review.googlesource.com/c/go/+/187657 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
ad1f2c9618
commit
581526ce96
13 changed files with 249 additions and 337 deletions
|
|
@ -204,225 +204,209 @@ func trunccmplxlit(oldv *Mpcplx, t *types.Type) *Mpcplx {
|
||||||
return cv
|
return cv
|
||||||
}
|
}
|
||||||
|
|
||||||
// canReuseNode indicates whether it is known to be safe
|
// TODO(mdempsky): Replace these with better APIs.
|
||||||
// to reuse a Node.
|
func convlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) }
|
||||||
type canReuseNode bool
|
func defaultlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) }
|
||||||
|
|
||||||
const (
|
// convlit1 converts an untyped expression n to type t. If n already
|
||||||
noReuse canReuseNode = false // not necessarily safe to reuse
|
// has a type, convlit1 has no effect.
|
||||||
reuseOK canReuseNode = true // safe to reuse
|
//
|
||||||
)
|
// 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.
|
if n == nil || n.Type == nil {
|
||||||
// implicit conversion.
|
// Allow sloppy callers.
|
||||||
// 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 {
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
if !explicit && !n.Type.IsUntyped() {
|
if !n.Type.IsUntyped() {
|
||||||
|
// Already typed; nothing to do.
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.Op == OLITERAL && !reuse {
|
if n.Op == OLITERAL {
|
||||||
// Can't always set n.Type directly on OLITERAL nodes.
|
// Can't always set n.Type directly on OLITERAL nodes.
|
||||||
// See discussion on CL 20813.
|
// See discussion on CL 20813.
|
||||||
n = n.rawcopy()
|
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 {
|
switch n.Op {
|
||||||
default:
|
default:
|
||||||
if n.Type == types.Idealbool {
|
Fatalf("unexpected untyped expression: %v", n)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.Type.IsUntyped() {
|
case OLITERAL:
|
||||||
if t.IsInterface() {
|
v := convertVal(n.Val(), t, explicit)
|
||||||
n.Left, n.Right = defaultlit2(n.Left, n.Right, true)
|
if v.U == nil {
|
||||||
n.Type = n.Left.Type // same as n.Right.Type per defaultlit2
|
break
|
||||||
} else {
|
|
||||||
n.Left = convlit(n.Left, t)
|
|
||||||
n.Right = convlit(n.Right, t)
|
|
||||||
n.Type = t
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
n.SetVal(v)
|
||||||
|
n.Type = t
|
||||||
return n
|
return n
|
||||||
|
|
||||||
// target is invalid type for a constant? leave alone.
|
case OPLUS, ONEG, OBITNOT, ONOT, OREAL, OIMAG:
|
||||||
case OLITERAL:
|
ot := operandType(n.Op, t)
|
||||||
if !okforconst[t.Etype] && n.Type.Etype != TNIL {
|
if ot == nil {
|
||||||
return defaultlitreuse(n, nil, reuse)
|
n = defaultlit(n, nil)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case OLSH, ORSH:
|
n.Left = convlit(n.Left, ot)
|
||||||
n.Left = convlit1(n.Left, t, explicit && n.Left.Type.IsUntyped(), noReuse)
|
if n.Left.Type == nil {
|
||||||
t = n.Left.Type
|
n.Type = nil
|
||||||
if t != nil && t.Etype == TIDEAL && n.Val().Ctype() != CTINT {
|
return n
|
||||||
n.SetVal(toint(n.Val()))
|
|
||||||
}
|
}
|
||||||
if t != nil && !t.IsInteger() {
|
n.Type = t
|
||||||
yyerror("invalid operation: %v (shift of type %v)", n, t)
|
return n
|
||||||
t = nil
|
|
||||||
|
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
|
n.Type = t
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case OCOMPLEX:
|
case OEQ, ONE, OLT, OLE, OGT, OGE:
|
||||||
if n.Type.Etype == TIDEAL {
|
if !t.IsBoolean() {
|
||||||
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:
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
n.Type = t
|
||||||
|
return n
|
||||||
|
|
||||||
case CTSTR, CTBOOL:
|
case OLSH, ORSH:
|
||||||
if et != n.Type.Etype {
|
n.Left = convlit1(n.Left, t, explicit, nil)
|
||||||
goto bad
|
n.Type = n.Left.Type
|
||||||
}
|
if n.Type != nil && !n.Type.IsInteger() {
|
||||||
|
yyerror("invalid operation: %v (shift of type %v)", n, n.Type)
|
||||||
case CTINT, CTRUNE, CTFLT, CTCPLX:
|
n.Type = nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Type = t
|
|
||||||
return n
|
|
||||||
|
|
||||||
bad:
|
|
||||||
if !n.Diag() {
|
if !n.Diag() {
|
||||||
if !t.Broke() {
|
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)
|
n.SetDiag(true)
|
||||||
}
|
}
|
||||||
|
n.Type = nil
|
||||||
if n.Type.IsUntyped() {
|
|
||||||
n = defaultlitreuse(n, nil, reuse)
|
|
||||||
}
|
|
||||||
return n
|
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 {
|
func tocplx(v Val) Val {
|
||||||
switch u := v.U.(type) {
|
switch u := v.U.(type) {
|
||||||
case *Mpint:
|
case *Mpint:
|
||||||
|
|
@ -609,8 +593,7 @@ func evconst(n *Node) {
|
||||||
|
|
||||||
case OCONV:
|
case OCONV:
|
||||||
if okforconst[n.Type.Etype] && nl.Op == OLITERAL {
|
if okforconst[n.Type.Etype] && nl.Op == OLITERAL {
|
||||||
// TODO(mdempsky): There should be a convval function.
|
setconst(n, convertVal(nl.Val(), n.Type, true))
|
||||||
setconst(n, convlit1(nl, n.Type, true, false).Val())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case OCONVNOP:
|
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<<i.
|
|
||||||
v1 := n.Val()
|
|
||||||
if t != nil {
|
|
||||||
if t.IsInteger() {
|
|
||||||
t1 = t
|
|
||||||
v1 = toint(n.Val())
|
|
||||||
} else if t.IsFloat() {
|
|
||||||
t1 = t
|
|
||||||
v1 = toflt(n.Val())
|
|
||||||
} else if t.IsComplex() {
|
|
||||||
t1 = t
|
|
||||||
v1 = tocplx(n.Val())
|
|
||||||
}
|
|
||||||
if n.Val().Ctype() != CTxxx {
|
|
||||||
n.SetVal(v1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.Val().Ctype() != CTxxx {
|
|
||||||
overflow(n.Val(), t1)
|
|
||||||
}
|
|
||||||
n = convlit1(n, t1, false, reuse)
|
|
||||||
lineno = lno
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultlit on both nodes simultaneously;
|
// defaultlit on both nodes simultaneously;
|
||||||
// if they're both ideal going in they better
|
// if they're both ideal going in they better
|
||||||
// get the same type going out.
|
// get the same type going out.
|
||||||
|
|
@ -1248,37 +1135,46 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) {
|
||||||
return l, r
|
return l, r
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.Type.IsBoolean() {
|
// Can't mix bool with non-bool, string with non-string, or nil with anything (untyped).
|
||||||
l = convlit(l, types.Types[TBOOL])
|
if l.Type.IsBoolean() != r.Type.IsBoolean() {
|
||||||
r = convlit(r, types.Types[TBOOL])
|
return l, r
|
||||||
}
|
}
|
||||||
|
if l.Type.IsString() != r.Type.IsString() {
|
||||||
lkind := idealkind(l)
|
return l, r
|
||||||
rkind := idealkind(r)
|
}
|
||||||
if lkind == CTCPLX || rkind == CTCPLX {
|
if l.isNil() || r.isNil() {
|
||||||
l = convlit(l, types.Types[TCOMPLEX128])
|
|
||||||
r = convlit(r, types.Types[TCOMPLEX128])
|
|
||||||
return l, r
|
return l, r
|
||||||
}
|
}
|
||||||
|
|
||||||
if lkind == CTFLT || rkind == CTFLT {
|
k := idealkind(l)
|
||||||
l = convlit(l, types.Types[TFLOAT64])
|
if rk := idealkind(r); rk > k {
|
||||||
r = convlit(r, types.Types[TFLOAT64])
|
k = rk
|
||||||
return l, r
|
|
||||||
}
|
}
|
||||||
|
t := defaultType(k)
|
||||||
if lkind == CTRUNE || rkind == CTRUNE {
|
l = convlit(l, t)
|
||||||
l = convlit(l, types.Runetype)
|
r = convlit(r, t)
|
||||||
r = convlit(r, types.Runetype)
|
|
||||||
return l, r
|
|
||||||
}
|
|
||||||
|
|
||||||
l = convlit(l, types.Types[TINT])
|
|
||||||
r = convlit(r, types.Types[TINT])
|
|
||||||
|
|
||||||
return l, r
|
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.
|
// strlit returns the value of a literal string Node as a string.
|
||||||
func strlit(n *Node) string {
|
func strlit(n *Node) string {
|
||||||
return n.Val().U.(string)
|
return n.Val().U.(string)
|
||||||
|
|
|
||||||
|
|
@ -1549,11 +1549,25 @@ func (s *state) ssaOp(op Op, t *types.Type) ssa.Op {
|
||||||
}
|
}
|
||||||
|
|
||||||
func floatForComplex(t *types.Type) *types.Type {
|
func floatForComplex(t *types.Type) *types.Type {
|
||||||
if t.Size() == 8 {
|
switch t.Etype {
|
||||||
|
case TCOMPLEX64:
|
||||||
return types.Types[TFLOAT32]
|
return types.Types[TFLOAT32]
|
||||||
} else {
|
case TCOMPLEX128:
|
||||||
return types.Types[TFLOAT64]
|
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 {
|
type opAndTwoTypes struct {
|
||||||
|
|
|
||||||
|
|
@ -798,11 +798,10 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
|
||||||
yyerror("use of untyped nil")
|
yyerror("use of untyped nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
old := n
|
n = convlit1(n, t, false, context)
|
||||||
od := old.Diag()
|
if n.Type == nil {
|
||||||
old.SetDiag(true) // silence errors about n; we'll issue one below
|
return n
|
||||||
n = defaultlit(n, t)
|
}
|
||||||
old.SetDiag(od)
|
|
||||||
if t.Etype == TBLANK {
|
if t.Etype == TBLANK {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
@ -826,9 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
|
||||||
var why string
|
var why string
|
||||||
op := assignop(n.Type, t, &why)
|
op := assignop(n.Type, t, &why)
|
||||||
if op == 0 {
|
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
|
op = OCONV
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -709,7 +709,11 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
|
|
||||||
if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
|
if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
|
||||||
l, r = defaultlit2(l, r, true)
|
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)
|
yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
|
|
@ -1049,10 +1053,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case TMAP:
|
case TMAP:
|
||||||
n.Right = defaultlit(n.Right, t.Key())
|
n.Right = assignconv(n.Right, t.Key(), "map index")
|
||||||
if n.Right.Type != nil {
|
|
||||||
n.Right = assignconv(n.Right, t.Key(), "map index")
|
|
||||||
}
|
|
||||||
n.Type = t.Elem()
|
n.Type = t.Elem()
|
||||||
n.Op = OINDEXMAP
|
n.Op = OINDEXMAP
|
||||||
n.ResetAux()
|
n.ResetAux()
|
||||||
|
|
@ -1104,13 +1105,11 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Right = defaultlit(n.Right, t.Elem())
|
n.Right = assignconv(n.Right, t.Elem(), "send")
|
||||||
r := n.Right
|
if n.Right.Type == nil {
|
||||||
if r.Type == nil {
|
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
n.Right = assignconv(r, t.Elem(), "send")
|
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
|
|
||||||
case OSLICEHEADER:
|
case OSLICEHEADER:
|
||||||
|
|
@ -1638,7 +1637,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
ok |= ctxExpr
|
ok |= ctxExpr
|
||||||
checkwidth(n.Type) // ensure width is calculated for backend
|
checkwidth(n.Type) // ensure width is calculated for backend
|
||||||
n.Left = typecheck(n.Left, ctxExpr)
|
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
|
t := n.Left.Type
|
||||||
if t == nil || n.Type == nil {
|
if t == nil || n.Type == nil {
|
||||||
n.Type = nil
|
n.Type = nil
|
||||||
|
|
@ -2862,7 +2861,6 @@ func typecheckcomplit(n *Node) (res *Node) {
|
||||||
r := *vp
|
r := *vp
|
||||||
pushtype(r, t.Elem())
|
pushtype(r, t.Elem())
|
||||||
r = typecheck(r, ctxExpr)
|
r = typecheck(r, ctxExpr)
|
||||||
r = defaultlit(r, t.Elem())
|
|
||||||
*vp = assignconv(r, t.Elem(), "array or slice literal")
|
*vp = assignconv(r, t.Elem(), "array or slice literal")
|
||||||
|
|
||||||
i++
|
i++
|
||||||
|
|
@ -2900,14 +2898,12 @@ func typecheckcomplit(n *Node) (res *Node) {
|
||||||
r := l.Left
|
r := l.Left
|
||||||
pushtype(r, t.Key())
|
pushtype(r, t.Key())
|
||||||
r = typecheck(r, ctxExpr)
|
r = typecheck(r, ctxExpr)
|
||||||
r = defaultlit(r, t.Key())
|
|
||||||
l.Left = assignconv(r, t.Key(), "map key")
|
l.Left = assignconv(r, t.Key(), "map key")
|
||||||
cs.add(lineno, l.Left, "key", "map literal")
|
cs.add(lineno, l.Left, "key", "map literal")
|
||||||
|
|
||||||
r = l.Right
|
r = l.Right
|
||||||
pushtype(r, t.Elem())
|
pushtype(r, t.Elem())
|
||||||
r = typecheck(r, ctxExpr)
|
r = typecheck(r, ctxExpr)
|
||||||
r = defaultlit(r, t.Elem())
|
|
||||||
l.Right = assignconv(r, t.Elem(), "map value")
|
l.Right = assignconv(r, t.Elem(), "map value")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1281,6 +1281,15 @@ func (t *Type) IsPtrShaped() bool {
|
||||||
t.Etype == TMAP || t.Etype == TCHAN || t.Etype == TFUNC
|
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 {
|
func (t *Type) IsString() bool {
|
||||||
return t.Etype == TSTRING
|
return t.Etype == TSTRING
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ var _ = int(unsafe.Pointer(uintptr(65))) // ERROR "convert"
|
||||||
// implicit conversions merit scrutiny
|
// implicit conversions merit scrutiny
|
||||||
var s string
|
var s string
|
||||||
var bad1 string = 1 // ERROR "conver|incompatible|invalid|cannot"
|
var bad1 string = 1 // ERROR "conver|incompatible|invalid|cannot"
|
||||||
var bad2 = s + 1 // ERROR "conver|incompatible|invalid"
|
var bad2 = s + 1 // ERROR "conver|incompatible|invalid|cannot"
|
||||||
var bad3 = s + 'a' // ERROR "conver|incompatible|invalid"
|
var bad3 = s + 'a' // ERROR "conver|incompatible|invalid|cannot"
|
||||||
var bad4 = "a" + 1 // ERROR "literals|incompatible|convert|invalid"
|
var bad4 = "a" + 1 // ERROR "literals|incompatible|convert|invalid"
|
||||||
var bad5 = "a" + 'a' // ERROR "literals|incompatible|convert|invalid"
|
var bad5 = "a" + 'a' // ERROR "literals|incompatible|convert|invalid"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ var (
|
||||||
_ = sum()
|
_ = sum()
|
||||||
_ = sum(1.0, 2.0)
|
_ = sum(1.0, 2.0)
|
||||||
_ = sum(1.5) // ERROR "integer"
|
_ = 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"
|
_ = sum([]int{1}) // ERROR "\[\]int literal.*as type int|incompatible"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,5 @@ type Foo struct {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var s []int
|
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@
|
||||||
|
|
||||||
package p
|
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"
|
||||||
|
|
|
||||||
|
|
@ -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 "cannot convert true" "mismatched types int and bool"
|
_ = 1 + true // ERROR "mismatched types untyped number and untyped bool"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_ = []byte{"foo"} // ERROR "cannot convert"
|
_ = []byte{"foo"} // ERROR "cannot use"
|
||||||
_ = []int{"foo"} // ERROR "cannot convert"
|
_ = []int{"foo"} // ERROR "cannot use"
|
||||||
_ = []rune{"foo"} // ERROR "cannot convert"
|
_ = []rune{"foo"} // ERROR "cannot use"
|
||||||
_ = []string{"foo"} // OK
|
_ = []string{"foo"} // OK
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ func main() {
|
||||||
var n byte // ERROR "not a type|expected type"
|
var n byte // ERROR "not a type|expected type"
|
||||||
var y = float32(0) // ERROR "cannot call|expected function"
|
var y = float32(0) // ERROR "cannot call|expected function"
|
||||||
const (
|
const (
|
||||||
a = 1 + iota // ERROR "invalid operation|incompatible types" "cannot convert iota"
|
a = 1 + iota // ERROR "invalid operation|incompatible types"
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ func h(x float64) int { return 0 }
|
||||||
var (
|
var (
|
||||||
s uint = 33
|
s uint = 33
|
||||||
u = 1.0 << s // ERROR "invalid operation|shift of non-integer operand"
|
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
|
// non-constant shift expressions
|
||||||
var (
|
var (
|
||||||
e1 = g(2.0 << s) // ERROR "invalid|shift of non-integer operand" "as type interface"
|
e1 = g(2.0 << s) // ERROR "invalid|shift of non-integer operand"
|
||||||
f1 = h(2 << s) // ERROR "invalid" "as type float64"
|
f1 = h(2 << s) // ERROR "invalid"
|
||||||
g1 int64 = 1.1 << s // ERROR "truncated"
|
g1 int64 = 1.1 << s // ERROR "truncated"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -66,6 +66,7 @@ func _() {
|
||||||
u2 = 1<<s != 1.0 // ERROR "non-integer|float64"
|
u2 = 1<<s != 1.0 // ERROR "non-integer|float64"
|
||||||
v float32 = 1 << s // ERROR "non-integer|float32"
|
v float32 = 1 << s // ERROR "non-integer|float32"
|
||||||
w int64 = 1.0 << 33 // 1.0<<33 is a constant shift expression
|
w int64 = 1.0 << 33 // 1.0<<33 is a constant shift expression
|
||||||
|
|
||||||
_, _, _, _, _, _, _, _, _, _ = j, k, m, n, o, u, u1, u2, v, w
|
_, _, _, _, _, _, _, _, _, _ = j, k, m, n, o, u, u1, u2, v, w
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue