// Copyright 2013 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 initialization and assignment checks.package typesimport ()// assignment reports whether x can be assigned to a variable of type T,// if necessary by attempting to convert untyped values to the appropriate// type. context describes the context in which the assignment takes place.// Use T == nil to indicate assignment to an untyped blank identifier.// x.mode is set to invalid if the assignment failed.func ( *Checker) ( *operand, Type, string) { .singleValue()switch .mode {caseinvalid:return// error reported beforecaseconstant_, variable, mapindex, value, commaok, commaerr:// okdefault:// we may get here because of other problems (issue #39634, crash 12) .errorf(, 0, "cannot assign %s to %s in %s", , , )return }ifisUntyped(.typ) { := // spec: "If an untyped constant is assigned to a variable of interface // type or the blank identifier, the constant is first converted to type // bool, rune, int, float64, complex128 or string respectively, depending // on whether the value is a boolean, rune, integer, floating-point, // complex, or string constant."if == nil || IsInterface() && !isTypeParam() {if == nil && .typ == Typ[UntypedNil] { .errorf(, _UntypedNil, "use of untyped nil in %s", ) .mode = invalidreturn } = Default(.typ) } , , := .implicitTypeAndValue(, )if != 0 { := .sprintf("cannot use %s as %s value in %s", , , )switch {case_TruncatedFloat: += " (truncated)"case_NumericOverflow: += " (overflows)"default: = _IncompatibleAssign } .error(, , ) .mode = invalidreturn }if != nil { .val = .updateExprVal(.expr, ) }if != .typ { .typ = .updateExprType(.expr, , false) } }// A generic (non-instantiated) function value cannot be assigned to a variable.if , := under(.typ).(*Signature); != nil && .TypeParams().Len() > 0 { .errorf(, _WrongTypeArgCount, "cannot use generic function %s without instantiation in %s", , ) }// spec: "If a left-hand side is the blank identifier, any typed or // non-constant value except for the predeclared identifier nil may // be assigned to it."if == nil {return } := ""if , := .assignableTo(, , &); ! {ifcompilerErrorMessages {if != "" { .errorf(, , "cannot use %s as type %s in %s:\n\t%s", , , , ) } else { .errorf(, , "cannot use %s as type %s in %s", , , ) } } else {if != "" { .errorf(, , "cannot use %s as %s value in %s: %s", , , , ) } else { .errorf(, , "cannot use %s as %s value in %s", , , ) } } .mode = invalid }}func ( *Checker) ( *Const, *operand) {if .mode == invalid || .typ == Typ[Invalid] || .typ == Typ[Invalid] {if .typ == nil { .typ = Typ[Invalid] }return }// rhs must be a constantif .mode != constant_ { .errorf(, _InvalidConstInit, "%s is not constant", )if .typ == nil { .typ = Typ[Invalid] }return }assert(isConstType(.typ))// If the lhs doesn't have a type yet, use the type of x.if .typ == nil { .typ = .typ } .assignment(, .typ, "constant declaration")if .mode == invalid {return } .val = .val}func ( *Checker) ( *Var, *operand, string) Type {if .mode == invalid || .typ == Typ[Invalid] || .typ == Typ[Invalid] {if .typ == nil { .typ = Typ[Invalid] }returnnil }// If the lhs doesn't have a type yet, use the type of x.if .typ == nil { := .typifisUntyped() {// convert untyped types to default typesif == Typ[UntypedNil] { .errorf(, _UntypedNil, "use of untyped nil in %s", ) .typ = Typ[Invalid]returnnil } = Default() } .typ = } .assignment(, .typ, )if .mode == invalid {returnnil }return .typ}func ( *Checker) ( ast.Expr, *operand) Type {if .mode == invalid || .typ == Typ[Invalid] { .useLHS()returnnil }// Determine if the lhs is a (possibly parenthesized) identifier. , := unparen().(*ast.Ident)// Don't evaluate lhs if it is the blank identifier.if != nil && .Name == "_" { .recordDef(, nil) .assignment(, nil, "assignment to _ identifier")if .mode == invalid {returnnil }return .typ }// If the lhs is an identifier denoting a variable v, this assignment // is not a 'use' of v. Remember current value of v.used and restore // after evaluating the lhs via check.expr.var *Varvarboolif != nil {if := .lookup(.Name); != nil {// It's ok to mark non-local variables, but ignore variables // from other packages to avoid potential race conditions with // dot-imported variables.if , := .(*Var); != nil && .pkg == .pkg { = = .used } } }varoperand .expr(&, )if != nil { .used = // restore v.used }if .mode == invalid || .typ == Typ[Invalid] {returnnil }// spec: "Each left-hand side operand must be addressable, a map index // expression, or the blank identifier. Operands may be parenthesized."switch .mode {caseinvalid:returnnilcasevariable, mapindex:// okdefault:if , := .expr.(*ast.SelectorExpr); {varoperand .expr(&, .X)if .mode == mapindex { .errorf(&, _UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(.expr))returnnil } } .errorf(&, _UnassignableOperand, "cannot assign to %s", &)returnnil } .assignment(, .typ, "assignment")if .mode == invalid {returnnil }return .typ}// operandTypes returns the list of types for the given operands.func ( []*operand) ( []Type) {for , := range { = append(, .typ) }return}// varTypes returns the list of types for the given variables.func ( []*Var) ( []Type) {for , := range { = append(, .typ) }return}// typesSummary returns a string of the form "(t1, t2, ...)" where the// ti's are user-friendly string representations for the given types.// If variadic is set and the last type is a slice, its string is of// the form "...E" where E is the slice's element type.func ( *Checker) ( []Type, bool) string {var []stringfor , := range {varstringswitch {case == nil:fallthrough// should not happen but be cautiouscase == Typ[Invalid]: = "<T>"caseisUntyped():ifisNumeric() {// Do not imply a specific type requirement: // "have number, want float64" is better than // "have untyped int, want float64" or // "have int, want float64". = "number" } else {// If we don't have a number, omit the "untyped" qualifier // for compactness. = strings.Replace(.(*Basic).name, "untyped ", "", -1) }case && == len()-1: = .sprintf("...%s", .(*Slice).elem) }if == "" { = .sprintf("%s", ) } = append(, ) }return"(" + strings.Join(, ", ") + ")"}func ( int, string) string {if != 1 { += "s" }returnfmt.Sprintf("%d %s", , )}func ( *Checker) ( []ast.Expr, , int) { := measure(, "variable") := measure(, "value") := [0]iflen() == 1 {if , := unparen().(*ast.CallExpr); != nil { .errorf(, _WrongAssignCount, "assignment mismatch: %s but %s returns %s", , .Fun, )return } } .errorf(, _WrongAssignCount, "assignment mismatch: %s but %s", , )}// If returnStmt != nil, initVars is called to type-check the assignment// of return expressions, and returnStmt is the return statement.func ( *Checker) ( []*Var, []ast.Expr, ast.Stmt) { , := .exprList(, len() == 2 && == nil)iflen() != len() {// invalidate lhsfor , := range { .used = true// avoid declared but not used errorsif .typ == nil { .typ = Typ[Invalid] } }// don't report an error if we already reported onefor , := range {if .mode == invalid {return } }if != nil {varpositioner = := "not enough"iflen() > len() { = [len()].expr// report at first extra value = "too many" } elseiflen() > 0 { = [len()-1].expr// report at last value } .errorf(, _WrongResultCount, "%s return values\n\thave %s\n\twant %s", , .typesSummary(operandTypes(), false), .typesSummary(varTypes(), false), )return }ifcompilerErrorMessages { .assignError(, len(), len()) } else { .errorf([0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(), len()) }return } := "assignment"if != nil { = "return statement" }if {var [2]Typefor := range { [] = .initVar([], [], ) } .recordCommaOkTypes([0], )return }for , := range { .initVar(, [], ) }}func ( *Checker) (, []ast.Expr) { , := .exprList(, len() == 2)iflen() != len() { .useLHS(...)// don't report an error if we already reported onefor , := range {if .mode == invalid {return } }ifcompilerErrorMessages { .assignError(, len(), len()) } else { .errorf([0], _WrongAssignCount, "cannot assign %d values to %d variables", len(), len()) }return }if {var [2]Typefor := range { [] = .assignVar([], []) } .recordCommaOkTypes([0], )return }for , := range { .assignVar(, []) }}func ( *Checker) ( positioner, , []ast.Expr) { := len(.delayed) := .scope// collect lhs variables := make(map[string]bool, len()) := make([]*Var, len()) := make([]*Var, 0, len()) := falsefor , := range { , := .(*ast.Ident)if == nil { .useLHS()// TODO(rFindley) this is redundant with a parser error. Consider omitting? .errorf(, _BadDecl, "non-name %s on left side of :=", ) = truecontinue } := .Nameif != "_" {if [] { .errorf(, _RepeatedDecl, "%s repeated on left side of :=", ) = truecontinue } [] = true }// Use the correct obj if the ident is redeclared. The // variable's scope starts after the declaration; so we // must use Scope.Lookup here and call Scope.Insert // (via check.declare) later.if := .Lookup(); != nil { .recordUse(, )// redeclared object must be a variableif , := .(*Var); != nil { [] = } else { .errorf(, _UnassignableOperand, "cannot assign to %s", ) = true }continue }// declare new variable := NewVar(.Pos(), .pkg, , nil) [] = if != "_" { = append(, ) } .recordDef(, ) }// create dummy variables where the lhs is invalidfor , := range {if == nil { [] = NewVar([].Pos(), .pkg, "_", nil) } } .initVars(, , nil)// process function literals in rhs expressions before scope changes .processDelayed()iflen() == 0 && ! { .softErrorf(, _NoNewVar, "no new variables on left side of :=")return }// declare new variables // spec: "The scope of a constant or variable identifier declared inside // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl // for short variable declarations) and ends at the end of the innermost // containing block." := [len()-1].End()for , := range { .declare(, nil, , ) // id = nil: recordDef already called }}
The pages are generated with Goldsv0.4.9. (GOOS=linux GOARCH=amd64)