diff options
Diffstat (limited to 'src/cmd')
| -rw-r--r-- | src/cmd/compile/internal/gc/const.go | 470 | ||||
| -rw-r--r-- | src/cmd/compile/internal/gc/ssa.go | 18 | ||||
| -rw-r--r-- | src/cmd/compile/internal/gc/subr.go | 13 | ||||
| -rw-r--r-- | src/cmd/compile/internal/gc/typecheck.go | 22 | ||||
| -rw-r--r-- | src/cmd/compile/internal/types/type.go | 9 |
5 files changed, 222 insertions, 310 deletions
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,223 +204,207 @@ 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 -) - -// 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 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) + } -// 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 } - 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 + // 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 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 - } + 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: + Fatalf("unexpected untyped expression: %v", 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) + v := convertVal(n.Val(), t, explicit) + if v.U == nil { + break } + n.SetVal(v) + n.Type = t + return n - 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())) - } - if t != nil && !t.IsInteger() { - yyerror("invalid operation: %v (shift of type %v)", n, t) - t = nil + case OPLUS, ONEG, OBITNOT, ONOT, OREAL, OIMAG: + ot := operandType(n.Op, t) + if ot == nil { + n = defaultlit(n, nil) + break } + n.Left = convlit(n.Left, ot) + if n.Left.Type == nil { + 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 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 + } - case TCOMPLEX64: - n.Type = t - n.Left = convlit(n.Left, types.Types[TFLOAT32]) - n.Right = convlit(n.Right, types.Types[TFLOAT32]) - } + 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 - } - // avoid repeated calculations, errors - if types.Identical(n.Type, t) { + case OEQ, ONE, OLT, OLE, OGT, OGE: + if !t.IsBoolean() { + break + } + n.Type = t return n - } - ct := consttype(n) - var et types.EType - if ct == 0 { - 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 } - et = t.Etype - if et == TINTER { - if ct == CTNIL && n.Type == types.Types[TNIL] { - n.Type = t - return n + if !n.Diag() { + if !t.Broke() { + 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) + } } - return defaultlitreuse(n, nil, reuse) + n.SetDiag(true) } + n.Type = nil + return n +} - switch ct { +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: - 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 + if okfor[op][t.Etype] { + return t } + } + return nil +} - case CTSTR, CTBOOL: - if et != n.Type.Etype { - goto bad +// 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 CTINT, CTRUNE, CTFLT, CTCPLX: - if n.Type.Etype == TUNSAFEPTR && t.Etype != TUINTPTR { - goto bad + case CTSTR: + if t.IsString() { + return v } - 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 CTINT, CTRUNE: + if explicit && t.IsString() { + return tostr(v) } - } - - n.Type = t - return n - -bad: - if !n.Diag() { - if !t.Broke() { - yyerror("cannot convert %L to type %v", n, t) + 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 } - n.SetDiag(true) } - if n.Type.IsUntyped() { - n = defaultlitreuse(n, nil, reuse) - } - return n + return Val{} } func tocplx(v Val) Val { @@ -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<<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; // if they're both ideal going in they better // get the same type going out. @@ -1248,37 +1135,46 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) { return l, r } - if l.Type.IsBoolean() { - l = convlit(l, types.Types[TBOOL]) - r = convlit(r, types.Types[TBOOL]) - } - - lkind := idealkind(l) - rkind := idealkind(r) - if lkind == CTCPLX || rkind == CTCPLX { - l = convlit(l, types.Types[TCOMPLEX128]) - r = convlit(r, types.Types[TCOMPLEX128]) + // Can't mix bool with non-bool, string with non-string, or nil with anything (untyped). + if l.Type.IsBoolean() != r.Type.IsBoolean() { return l, r } - - if lkind == CTFLT || rkind == CTFLT { - l = convlit(l, types.Types[TFLOAT64]) - r = convlit(r, types.Types[TFLOAT64]) + if l.Type.IsString() != r.Type.IsString() { return l, r } - - if lkind == CTRUNE || rkind == CTRUNE { - l = convlit(l, types.Runetype) - r = convlit(r, types.Runetype) + if l.isNil() || r.isNil() { return l, r } - l = convlit(l, types.Types[TINT]) - r = convlit(r, types.Types[TINT]) - + k := idealkind(l) + if rk := idealkind(r); rk > k { + k = rk + } + 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 } |
