// Copyright 2012 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. // This file implements typechecking of conversions. package types import ( "go/constant" "go/token" "unicode" ) // Conversion type-checks the conversion T(x). // The result is in x. func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant_ constConvertibleTo := func(T Type, val *constant.Value) bool { switch t, _ := under(T).(*Basic); { case t == nil: // nothing to do case representableConst(x.val, check, t, val): return true case isInteger(x.typ) && isString(t): codepoint := unicode.ReplacementChar if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune { codepoint = rune(i) } if val != nil { *val = constant.MakeString(string(codepoint)) } return true } return false } var ok bool var cause string switch { case constArg && isConstType(T): // constant conversion ok = constConvertibleTo(T, &x.val) case constArg && isTypeParam(T): // x is convertible to T if it is convertible // to each specific type in the type set of T. // If T's type set is empty, or if it doesn't // have specific types, constant x cannot be // converted. ok = T.(*TypeParam).underIs(func(u Type) bool { // u is nil if there are no specific type terms if u == nil { cause = check.sprintf("%s does not contain specific types", T) return false } if isString(x.typ) && isBytesOrRunes(u) { return true } if !constConvertibleTo(u, nil) { cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T) return false } return true }) x.mode = value // type parameters are not constants case x.convertibleTo(check, T, &cause): // non-constant conversion ok = true x.mode = value } if !ok { // TODO(rfindley): use types2-style error reporting here. if compilerErrorMessages { if cause != "" { // Add colon at end of line if we have a following cause. err := newErrorf(x, _InvalidConversion, "cannot convert %s to type %s:", x, T) err.errorf(token.NoPos, cause) check.report(err) } else { check.errorf(x, _InvalidConversion, "cannot convert %s to type %s", x, T) } } else { if cause != "" { check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, cause) } else { check.errorf(x, _InvalidConversion, "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,...". 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. // - For constant integer to string conversions, keep the argument type. // (See also the TODO below.) if isNonTypeParamInterface(T) || constArg && !isConstType(T) || x.isNil() { final = Default(x.typ) // default type of untyped nil is untyped nil } else if x.mode == constant_ && isInteger(x.typ) && allString(T) { final = x.typ } check.updateExprType(x.expr, final, true) } x.typ = T } // TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type // of x is fully known, but that's not the case for say string(1<