Black Lives Matter. Support the Equal Justice Initiative.

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 (
    10  	"go/constant"
    11  	"unicode"
    12  )
    13  
    14  // Conversion type-checks the conversion T(x).
    15  // The result is in x.
    16  func (check *Checker) conversion(x *operand, T Type) {
    17  	constArg := x.mode == constant_
    18  
    19  	var ok bool
    20  	var reason string
    21  	switch {
    22  	case constArg && isConstType(T):
    23  		// constant conversion
    24  		switch t := asBasic(T); {
    25  		case representableConst(x.val, check, t, &x.val):
    26  			ok = true
    27  		case isInteger(x.typ) && isString(t):
    28  			codepoint := unicode.ReplacementChar
    29  			if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
    30  				codepoint = rune(i)
    31  			}
    32  			x.val = constant.MakeString(string(codepoint))
    33  			ok = true
    34  		}
    35  	case x.convertibleTo(check, T, &reason):
    36  		// non-constant conversion
    37  		x.mode = value
    38  		ok = true
    39  	}
    40  
    41  	if !ok {
    42  		if reason != "" {
    43  			check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, reason)
    44  		} else {
    45  			check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
    46  		}
    47  		x.mode = invalid
    48  		return
    49  	}
    50  
    51  	// The conversion argument types are final. For untyped values the
    52  	// conversion provides the type, per the spec: "A constant may be
    53  	// given a type explicitly by a constant declaration or conversion,...".
    54  	if isUntyped(x.typ) {
    55  		final := T
    56  		// - For conversions to interfaces, use the argument's default type.
    57  		// - For conversions of untyped constants to non-constant types, also
    58  		//   use the default type (e.g., []byte("foo") should report string
    59  		//   not []byte as type for the constant "foo").
    60  		// - Keep untyped nil for untyped nil arguments.
    61  		// - For integer to string conversions, keep the argument type.
    62  		//   (See also the TODO below.)
    63  		if IsInterface(T) || constArg && !isConstType(T) || x.isNil() {
    64  			final = Default(x.typ) // default type of untyped nil is untyped nil
    65  		} else if isInteger(x.typ) && isString(T) {
    66  			final = x.typ
    67  		}
    68  		check.updateExprType(x.expr, final, true)
    69  	}
    70  
    71  	x.typ = T
    72  }
    73  
    74  // TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
    75  // of x is fully known, but that's not the case for say string(1<<s + 1.0):
    76  // Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
    77  // (correct!) refusal of the conversion. But the reported error is essentially
    78  // "cannot convert untyped float value to string", yet the correct error (per
    79  // the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
    80  // be converted to UntypedFloat because of the addition of 1.0. Fixing this
    81  // is tricky because we'd have to run updateExprType on the argument first.
    82  // (Issue #21982.)
    83  
    84  // convertibleTo reports whether T(x) is valid.
    85  // The check parameter may be nil if convertibleTo is invoked through an
    86  // exported API call, i.e., when all methods have been type-checked.
    87  func (x *operand) convertibleTo(check *Checker, T Type, reason *string) bool {
    88  	// "x is assignable to T"
    89  	if ok, _ := x.assignableTo(check, T, nil); ok {
    90  		return true
    91  	}
    92  
    93  	// "x's type and T have identical underlying types if tags are ignored"
    94  	V := x.typ
    95  	Vu := under(V)
    96  	Tu := under(T)
    97  	if check.identicalIgnoreTags(Vu, Tu) {
    98  		return true
    99  	}
   100  
   101  	// "x's type and T are unnamed pointer types and their pointer base types
   102  	// have identical underlying types if tags are ignored"
   103  	if V, ok := V.(*Pointer); ok {
   104  		if T, ok := T.(*Pointer); ok {
   105  			if check.identicalIgnoreTags(under(V.base), under(T.base)) {
   106  				return true
   107  			}
   108  		}
   109  	}
   110  
   111  	// "x's type and T are both integer or floating point types"
   112  	if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
   113  		return true
   114  	}
   115  
   116  	// "x's type and T are both complex types"
   117  	if isComplex(V) && isComplex(T) {
   118  		return true
   119  	}
   120  
   121  	// "x is an integer or a slice of bytes or runes and T is a string type"
   122  	if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
   123  		return true
   124  	}
   125  
   126  	// "x is a string and T is a slice of bytes or runes"
   127  	if isString(V) && isBytesOrRunes(Tu) {
   128  		return true
   129  	}
   130  
   131  	// package unsafe:
   132  	// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
   133  	if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
   134  		return true
   135  	}
   136  	// "and vice versa"
   137  	if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
   138  		return true
   139  	}
   140  
   141  	// "x is a slice, T is a pointer-to-array type,
   142  	// and the slice and array types have identical element types."
   143  	if s := asSlice(V); s != nil {
   144  		if p := asPointer(T); p != nil {
   145  			if a := asArray(p.Elem()); a != nil {
   146  				if check.identical(s.Elem(), a.Elem()) {
   147  					if check == nil || check.allowVersion(check.pkg, 1, 17) {
   148  						return true
   149  					}
   150  					if reason != nil {
   151  						*reason = "conversion of slices to array pointers requires go1.17 or later"
   152  					}
   153  				}
   154  			}
   155  		}
   156  	}
   157  
   158  	return false
   159  }
   160  
   161  func isUintptr(typ Type) bool {
   162  	t := asBasic(typ)
   163  	return t != nil && t.kind == Uintptr
   164  }
   165  
   166  func isUnsafePointer(typ Type) bool {
   167  	// TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct?
   168  	//            (The former calls under(), while the latter doesn't.)
   169  	//            The spec does not say so, but gc claims it is. See also
   170  	//            issue 6326.
   171  	t := asBasic(typ)
   172  	return t != nil && t.kind == UnsafePointer
   173  }
   174  
   175  func isPointer(typ Type) bool {
   176  	return asPointer(typ) != nil
   177  }
   178  
   179  func isBytesOrRunes(typ Type) bool {
   180  	if s := asSlice(typ); s != nil {
   181  		t := asBasic(s.elem)
   182  		return t != nil && (t.kind == Byte || t.kind == Rune)
   183  	}
   184  	return false
   185  }
   186  

View as plain text