blob: e7f9ec5cd81a31cd6aa8869cf9e119c2f5dd32c9 [file] [log] [blame]
// Copyright 2009 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 typecheck
import (
"fmt"
"go/constant"
"go/token"
"math"
"math/big"
"unicode"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
)
func roundFloat(v constant.Value, sz int64) constant.Value {
switch sz {
case 4:
f, _ := constant.Float32Val(v)
return makeFloat64(float64(f))
case 8:
f, _ := constant.Float64Val(v)
return makeFloat64(f)
}
base.Fatalf("unexpected size: %v", sz)
panic("unreachable")
}
// truncate float literal fv to 32-bit or 64-bit precision
// according to type; return truncated value.
func truncfltlit(v constant.Value, t *types.Type) constant.Value {
if t.IsUntyped() {
return v
}
return roundFloat(v, t.Size())
}
// truncate Real and Imag parts of Mpcplx to 32-bit or 64-bit
// precision, according to type; return truncated value. In case of
// overflow, calls Errorf but does not truncate the input value.
func trunccmplxlit(v constant.Value, t *types.Type) constant.Value {
if t.IsUntyped() {
return v
}
fsz := t.Size() / 2
return makeComplex(roundFloat(constant.Real(v), fsz), roundFloat(constant.Imag(v), fsz))
}
// TODO(mdempsky): Replace these with better APIs.
func convlit(n ir.Node, t *types.Type) ir.Node { return convlit1(n, t, false, nil) }
func DefaultLit(n ir.Node, t *types.Type) ir.Node { return convlit1(n, t, false, nil) }
// 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 ir.Node, t *types.Type, explicit bool, context func() string) ir.Node {
if explicit && t == nil {
base.Fatalf("explicit conversion missing type")
}
if t != nil && t.IsUntyped() {
base.Fatalf("bad conversion to untyped: %v", t)
}
if n == nil || n.Type() == nil {
// Allow sloppy callers.
return n
}
if !n.Type().IsUntyped() {
// Already typed; nothing to do.
return n
}
// Nil is technically not a constant, so handle it specially.
if n.Type().Kind() == types.TNIL {
if n.Op() != ir.ONIL {
base.Fatalf("unexpected op: %v (%v)", n, n.Op())
}
n = ir.Copy(n)
if t == nil {
base.Fatalf("use of untyped nil")
}
if !t.HasNil() {
// Leave for caller to handle.
return n
}
n.SetType(t)
return n
}
if t == nil || !ir.OKForConst[t.Kind()] {
t = defaultType(n.Type())
}
switch n.Op() {
default:
base.Fatalf("unexpected untyped expression: %v", n)
case ir.OLITERAL:
v := ConvertVal(n.Val(), t, explicit)
if v.Kind() == constant.Unknown {
n = ir.NewConstExpr(n.Val(), n)
break
}
n = ir.NewConstExpr(v, n)
n.SetType(t)
return n
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.OREAL, ir.OIMAG:
ot := operandType(n.Op(), t)
if ot == nil {
n = DefaultLit(n, nil)
break
}
n := n.(*ir.UnaryExpr)
n.X = convlit(n.X, ot)
if n.X.Type() == nil {
n.SetType(nil)
return n
}
n.SetType(t)
return n
case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT, ir.OOROR, ir.OANDAND, ir.OCOMPLEX:
ot := operandType(n.Op(), t)
if ot == nil {
n = DefaultLit(n, nil)
break
}
var l, r ir.Node
switch n := n.(type) {
case *ir.BinaryExpr:
n.X = convlit(n.X, ot)
n.Y = convlit(n.Y, ot)
l, r = n.X, n.Y
case *ir.LogicalExpr:
n.X = convlit(n.X, ot)
n.Y = convlit(n.Y, ot)
l, r = n.X, n.Y
}
if l.Type() == nil || r.Type() == nil {
n.SetType(nil)
return n
}
if !types.Identical(l.Type(), r.Type()) {
base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type())
n.SetType(nil)
return n
}
n.SetType(t)
return n
case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
n := n.(*ir.BinaryExpr)
if !t.IsBoolean() {
break
}
n.SetType(t)
return n
case ir.OLSH, ir.ORSH:
n := n.(*ir.BinaryExpr)
n.X = convlit1(n.X, t, explicit, nil)
n.SetType(n.X.Type())
if n.Type() != nil && !n.Type().IsInteger() {
base.Errorf("invalid operation: %v (shift of type %v)", n, n.Type())
n.SetType(nil)
}
return n
}
if explicit {
base.Fatalf("cannot convert %L to type %v", n, t)
} else if context != nil {
base.Fatalf("cannot use %L as type %v in %s", n, t, context())
} else {
base.Fatalf("cannot use %L as type %v", n, t)
}
n.SetType(nil)
return n
}
func operandType(op ir.Op, t *types.Type) *types.Type {
switch op {
case ir.OCOMPLEX:
if t.IsComplex() {
return types.FloatForComplex(t)
}
case ir.OREAL, ir.OIMAG:
if t.IsFloat() {
return types.ComplexForFloat(t)
}
default:
if okfor[op][t.Kind()] {
return t
}
}
return nil
}
// ConvertVal converts v into a representation appropriate for t. If
// no such representation exists, it returns constant.MakeUnknown()
// instead.
//
// If explicit is true, then conversions from integer to string are
// also allowed.
func ConvertVal(v constant.Value, t *types.Type, explicit bool) constant.Value {
switch ct := v.Kind(); ct {
case constant.Bool:
if t.IsBoolean() {
return v
}
case constant.String:
if t.IsString() {
return v
}
case constant.Int:
if explicit && t.IsString() {
return tostr(v)
}
fallthrough
case constant.Float, constant.Complex:
switch {
case t.IsInteger():
v = toint(v)
return v
case t.IsFloat():
v = toflt(v)
v = truncfltlit(v, t)
return v
case t.IsComplex():
v = tocplx(v)
v = trunccmplxlit(v, t)
return v
}
}
return constant.MakeUnknown()
}
func tocplx(v constant.Value) constant.Value {
return constant.ToComplex(v)
}
func toflt(v constant.Value) constant.Value {
if v.Kind() == constant.Complex {
v = constant.Real(v)
}
return constant.ToFloat(v)
}
func toint(v constant.Value) constant.Value {
if v.Kind() == constant.Complex {
v = constant.Real(v)
}
if v := constant.ToInt(v); v.Kind() == constant.Int {
return v
}
// The value of v cannot be represented as an integer;
// so we need to print an error message.
// Unfortunately some float values cannot be
// reasonably formatted for inclusion in an error
// message (example: 1 + 1e-100), so first we try to
// format the float; if the truncation resulted in
// something that looks like an integer we omit the
// value from the error message.
// (See issue #11371).
f := ir.BigFloat(v)
if f.MantExp(nil) > 2*ir.ConstPrec {
base.Errorf("integer too large")
} else {
var t big.Float
t.Parse(fmt.Sprint(v), 0)
if t.IsInt() {
base.Errorf("constant truncated to integer")
} else {
base.Errorf("constant %v truncated to integer", v)
}
}
// Prevent follow-on errors.
return constant.MakeUnknown()
}
func tostr(v constant.Value) constant.Value {
if v.Kind() == constant.Int {
r := unicode.ReplacementChar
if x, ok := constant.Uint64Val(v); ok && x <= unicode.MaxRune {
r = rune(x)
}
v = constant.MakeString(string(r))
}
return v
}
func makeFloat64(f float64) constant.Value {
if math.IsInf(f, 0) {
base.Fatalf("infinity is not a valid constant")
}
return constant.MakeFloat64(f)
}
func makeComplex(real, imag constant.Value) constant.Value {
return constant.BinaryOp(constant.ToFloat(real), token.ADD, constant.MakeImag(constant.ToFloat(imag)))
}
// DefaultLit on both nodes simultaneously;
// if they're both ideal going in they better
// get the same type going out.
// force means must assign concrete (non-ideal) type.
// The results of defaultlit2 MUST be assigned back to l and r, e.g.
//
// n.Left, n.Right = defaultlit2(n.Left, n.Right, force)
func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
if l.Type() == nil || r.Type() == nil {
return l, r
}
if !l.Type().IsInterface() && !r.Type().IsInterface() {
// Can't mix bool with non-bool, string with non-string.
if l.Type().IsBoolean() != r.Type().IsBoolean() {
return l, r
}
if l.Type().IsString() != r.Type().IsString() {
return l, r
}
}
if !l.Type().IsUntyped() {
r = convlit(r, l.Type())
return l, r
}
if !r.Type().IsUntyped() {
l = convlit(l, r.Type())
return l, r
}
if !force {
return l, r
}
// Can't mix nil with anything untyped.
if ir.IsNil(l) || ir.IsNil(r) {
return l, r
}
t := defaultType(mixUntyped(l.Type(), r.Type()))
l = convlit(l, t)
r = convlit(r, t)
return l, r
}
func mixUntyped(t1, t2 *types.Type) *types.Type {
if t1 == t2 {
return t1
}
rank := func(t *types.Type) int {
switch t {
case types.UntypedInt:
return 0
case types.UntypedRune:
return 1
case types.UntypedFloat:
return 2
case types.UntypedComplex:
return 3
}
base.Fatalf("bad type %v", t)
panic("unreachable")
}
if rank(t2) > rank(t1) {
return t2
}
return t1
}
func defaultType(t *types.Type) *types.Type {
if !t.IsUntyped() || t.Kind() == types.TNIL {
return t
}
switch t {
case types.UntypedBool:
return types.Types[types.TBOOL]
case types.UntypedString:
return types.Types[types.TSTRING]
case types.UntypedInt:
return types.Types[types.TINT]
case types.UntypedRune:
return types.RuneType
case types.UntypedFloat:
return types.Types[types.TFLOAT64]
case types.UntypedComplex:
return types.Types[types.TCOMPLEX128]
}
base.Fatalf("bad type %v", t)
return nil
}
// IndexConst checks if Node n contains a constant expression
// representable as a non-negative int and returns its value.
// If n is not a constant expression, not representable as an
// integer, or negative, it returns -1. If n is too large, it
// returns -2.
func IndexConst(n ir.Node) int64 {
if n.Op() != ir.OLITERAL {
return -1
}
if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
return -1
}
v := toint(n.Val())
if v.Kind() != constant.Int || constant.Sign(v) < 0 {
return -1
}
if ir.ConstOverflow(v, types.Types[types.TINT]) {
return -2
}
return ir.IntVal(types.Types[types.TINT], v)
}
// callOrChan reports whether n is a call or channel operation.
func callOrChan(n ir.Node) bool {
switch n.Op() {
case ir.OAPPEND,
ir.OCALL,
ir.OCALLFUNC,
ir.OCALLINTER,
ir.OCALLMETH,
ir.OCAP,
ir.OCLEAR,
ir.OCLOSE,
ir.OCOMPLEX,
ir.OCOPY,
ir.ODELETE,
ir.OIMAG,
ir.OLEN,
ir.OMAKE,
ir.OMAX,
ir.OMIN,
ir.ONEW,
ir.OPANIC,
ir.OPRINT,
ir.OPRINTLN,
ir.OREAL,
ir.ORECOVER,
ir.ORECOVERFP,
ir.ORECV,
ir.OUNSAFEADD,
ir.OUNSAFESLICE,
ir.OUNSAFESLICEDATA,
ir.OUNSAFESTRING,
ir.OUNSAFESTRINGDATA:
return true
}
return false
}