Example #1
0
// Conversion type-checks the conversion T(x).
// The result is in x.
func (check *checker) conversion(x *operand, T Type) {
	constArg := x.mode == constant

	var ok bool
	switch {
	case constArg && isConstType(T):
		// constant conversion
		switch t := T.Underlying().(*Basic); {
		case isRepresentableConst(x.val, check.conf, t.Kind, &x.val):
			ok = true
		case x.isInteger() && isString(t):
			codepoint := int64(-1)
			if i, ok := exact.Int64Val(x.val); ok {
				codepoint = i
			}
			// If codepoint < 0 the absolute value is too large (or unknown) for
			// conversion. This is the same as converting any other out-of-range
			// value - let string(codepoint) do the work.
			x.val = exact.MakeString(string(codepoint))
			ok = true
		}
	case x.isConvertible(check.conf, T):
		// non-constant conversion
		x.mode = value
		ok = true
	}

	if !ok {
		check.errorf(x.pos(), "cannot convert %s to %s", x, T)
		x.mode = invalid
		return
	}

	// The conversion argument types are final. For untyped values the
	// conversion provides the type, per the spec: "A constant may be
	// given a type explicitly by a constant declaration or conversion,...".
	final := x.typ
	if isUntyped(x.typ) {
		final = T
		// - For conversions to interfaces, use the argument's default type.
		// - For conversions of untyped constants to non-constant types, also
		//   use the default type (e.g., []byte("foo") should report string
		//   not []byte as type for the constant "foo").
		// - Keep untyped nil for untyped nil arguments.
		if isInterface(T) || constArg && !isConstType(T) {
			final = defaultType(x.typ)
		}
	}

	x.typ = T
	check.updateExprType(x.expr, final, true)
}
Example #2
0
// index checks an index expression for validity.
// If max >= 0, it is the upper bound for index.
// If index is valid and the result i >= 0, then i is the constant value of index.
func (check *checker) index(index ast.Expr, max int64) (i int64, valid bool) {
	var x operand
	check.expr(&x, index)
	if x.mode == invalid {
		return
	}

	// an untyped constant must be representable as Int
	check.convertUntyped(&x, Typ[Int])
	if x.mode == invalid {
		return
	}

	// the index must be of integer type
	if !isInteger(x.typ) {
		check.invalidArg(x.pos(), "index %s must be integer", &x)
		return
	}

	// a constant index i must be in bounds
	if x.mode == constant {
		if exact.Sign(x.val) < 0 {
			check.invalidArg(x.pos(), "index %s must not be negative", &x)
			return
		}
		i, valid = exact.Int64Val(x.val)
		if !valid || max >= 0 && i >= max {
			check.errorf(x.pos(), "index %s is out of bounds", &x)
			return i, false
		}
		// 0 <= i [ && i < max ]
		return i, true
	}

	return -1, true
}
Example #3
0
// isRepresentableConst reports whether x can be represented as
// value of the given basic type kind and for the configuration
// provided (only needed for int/uint sizes).
//
// If rounded != nil, *rounded is set to the rounded value of x for
// representable floating-point values; it is left alone otherwise.
// It is ok to provide the addressof the first argument for rounded.
func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool {
	switch x.Kind() {
	case exact.Unknown:
		return true

	case exact.Bool:
		return as == Bool || as == UntypedBool

	case exact.Int:
		if x, ok := exact.Int64Val(x); ok {
			switch as {
			case Int:
				var s = uint(conf.sizeof(Typ[as])) * 8
				return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
			case Int8:
				const s = 8
				return -1<<(s-1) <= x && x <= 1<<(s-1)-1
			case Int16:
				const s = 16
				return -1<<(s-1) <= x && x <= 1<<(s-1)-1
			case Int32:
				const s = 32
				return -1<<(s-1) <= x && x <= 1<<(s-1)-1
			case Int64:
				return true
			case Uint, Uintptr:
				if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 {
					return 0 <= x && x <= int64(1)<<s-1
				}
				return 0 <= x
			case Uint8:
				const s = 8
				return 0 <= x && x <= 1<<s-1
			case Uint16:
				const s = 16
				return 0 <= x && x <= 1<<s-1
			case Uint32:
				const s = 32
				return 0 <= x && x <= 1<<s-1
			case Uint64:
				return 0 <= x
			case Float32, Float64, Complex64, Complex128,
				UntypedInt, UntypedFloat, UntypedComplex:
				return true
			}
		}

		n := exact.BitLen(x)
		switch as {
		case Uint, Uintptr:
			var s = uint(conf.sizeof(Typ[as])) * 8
			return exact.Sign(x) >= 0 && n <= int(s)
		case Uint64:
			return exact.Sign(x) >= 0 && n <= 64
		case Float32, Complex64:
			if rounded == nil {
				return fitsFloat32(x)
			}
			r := roundFloat32(x)
			if r != nil {
				*rounded = r
				return true
			}
		case Float64, Complex128:
			if rounded == nil {
				return fitsFloat64(x)
			}
			r := roundFloat64(x)
			if r != nil {
				*rounded = r
				return true
			}
		case UntypedInt, UntypedFloat, UntypedComplex:
			return true
		}

	case exact.Float:
		switch as {
		case Float32, Complex64:
			if rounded == nil {
				return fitsFloat32(x)
			}
			r := roundFloat32(x)
			if r != nil {
				*rounded = r
				return true
			}
		case Float64, Complex128:
			if rounded == nil {
				return fitsFloat64(x)
			}
			r := roundFloat64(x)
			if r != nil {
				*rounded = r
				return true
			}
		case UntypedFloat, UntypedComplex:
			return true
		}

	case exact.Complex:
		switch as {
		case Complex64:
			if rounded == nil {
				return fitsFloat32(exact.Real(x)) && fitsFloat32(exact.Imag(x))
			}
			re := roundFloat32(exact.Real(x))
			im := roundFloat32(exact.Imag(x))
			if re != nil && im != nil {
				*rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
				return true
			}
		case Complex128:
			if rounded == nil {
				return fitsFloat64(exact.Real(x)) && fitsFloat64(exact.Imag(x))
			}
			re := roundFloat64(exact.Real(x))
			im := roundFloat64(exact.Imag(x))
			if re != nil && im != nil {
				*rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
				return true
			}
		case UntypedComplex:
			return true
		}

	case exact.String:
		return as == String || as == UntypedString

	default:
		unreachable()
	}

	return false
}
Example #4
0
// typInternal contains the core of type checking of types.
// Must only be called by typ.
//
func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
	switch e := e.(type) {
	case *ast.BadExpr:
		// ignore - error reported before

	case *ast.Ident:
		var x operand
		check.ident(&x, e, def, cycleOk)

		switch x.mode {
		case typexpr:
			return x.typ
		case invalid:
			// ignore - error reported before
		case novalue:
			check.errorf(x.pos(), "%s used as type", &x)
		default:
			check.errorf(x.pos(), "%s is not a type", &x)
		}

	case *ast.SelectorExpr:
		var x operand
		check.selector(&x, e)

		switch x.mode {
		case typexpr:
			return x.typ
		case invalid:
			// ignore - error reported before
		case novalue:
			check.errorf(x.pos(), "%s used as type", &x)
		default:
			check.errorf(x.pos(), "%s is not a type", &x)
		}

	case *ast.ParenExpr:
		return check.typ(e.X, def, cycleOk)

	case *ast.ArrayType:
		if e.Len != nil {
			var x operand
			check.expr(&x, e.Len)
			if x.mode != constant {
				if x.mode != invalid {
					check.errorf(x.pos(), "array length %s must be constant", &x)
				}
				break
			}
			if !x.isInteger() {
				check.errorf(x.pos(), "array length %s must be integer", &x)
				break
			}
			n, ok := exact.Int64Val(x.val)
			if !ok || n < 0 {
				check.errorf(x.pos(), "invalid array length %s", &x)
				break
			}

			typ := new(Array)
			if def != nil {
				def.UnderlyingT = typ
			}

			typ.Len = n
			typ.Elem = check.typ(e.Elt, nil, cycleOk)
			return typ

		} else {
			typ := new(Slice)
			if def != nil {
				def.UnderlyingT = typ
			}

			typ.Elem = check.typ(e.Elt, nil, true)
			return typ
		}

	case *ast.StructType:
		typ := new(Struct)
		if def != nil {
			def.UnderlyingT = typ
		}

		typ.Fields, typ.tags = check.collectFields(e.Fields, cycleOk)
		return typ

	case *ast.StarExpr:
		typ := new(Pointer)
		if def != nil {
			def.UnderlyingT = typ
		}

		typ.Elem = check.typ(e.X, nil, true)
		return typ

	case *ast.FuncType:
		return check.funcType(nil, e, def)

	case *ast.InterfaceType:
		return check.interfaceType(e, def, cycleOk)

	case *ast.MapType:
		typ := new(Map)
		if def != nil {
			def.UnderlyingT = typ
		}

		typ.Key = check.typ(e.Key, nil, true)
		typ.Elem = check.typ(e.Value, nil, true)

		// spec: "The comparison operators == and != must be fully defined
		// for operands of the key type; thus the key type must not be a
		// function, map, or slice."
		//
		// Delay this check because it requires fully setup types;
		// it is safe to continue in any case (was issue 6667).
		check.delay(func() {
			if !Comparable(typ.Key) {
				check.errorf(e.Key.Pos(), "invalid map key type %s", typ.Key)
			}
		})

		return typ

	case *ast.ChanType:
		typ := new(Chan)
		if def != nil {
			def.UnderlyingT = typ
		}

		dir := SendRecv
		switch e.Dir {
		case ast.SEND | ast.RECV:
			// nothing to do
		case ast.SEND:
			dir = SendOnly
		case ast.RECV:
			dir = RecvOnly
		default:
			check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
			// ok to continue
		}
		typ.Dir = dir
		typ.Elem = check.typ(e.Value, nil, true)
		return typ

	default:
		check.errorf(e.Pos(), "%s is not a type", e)
	}

	return Typ[Invalid]
}