Source file src/go/types/conversions.go

     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  	constConvertibleTo := func(T Type, val *constant.Value) bool {
    20  		switch t, _ := under(T).(*Basic); {
    21  		case t == nil:
    22  			// nothing to do
    23  		case representableConst(x.val, check, t, val):
    24  			return true
    25  		case isInteger(x.typ) && isString(t):
    26  			codepoint := unicode.ReplacementChar
    27  			if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
    28  				codepoint = rune(i)
    29  			}
    30  			if val != nil {
    31  				*val = constant.MakeString(string(codepoint))
    32  			}
    33  			return true
    34  		}
    35  		return false
    36  	}
    37  
    38  	var ok bool
    39  	var cause string
    40  	switch {
    41  	case constArg && isConstType(T):
    42  		// constant conversion
    43  		ok = constConvertibleTo(T, &x.val)
    44  	case constArg && isTypeParam(T):
    45  		// x is convertible to T if it is convertible
    46  		// to each specific type in the type set of T.
    47  		// If T's type set is empty, or if it doesn't
    48  		// have specific types, constant x cannot be
    49  		// converted.
    50  		ok = T.(*TypeParam).underIs(func(u Type) bool {
    51  			// u is nil if there are no specific type terms
    52  			if u == nil {
    53  				cause = check.sprintf("%s does not contain specific types", T)
    54  				return false
    55  			}
    56  			if isString(x.typ) && isBytesOrRunes(u) {
    57  				return true
    58  			}
    59  			if !constConvertibleTo(u, nil) {
    60  				cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T)
    61  				return false
    62  			}
    63  			return true
    64  		})
    65  		x.mode = value // type parameters are not constants
    66  	case x.convertibleTo(check, T, &cause):
    67  		// non-constant conversion
    68  		ok = true
    69  		x.mode = value
    70  	}
    71  
    72  	if !ok {
    73  		// TODO(rfindley): use types2-style error reporting here.
    74  		if compilerErrorMessages {
    75  			if cause != "" {
    76  				// Add colon at end of line if we have a following cause.
    77  				check.errorf(x, _InvalidConversion, "cannot convert %s to type %s:\n\t%s", x, T, cause)
    78  			} else {
    79  				check.errorf(x, _InvalidConversion, "cannot convert %s to type %s", x, T)
    80  			}
    81  		} else {
    82  			if cause != "" {
    83  				check.errorf(x, _InvalidConversion, "cannot convert %s to %s (%s)", x, T, cause)
    84  			} else {
    85  				check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
    86  			}
    87  		}
    88  		x.mode = invalid
    89  		return
    90  	}
    91  
    92  	// The conversion argument types are final. For untyped values the
    93  	// conversion provides the type, per the spec: "A constant may be
    94  	// given a type explicitly by a constant declaration or conversion,...".
    95  	if isUntyped(x.typ) {
    96  		final := T
    97  		// - For conversions to interfaces, use the argument's default type.
    98  		// - For conversions of untyped constants to non-constant types, also
    99  		//   use the default type (e.g., []byte("foo") should report string
   100  		//   not []byte as type for the constant "foo").
   101  		// - Keep untyped nil for untyped nil arguments.
   102  		// - For constant integer to string conversions, keep the argument type.
   103  		//   (See also the TODO below.)
   104  		if IsInterface(T) && !isTypeParam(T) || constArg && !isConstType(T) || x.isNil() {
   105  			final = Default(x.typ) // default type of untyped nil is untyped nil
   106  		} else if x.mode == constant_ && isInteger(x.typ) && allString(T) {
   107  			final = x.typ
   108  		}
   109  		check.updateExprType(x.expr, final, true)
   110  	}
   111  
   112  	x.typ = T
   113  }
   114  
   115  // TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
   116  // of x is fully known, but that's not the case for say string(1<<s + 1.0):
   117  // Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
   118  // (correct!) refusal of the conversion. But the reported error is essentially
   119  // "cannot convert untyped float value to string", yet the correct error (per
   120  // the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
   121  // be converted to UntypedFloat because of the addition of 1.0. Fixing this
   122  // is tricky because we'd have to run updateExprType on the argument first.
   123  // (Issue #21982.)
   124  
   125  // convertibleTo reports whether T(x) is valid. In the failure case, *cause
   126  // may be set to the cause for the failure.
   127  // The check parameter may be nil if convertibleTo is invoked through an
   128  // exported API call, i.e., when all methods have been type-checked.
   129  func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
   130  	// "x is assignable to T"
   131  	if ok, _ := x.assignableTo(check, T, cause); ok {
   132  		return true
   133  	}
   134  
   135  	// "V and T have identical underlying types if tags are ignored
   136  	// and V and T are not type parameters"
   137  	V := x.typ
   138  	Vu := under(V)
   139  	Tu := under(T)
   140  	Vp, _ := V.(*TypeParam)
   141  	Tp, _ := T.(*TypeParam)
   142  	if IdenticalIgnoreTags(Vu, Tu) && Vp == nil && Tp == nil {
   143  		return true
   144  	}
   145  
   146  	// "V and T are unnamed pointer types and their pointer base types
   147  	// have identical underlying types if tags are ignored
   148  	// and their pointer base types are not type parameters"
   149  	if V, ok := V.(*Pointer); ok {
   150  		if T, ok := T.(*Pointer); ok {
   151  			if IdenticalIgnoreTags(under(V.base), under(T.base)) && !isTypeParam(V.base) && !isTypeParam(T.base) {
   152  				return true
   153  			}
   154  		}
   155  	}
   156  
   157  	// "V and T are both integer or floating point types"
   158  	if isIntegerOrFloat(Vu) && isIntegerOrFloat(Tu) {
   159  		return true
   160  	}
   161  
   162  	// "V and T are both complex types"
   163  	if isComplex(Vu) && isComplex(Tu) {
   164  		return true
   165  	}
   166  
   167  	// "V is an integer or a slice of bytes or runes and T is a string type"
   168  	if (isInteger(Vu) || isBytesOrRunes(Vu)) && isString(Tu) {
   169  		return true
   170  	}
   171  
   172  	// "V is a string and T is a slice of bytes or runes"
   173  	if isString(Vu) && isBytesOrRunes(Tu) {
   174  		return true
   175  	}
   176  
   177  	// package unsafe:
   178  	// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
   179  	if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(Tu) {
   180  		return true
   181  	}
   182  	// "and vice versa"
   183  	if isUnsafePointer(Vu) && (isPointer(Tu) || isUintptr(Tu)) {
   184  		return true
   185  	}
   186  
   187  	// "V a slice, T is a pointer-to-array type,
   188  	// and the slice and array types have identical element types."
   189  	if s, _ := Vu.(*Slice); s != nil {
   190  		if p, _ := Tu.(*Pointer); p != nil {
   191  			if a, _ := under(p.Elem()).(*Array); a != nil {
   192  				if Identical(s.Elem(), a.Elem()) {
   193  					if check == nil || check.allowVersion(check.pkg, 1, 17) {
   194  						return true
   195  					}
   196  					if cause != nil {
   197  						*cause = "conversion of slices to array pointers requires go1.17 or later"
   198  					}
   199  				}
   200  			}
   201  		}
   202  	}
   203  
   204  	// optimization: if we don't have type parameters, we're done
   205  	if Vp == nil && Tp == nil {
   206  		return false
   207  	}
   208  
   209  	errorf := func(format string, args ...any) {
   210  		if check != nil && cause != nil {
   211  			msg := check.sprintf(format, args...)
   212  			if *cause != "" {
   213  				msg += "\n\t" + *cause
   214  			}
   215  			*cause = msg
   216  		}
   217  	}
   218  
   219  	// generic cases with specific type terms
   220  	// (generic operands cannot be constants, so we can ignore x.val)
   221  	switch {
   222  	case Vp != nil && Tp != nil:
   223  		x := *x // don't clobber outer x
   224  		return Vp.is(func(V *term) bool {
   225  			if V == nil {
   226  				return false // no specific types
   227  			}
   228  			x.typ = V.typ
   229  			return Tp.is(func(T *term) bool {
   230  				if T == nil {
   231  					return false // no specific types
   232  				}
   233  				if !x.convertibleTo(check, T.typ, cause) {
   234  					errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
   235  					return false
   236  				}
   237  				return true
   238  			})
   239  		})
   240  	case Vp != nil:
   241  		x := *x // don't clobber outer x
   242  		return Vp.is(func(V *term) bool {
   243  			if V == nil {
   244  				return false // no specific types
   245  			}
   246  			x.typ = V.typ
   247  			if !x.convertibleTo(check, T, cause) {
   248  				errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T)
   249  				return false
   250  			}
   251  			return true
   252  		})
   253  	case Tp != nil:
   254  		return Tp.is(func(T *term) bool {
   255  			if T == nil {
   256  				return false // no specific types
   257  			}
   258  			if !x.convertibleTo(check, T.typ, cause) {
   259  				errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp)
   260  				return false
   261  			}
   262  			return true
   263  		})
   264  	}
   265  
   266  	return false
   267  }
   268  
   269  func isUintptr(typ Type) bool {
   270  	t, _ := under(typ).(*Basic)
   271  	return t != nil && t.kind == Uintptr
   272  }
   273  
   274  func isUnsafePointer(typ Type) bool {
   275  	t, _ := under(typ).(*Basic)
   276  	return t != nil && t.kind == UnsafePointer
   277  }
   278  
   279  func isPointer(typ Type) bool {
   280  	_, ok := under(typ).(*Pointer)
   281  	return ok
   282  }
   283  
   284  func isBytesOrRunes(typ Type) bool {
   285  	if s, _ := under(typ).(*Slice); s != nil {
   286  		t, _ := under(s.elem).(*Basic)
   287  		return t != nil && (t.kind == Byte || t.kind == Rune)
   288  	}
   289  	return false
   290  }
   291  

View as plain text