...

Source file src/go/types/conversions.go

Documentation: go/types

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements typechecking of conversions.
     6  
     7  package types
     8  
     9  import "go/constant"
    10  
    11  // Conversion type-checks the conversion T(x).
    12  // The result is in x.
    13  func (check *Checker) conversion(x *operand, T Type) {
    14  	constArg := x.mode == constant_
    15  
    16  	var ok bool
    17  	switch {
    18  	case constArg && isConstType(T):
    19  		// constant conversion
    20  		switch t := T.Underlying().(*Basic); {
    21  		case representableConst(x.val, check, t, &x.val):
    22  			ok = true
    23  		case isInteger(x.typ) && isString(t):
    24  			codepoint := int64(-1)
    25  			if i, ok := constant.Int64Val(x.val); ok {
    26  				codepoint = i
    27  			}
    28  			// If codepoint < 0 the absolute value is too large (or unknown) for
    29  			// conversion. This is the same as converting any other out-of-range
    30  			// value - let string(codepoint) do the work.
    31  			x.val = constant.MakeString(string(codepoint))
    32  			ok = true
    33  		}
    34  	case x.convertibleTo(check, T):
    35  		// non-constant conversion
    36  		x.mode = value
    37  		ok = true
    38  	}
    39  
    40  	if !ok {
    41  		check.errorf(x.pos(), "cannot convert %s to %s", x, T)
    42  		x.mode = invalid
    43  		return
    44  	}
    45  
    46  	// The conversion argument types are final. For untyped values the
    47  	// conversion provides the type, per the spec: "A constant may be
    48  	// given a type explicitly by a constant declaration or conversion,...".
    49  	if isUntyped(x.typ) {
    50  		final := T
    51  		// - For conversions to interfaces, use the argument's default type.
    52  		// - For conversions of untyped constants to non-constant types, also
    53  		//   use the default type (e.g., []byte("foo") should report string
    54  		//   not []byte as type for the constant "foo").
    55  		// - Keep untyped nil for untyped nil arguments.
    56  		// - For integer to string conversions, keep the argument type.
    57  		//   (See also the TODO below.)
    58  		if IsInterface(T) || constArg && !isConstType(T) {
    59  			final = Default(x.typ)
    60  		} else if isInteger(x.typ) && isString(T) {
    61  			final = x.typ
    62  		}
    63  		check.updateExprType(x.expr, final, true)
    64  	}
    65  
    66  	x.typ = T
    67  }
    68  
    69  // TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
    70  // of x is fully known, but that's not the case for say string(1<<s + 1.0):
    71  // Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
    72  // (correct!) refusal of the conversion. But the reported error is essentially
    73  // "cannot convert untyped float value to string", yet the correct error (per
    74  // the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
    75  // be converted to UntypedFloat because of the addition of 1.0. Fixing this
    76  // is tricky because we'd have to run updateExprType on the argument first.
    77  // (Issue #21982.)
    78  
    79  // convertibleTo reports whether T(x) is valid.
    80  // The check parameter may be nil if convertibleTo is invoked through an
    81  // exported API call, i.e., when all methods have been type-checked.
    82  func (x *operand) convertibleTo(check *Checker, T Type) bool {
    83  	// "x is assignable to T"
    84  	if x.assignableTo(check, T, nil) {
    85  		return true
    86  	}
    87  
    88  	// "x's type and T have identical underlying types if tags are ignored"
    89  	V := x.typ
    90  	Vu := V.Underlying()
    91  	Tu := T.Underlying()
    92  	if IdenticalIgnoreTags(Vu, Tu) {
    93  		return true
    94  	}
    95  
    96  	// "x's type and T are unnamed pointer types and their pointer base types
    97  	// have identical underlying types if tags are ignored"
    98  	if V, ok := V.(*Pointer); ok {
    99  		if T, ok := T.(*Pointer); ok {
   100  			if IdenticalIgnoreTags(V.base.Underlying(), T.base.Underlying()) {
   101  				return true
   102  			}
   103  		}
   104  	}
   105  
   106  	// "x's type and T are both integer or floating point types"
   107  	if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
   108  		return true
   109  	}
   110  
   111  	// "x's type and T are both complex types"
   112  	if isComplex(V) && isComplex(T) {
   113  		return true
   114  	}
   115  
   116  	// "x is an integer or a slice of bytes or runes and T is a string type"
   117  	if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
   118  		return true
   119  	}
   120  
   121  	// "x is a string and T is a slice of bytes or runes"
   122  	if isString(V) && isBytesOrRunes(Tu) {
   123  		return true
   124  	}
   125  
   126  	// package unsafe:
   127  	// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
   128  	if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
   129  		return true
   130  	}
   131  	// "and vice versa"
   132  	if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
   133  		return true
   134  	}
   135  
   136  	return false
   137  }
   138  
   139  func isUintptr(typ Type) bool {
   140  	t, ok := typ.Underlying().(*Basic)
   141  	return ok && t.kind == Uintptr
   142  }
   143  
   144  func isUnsafePointer(typ Type) bool {
   145  	// TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
   146  	//            The spec does not say so, but gc claims it is. See also
   147  	//            issue 6326.
   148  	t, ok := typ.Underlying().(*Basic)
   149  	return ok && t.kind == UnsafePointer
   150  }
   151  
   152  func isPointer(typ Type) bool {
   153  	_, ok := typ.Underlying().(*Pointer)
   154  	return ok
   155  }
   156  
   157  func isBytesOrRunes(typ Type) bool {
   158  	if s, ok := typ.(*Slice); ok {
   159  		t, ok := s.elem.Underlying().(*Basic)
   160  		return ok && (t.kind == Byte || t.kind == Rune)
   161  	}
   162  	return false
   163  }
   164  

View as plain text