// Copyright 2009 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. package gc import ( "cmd/compile/internal/types" "cmd/internal/objabi" "fmt" "strings" ) // To enable tracing support (-t flag), set enableTrace to true. const enableTrace = false var trace bool var traceIndent []byte var skipDowidthForTracing bool func tracePrint(title string, n *Node) func(np **Node) { indent := traceIndent // guard against nil var pos, op string var tc uint8 if n != nil { pos = linestr(n.Pos) op = n.Op.String() tc = n.Typecheck() } skipDowidthForTracing = true defer func() { skipDowidthForTracing = false }() fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc) traceIndent = append(traceIndent, ". "...) return func(np **Node) { traceIndent = traceIndent[:len(traceIndent)-2] // if we have a result, use that if np != nil { n = *np } // guard against nil // use outer pos, op so we don't get empty pos/op if n == nil (nicer output) var tc uint8 var typ *types.Type if n != nil { pos = linestr(n.Pos) op = n.Op.String() tc = n.Typecheck() typ = n.Type } skipDowidthForTracing = true defer func() { skipDowidthForTracing = false }() fmt.Printf("%s: %s=> %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ) } } const ( ctxStmt = 1 << iota // evaluated at statement level ctxExpr // evaluated in value context ctxType // evaluated in type context ctxCallee // call-only expressions are ok ctxMultiOK // multivalue function returns are ok ctxAssign // assigning to expression ) // type checks the whole tree of an expression. // calculates expression types. // evaluates compile time constants. // marks variables that escape the local frame. // rewrites n.Op to be more specific in some cases. var typecheckdefstack []*Node // resolve ONONAME to definition, if any. func resolve(n *Node) (res *Node) { if n == nil || n.Op != ONONAME { return n } // only trace if there's work to do if enableTrace && trace { defer tracePrint("resolve", n)(&res) } if n.Sym.Pkg != localpkg { if inimport { Fatalf("recursive inimport") } inimport = true expandDecl(n) inimport = false return n } r := asNode(n.Sym.Def) if r == nil { return n } if r.Op == OIOTA { if x := getIotaValue(); x >= 0 { return nodintconst(x) } return n } return r } func typecheckslice(l []*Node, top int) { for i := range l { l[i] = typecheck(l[i], top) } } var _typekind = []string{ TINT: "int", TUINT: "uint", TINT8: "int8", TUINT8: "uint8", TINT16: "int16", TUINT16: "uint16", TINT32: "int32", TUINT32: "uint32", TINT64: "int64", TUINT64: "uint64", TUINTPTR: "uintptr", TCOMPLEX64: "complex64", TCOMPLEX128: "complex128", TFLOAT32: "float32", TFLOAT64: "float64", TBOOL: "bool", TSTRING: "string", TPTR: "pointer", TUNSAFEPTR: "unsafe.Pointer", TSTRUCT: "struct", TINTER: "interface", TCHAN: "chan", TMAP: "map", TARRAY: "array", TSLICE: "slice", TFUNC: "func", TNIL: "nil", TIDEAL: "untyped number", } func typekind(t *types.Type) string { if t.IsSlice() { return "slice" } et := t.Etype if int(et) < len(_typekind) { s := _typekind[et] if s != "" { return s } } return fmt.Sprintf("etype=%d", et) } func cycleFor(start *Node) []*Node { // Find the start node in typecheck_tcstack. // We know that it must exist because each time we mark // a node with n.SetTypecheck(2) we push it on the stack, // and each time we mark a node with n.SetTypecheck(2) we // pop it from the stack. We hit a cycle when we encounter // a node marked 2 in which case is must be on the stack. i := len(typecheck_tcstack) - 1 for i > 0 && typecheck_tcstack[i] != start { i-- } // collect all nodes with same Op var cycle []*Node for _, n := range typecheck_tcstack[i:] { if n.Op == start.Op { cycle = append(cycle, n) } } return cycle } func cycleTrace(cycle []*Node) string { var s string for i, n := range cycle { s += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cycle[(i+1)%len(cycle)]) } return s } var typecheck_tcstack []*Node // typecheck type checks node n. // The result of typecheck MUST be assigned back to n, e.g. // n.Left = typecheck(n.Left, top) func typecheck(n *Node, top int) (res *Node) { // cannot type check until all the source has been parsed if !typecheckok { Fatalf("early typecheck") } if n == nil { return nil } // only trace if there's work to do if enableTrace && trace { defer tracePrint("typecheck", n)(&res) } lno := setlineno(n) // Skip over parens. for n.Op == OPAREN { n = n.Left } // Resolve definition of name and value of iota lazily. n = resolve(n) // Skip typecheck if already done. // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed. if n.Typecheck() == 1 { switch n.Op { case ONAME, OTYPE, OLITERAL, OPACK: break default: lineno = lno return n } } if n.Typecheck() == 2 { // Typechecking loop. Trying printing a meaningful message, // otherwise a stack trace of typechecking. switch n.Op { // We can already diagnose variables used as types. case ONAME: if top&(ctxExpr|ctxType) == ctxType { yyerror("%v is not a type", n) } case OTYPE: // Only report a type cycle if we are expecting a type. // Otherwise let other code report an error. if top&ctxType == ctxType { // A cycle containing only alias types is an error // since it would expand indefinitely when aliases // are substituted. cycle := cycleFor(n) for _, n1 := range cycle { if n1.Name != nil && !n1.Name.Param.Alias { // Cycle is ok. But if n is an alias type and doesn't // have a type yet, we have a recursive type declaration // with aliases that we can't handle properly yet. // Report an error rather than crashing later. if n.Name != nil && n.Name.Param.Alias && n.Type == nil { lineno = n.Pos Fatalf("cannot handle alias type declaration (issue #25838): %v", n) } lineno = lno return n } } yyerrorl(n.Pos, "invalid recursive type alias %v%s", n, cycleTrace(cycle)) } case OLITERAL: if top&(ctxExpr|ctxType) == ctxType { yyerror("%v is not a type", n) break } yyerrorl(n.Pos, "constant definition loop%s", cycleTrace(cycleFor(n))) } if nsavederrors+nerrors == 0 { var trace string for i := len(typecheck_tcstack) - 1; i >= 0; i-- { x := typecheck_tcstack[i] trace += fmt.Sprintf("\n\t%v %v", x.Line(), x) } yyerror("typechecking loop involving %v%s", n, trace) } lineno = lno return n } n.SetTypecheck(2) typecheck_tcstack = append(typecheck_tcstack, n) n = typecheck1(n, top) n.SetTypecheck(1) last := len(typecheck_tcstack) - 1 typecheck_tcstack[last] = nil typecheck_tcstack = typecheck_tcstack[:last] lineno = lno return n } // indexlit implements typechecking of untyped values as // array/slice indexes. It is almost equivalent to defaultlit // but also accepts untyped numeric values representable as // value of type int (see also checkmake for comparison). // The result of indexlit MUST be assigned back to n, e.g. // n.Left = indexlit(n.Left) func indexlit(n *Node) *Node { if n != nil && n.Type != nil && n.Type.Etype == TIDEAL { return defaultlit(n, types.Types[TINT]) } return n } // The result of typecheck1 MUST be assigned back to n, e.g. // n.Left = typecheck1(n.Left, top) func typecheck1(n *Node, top int) (res *Node) { if enableTrace && trace { defer tracePrint("typecheck1", n)(&res) } switch n.Op { case OLITERAL, ONAME, ONONAME, OTYPE: if n.Sym == nil { break } if n.Op == ONAME && n.SubOp() != 0 && top&ctxCallee == 0 { yyerror("use of builtin %v not in function call", n.Sym) n.Type = nil return n } typecheckdef(n) if n.Op == ONONAME { n.Type = nil return n } } ok := 0 switch n.Op { // until typecheck is complete, do nothing. default: Dump("typecheck", n) Fatalf("typecheck %v", n.Op) // names case OLITERAL: ok |= ctxExpr if n.Type == nil && n.Val().Ctype() == CTSTR { n.Type = types.Idealstring } case ONONAME: ok |= ctxExpr case ONAME: if n.Name.Decldepth == 0 { n.Name.Decldepth = decldepth } if n.SubOp() != 0 { ok |= ctxCallee break } if top&ctxAssign == 0 { // not a write to the variable if n.isBlank() { yyerror("cannot use _ as value") n.Type = nil return n } n.Name.SetUsed(true) } ok |= ctxExpr case OPACK: yyerror("use of package %v without selector", n.Sym) n.Type = nil return n case ODDD: break // types (ODEREF is with exprs) case OTYPE: ok |= ctxType if n.Type == nil { return n } case OTARRAY: ok |= ctxType r := typecheck(n.Right, ctxType) if r.Type == nil { n.Type = nil return n } var t *types.Type if n.Left == nil { t = types.NewSlice(r.Type) } else if n.Left.Op == ODDD { if !n.Diag() { n.SetDiag(true) yyerror("use of [...] array outside of array literal") } n.Type = nil return n } else { n.Left = indexlit(typecheck(n.Left, ctxExpr)) l := n.Left if consttype(l) != CTINT { switch { case l.Type == nil: // Error already reported elsewhere. case l.Type.IsInteger() && l.Op != OLITERAL: yyerror("non-constant array bound %v", l) default: yyerror("invalid array bound %v", l) } n.Type = nil return n } v := l.Val() if doesoverflow(v, types.Types[TINT]) { yyerror("array bound is too large") n.Type = nil return n } bound := v.U.(*Mpint).Int64() if bound < 0 { yyerror("array bound must be non-negative") n.Type = nil return n } t = types.NewArray(r.Type, bound) } setTypeNode(n, t) n.Left = nil n.Right = nil checkwidth(t) case OTMAP: ok |= ctxType n.Left = typecheck(n.Left, ctxType) n.Right = typecheck(n.Right, ctxType) l := n.Left r := n.Right if l.Type == nil || r.Type == nil { n.Type = nil return n } if l.Type.NotInHeap() { yyerror("incomplete (or unallocatable) map key not allowed") } if r.Type.NotInHeap() { yyerror("incomplete (or unallocatable) map value not allowed") } setTypeNode(n, types.NewMap(l.Type, r.Type)) mapqueue = append(mapqueue, n) // check map keys when all types are settled n.Left = nil n.Right = nil case OTCHAN: ok |= ctxType n.Left = typecheck(n.Left, ctxType) l := n.Left if l.Type == nil { n.Type = nil return n } if l.Type.NotInHeap() { yyerror("chan of incomplete (or unallocatable) type not allowed") } setTypeNode(n, types.NewChan(l.Type, n.TChanDir())) n.Left = nil n.ResetAux() case OTSTRUCT: ok |= ctxType setTypeNode(n, tostruct(n.List.Slice())) n.List.Set(nil) case OTINTER: ok |= ctxType setTypeNode(n, tointerface(n.List.Slice())) case OTFUNC: ok |= ctxType setTypeNode(n, functype(n.Left, n.List.Slice(), n.Rlist.Slice())) n.Left = nil n.List.Set(nil) n.Rlist.Set(nil) // type or expr case ODEREF: n.Left = typecheck(n.Left, ctxExpr|ctxType) l := n.Left t := l.Type if t == nil { n.Type = nil return n } if l.Op == OTYPE { ok |= ctxType setTypeNode(n, types.NewPtr(l.Type)) n.Left = nil // Ensure l.Type gets dowidth'd for the backend. Issue 20174. checkwidth(l.Type) break } if !t.IsPtr() { if top&(ctxExpr|ctxStmt) != 0 { yyerror("invalid indirect of %L", n.Left) n.Type = nil return n } break } ok |= ctxExpr n.Type = t.Elem() // arithmetic exprs case OASOP, OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT, OLSH, ORSH, OMOD, OMUL, ONE, OOR, OOROR, OSUB, OXOR: var l *Node var op Op var r *Node if n.Op == OASOP { ok |= ctxStmt n.Left = typecheck(n.Left, ctxExpr) n.Right = typecheck(n.Right, ctxExpr) l = n.Left r = n.Right checkassign(n, n.Left) if l.Type == nil || r.Type == nil { n.Type = nil return n } if n.Implicit() && !okforarith[l.Type.Etype] { yyerror("invalid operation: %v (non-numeric type %v)", n, l.Type) n.Type = nil return n } // TODO(marvin): Fix Node.EType type union. op = n.SubOp() } else { ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) n.Right = typecheck(n.Right, ctxExpr) l = n.Left r = n.Right if l.Type == nil || r.Type == nil { n.Type = nil return n } op = n.Op } if op == OLSH || op == ORSH { r = defaultlit(r, types.Types[TUINT]) n.Right = r t := r.Type if !t.IsInteger() { yyerror("invalid operation: %v (shift count type %v, must be integer)", n, r.Type) n.Type = nil return n } if t.IsSigned() && !langSupported(1, 13, curpkg()) { yyerrorv("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type) n.Type = nil return n } t = l.Type if t != nil && t.Etype != TIDEAL && !t.IsInteger() { yyerror("invalid operation: %v (shift of type %v)", n, t) n.Type = nil return n } // no defaultlit for left // the outer context gives the type n.Type = l.Type break } // ideal mixed with non-ideal l, r = defaultlit2(l, r, false) n.Left = l n.Right = r if l.Type == nil || r.Type == nil { n.Type = nil return n } t := l.Type if t.Etype == TIDEAL { t = r.Type } et := t.Etype if et == TIDEAL { et = TINT } aop := OXXX if iscmp[n.Op] && t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) { // comparison is okay as long as one side is // assignable to the other. convert so they have // the same type. // // the only conversion that isn't a no-op is concrete == interface. // in that case, check comparability of the concrete type. // The conversion allocates, so only do it if the concrete type is huge. converted := false if r.Type.Etype != TBLANK { aop = assignop(l.Type, r.Type, nil) if aop != 0 { if r.Type.IsInterface() && !l.Type.IsInterface() && !IsComparable(l.Type) { yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type)) n.Type = nil return n } dowidth(l.Type) if r.Type.IsInterface() == l.Type.IsInterface() || l.Type.Width >= 1<<16 { l = nod(aop, l, nil) l.Type = r.Type l.SetTypecheck(1) n.Left = l } t = r.Type converted = true } } if !converted && l.Type.Etype != TBLANK { aop = assignop(r.Type, l.Type, nil) if aop != 0 { if l.Type.IsInterface() && !r.Type.IsInterface() && !IsComparable(r.Type) { yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type)) n.Type = nil return n } dowidth(r.Type) if r.Type.IsInterface() == l.Type.IsInterface() || r.Type.Width >= 1<<16 { r = nod(aop, r, nil) r.Type = l.Type r.SetTypecheck(1) n.Right = r } t = l.Type } } et = t.Etype } if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) { l, r = defaultlit2(l, r, true) if l.Type == nil || r.Type == nil { n.Type = nil return n } if l.Type.IsInterface() == r.Type.IsInterface() || aop == 0 { yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) n.Type = nil return n } } if !okfor[op][et] { yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) n.Type = nil return n } // okfor allows any array == array, map == map, func == func. // restrict to slice/map/func == nil and nil == slice/map/func. if l.Type.IsArray() && !IsComparable(l.Type) { yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type) n.Type = nil return n } if l.Type.IsSlice() && !l.isNil() && !r.isNil() { yyerror("invalid operation: %v (slice can only be compared to nil)", n) n.Type = nil return n } if l.Type.IsMap() && !l.isNil() && !r.isNil() { yyerror("invalid operation: %v (map can only be compared to nil)", n) n.Type = nil return n } if l.Type.Etype == TFUNC && !l.isNil() && !r.isNil() { yyerror("invalid operation: %v (func can only be compared to nil)", n) n.Type = nil return n } if l.Type.IsStruct() { if f := IncomparableField(l.Type); f != nil { yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type) n.Type = nil return n } } t = l.Type if iscmp[n.Op] { // TIDEAL includes complex constant, but only OEQ and ONE are defined for complex, // so check that the n.op is available for complex here before doing evconst. if !okfor[n.Op][TCOMPLEX128] && (Isconst(l, CTCPLX) || Isconst(r, CTCPLX)) { yyerror("invalid operation: %v (operator %v not defined on untyped complex)", n, n.Op) n.Type = nil return n } evconst(n) t = types.Idealbool if n.Op != OLITERAL { l, r = defaultlit2(l, r, true) n.Left = l n.Right = r } } if et == TSTRING && n.Op == OADD { // create OADDSTR node with list of strings in x + y + z + (w + v) + ... n.Op = OADDSTR if l.Op == OADDSTR { n.List.Set(l.List.Slice()) } else { n.List.Set1(l) } if r.Op == OADDSTR { n.List.AppendNodes(&r.List) } else { n.List.Append(r) } n.Left = nil n.Right = nil } if (op == ODIV || op == OMOD) && Isconst(r, CTINT) { if r.Val().U.(*Mpint).CmpInt64(0) == 0 { yyerror("division by zero") n.Type = nil return n } } n.Type = t case OBITNOT, ONEG, ONOT, OPLUS: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) l := n.Left t := l.Type if t == nil { n.Type = nil return n } if !okfor[n.Op][t.Etype] { yyerror("invalid operation: %v %v", n.Op, t) n.Type = nil return n } n.Type = t // exprs case OADDR: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) if n.Left.Type == nil { n.Type = nil return n } switch n.Left.Op { case OARRAYLIT, OMAPLIT, OSLICELIT, OSTRUCTLIT: n.Op = OPTRLIT default: checklvalue(n.Left, "take the address of") r := outervalue(n.Left) if r.Op == ONAME { if r.Orig != r { Fatalf("found non-orig name node %v", r) // TODO(mdempsky): What does this mean? } r.Name.SetAddrtaken(true) if r.Name.IsClosureVar() && !capturevarscomplete { // Mark the original variable as Addrtaken so that capturevars // knows not to pass it by value. // But if the capturevars phase is complete, don't touch it, // in case l.Name's containing function has not yet been compiled. r.Name.Defn.Name.SetAddrtaken(true) } } n.Left = defaultlit(n.Left, nil) if n.Left.Type == nil { n.Type = nil return n } } n.Type = types.NewPtr(n.Left.Type) case OCOMPLIT: ok |= ctxExpr n = typecheckcomplit(n) if n.Type == nil { return n } case OXDOT, ODOT: if n.Op == OXDOT { n = adddot(n) n.Op = ODOT if n.Left == nil { n.Type = nil return n } } n.Left = typecheck(n.Left, ctxExpr|ctxType) n.Left = defaultlit(n.Left, nil) t := n.Left.Type if t == nil { adderrorname(n) n.Type = nil return n } s := n.Sym if n.Left.Op == OTYPE { n = typecheckMethodExpr(n) if n.Type == nil { return n } ok = ctxExpr break } if t.IsPtr() && !t.Elem().IsInterface() { t = t.Elem() if t == nil { n.Type = nil return n } n.Op = ODOTPTR checkwidth(t) } if n.Sym.IsBlank() { yyerror("cannot refer to blank field or method") n.Type = nil return n } if lookdot(n, t, 0) == nil { // Legitimate field or method lookup failed, try to explain the error switch { case t.IsEmptyInterface(): yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type) case t.IsPtr() && t.Elem().IsInterface(): // Pointer to interface is almost always a mistake. yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type) case lookdot(n, t, 1) != nil: // Field or method matches by name, but it is not exported. yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Sym) default: if mt := lookdot(n, t, 2); mt != nil && visible(mt.Sym) { // Case-insensitive lookup. yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Sym, mt.Sym) } else { yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Sym) } } n.Type = nil return n } switch n.Op { case ODOTINTER, ODOTMETH: if top&ctxCallee != 0 { ok |= ctxCallee } else { typecheckpartialcall(n, s) ok |= ctxExpr } default: ok |= ctxExpr } case ODOTTYPE: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) l := n.Left t := l.Type if t == nil { n.Type = nil return n } if !t.IsInterface() { yyerror("invalid type assertion: %v (non-interface type %v on left)", n, t) n.Type = nil return n } if n.Right != nil { n.Right = typecheck(n.Right, ctxType) n.Type = n.Right.Type n.Right = nil if n.Type == nil { return n } } if n.Type != nil && !n.Type.IsInterface() { var missing, have *types.Field var ptr int if !implements(n.Type, t, &missing, &have, &ptr) { if have != nil && have.Sym == missing.Sym { yyerror("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+ "\t\thave %v%0S\n\t\twant %v%0S", n.Type, t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) } else if ptr != 0 { yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type, t, missing.Sym) } else if have != nil { yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+ "\t\thave %v%0S\n\t\twant %v%0S", n.Type, t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) } else { yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type, t, missing.Sym) } n.Type = nil return n } } case OINDEX: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) n.Left = implicitstar(n.Left) l := n.Left n.Right = typecheck(n.Right, ctxExpr) r := n.Right t := l.Type if t == nil || r.Type == nil { n.Type = nil return n } switch t.Etype { default: yyerror("invalid operation: %v (type %v does not support indexing)", n, t) n.Type = nil return n case TSTRING, TARRAY, TSLICE: n.Right = indexlit(n.Right) if t.IsString() { n.Type = types.Bytetype } else { n.Type = t.Elem() } why := "string" if t.IsArray() { why = "array" } else if t.IsSlice() { why = "slice" } if n.Right.Type != nil && !n.Right.Type.IsInteger() { yyerror("non-integer %s index %v", why, n.Right) break } if !n.Bounded() && Isconst(n.Right, CTINT) { x := n.Right.Int64() if x < 0 { yyerror("invalid %s index %v (index must be non-negative)", why, n.Right) } else if t.IsArray() && x >= t.NumElem() { yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem()) } else if Isconst(n.Left, CTSTR) && x >= int64(len(strlit(n.Left))) { yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(strlit(n.Left))) } else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { yyerror("invalid %s index %v (index too large)", why, n.Right) } } case TMAP: n.Right = assignconv(n.Right, t.Key(), "map index") n.Type = t.Elem() n.Op = OINDEXMAP n.ResetAux() } case ORECV: ok |= ctxStmt | ctxExpr n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) l := n.Left t := l.Type if t == nil { n.Type = nil return n } if !t.IsChan() { yyerror("invalid operation: %v (receive from non-chan type %v)", n, t) n.Type = nil return n } if !t.ChanDir().CanRecv() { yyerror("invalid operation: %v (receive from send-only type %v)", n, t) n.Type = nil return n } n.Type = t.Elem() case OSEND: ok |= ctxStmt n.Left = typecheck(n.Left, ctxExpr) n.Right = typecheck(n.Right, ctxExpr) n.Left = defaultlit(n.Left, nil) t := n.Left.Type if t == nil { n.Type = nil return n } if !t.IsChan() { yyerror("invalid operation: %v (send to non-chan type %v)", n, t) n.Type = nil return n } if !t.ChanDir().CanSend() { yyerror("invalid operation: %v (send to receive-only type %v)", n, t) n.Type = nil return n } n.Right = assignconv(n.Right, t.Elem(), "send") if n.Right.Type == nil { n.Type = nil return n } n.Type = nil case OSLICEHEADER: // Errors here are Fatalf instead of yyerror because only the compiler // can construct an OSLICEHEADER node. // Components used in OSLICEHEADER that are supplied by parsed source code // have already been typechecked in e.g. OMAKESLICE earlier. ok |= ctxExpr t := n.Type if t == nil { Fatalf("no type specified for OSLICEHEADER") } if !t.IsSlice() { Fatalf("invalid type %v for OSLICEHEADER", n.Type) } if n.Left == nil || n.Left.Type == nil || !n.Left.Type.IsUnsafePtr() { Fatalf("need unsafe.Pointer for OSLICEHEADER") } if x := n.List.Len(); x != 2 { Fatalf("expected 2 params (len, cap) for OSLICEHEADER, got %d", x) } n.Left = typecheck(n.Left, ctxExpr) l := typecheck(n.List.First(), ctxExpr) c := typecheck(n.List.Second(), ctxExpr) l = defaultlit(l, types.Types[TINT]) c = defaultlit(c, types.Types[TINT]) if Isconst(l, CTINT) && l.Int64() < 0 { Fatalf("len for OSLICEHEADER must be non-negative") } if Isconst(c, CTINT) && c.Int64() < 0 { Fatalf("cap for OSLICEHEADER must be non-negative") } if Isconst(l, CTINT) && Isconst(c, CTINT) && l.Val().U.(*Mpint).Cmp(c.Val().U.(*Mpint)) > 0 { Fatalf("len larger than cap for OSLICEHEADER") } n.List.SetFirst(l) n.List.SetSecond(c) case OMAKESLICECOPY: // Errors here are Fatalf instead of yyerror because only the compiler // can construct an OMAKESLICECOPY node. // Components used in OMAKESCLICECOPY that are supplied by parsed source code // have already been typechecked in OMAKE and OCOPY earlier. ok |= ctxExpr t := n.Type if t == nil { Fatalf("no type specified for OMAKESLICECOPY") } if !t.IsSlice() { Fatalf("invalid type %v for OMAKESLICECOPY", n.Type) } if n.Left == nil { Fatalf("missing len argument for OMAKESLICECOPY") } if n.Right == nil { Fatalf("missing slice argument to copy for OMAKESLICECOPY") } n.Left = typecheck(n.Left, ctxExpr) n.Right = typecheck(n.Right, ctxExpr) n.Left = defaultlit(n.Left, types.Types[TINT]) if !n.Left.Type.IsInteger() && n.Type.Etype != TIDEAL { yyerror("non-integer len argument in OMAKESLICECOPY") } if Isconst(n.Left, CTINT) { if n.Left.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { Fatalf("len for OMAKESLICECOPY too large") } if n.Left.Int64() < 0 { Fatalf("len for OMAKESLICECOPY must be non-negative") } } case OSLICE, OSLICE3: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) low, high, max := n.SliceBounds() hasmax := n.Op.IsSlice3() low = typecheck(low, ctxExpr) high = typecheck(high, ctxExpr) max = typecheck(max, ctxExpr) n.Left = defaultlit(n.Left, nil) low = indexlit(low) high = indexlit(high) max = indexlit(max) n.SetSliceBounds(low, high, max) l := n.Left if l.Type == nil { n.Type = nil return n } if l.Type.IsArray() { if !islvalue(n.Left) { yyerror("invalid operation %v (slice of unaddressable value)", n) n.Type = nil return n } n.Left = nod(OADDR, n.Left, nil) n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxExpr) l = n.Left } t := l.Type var tp *types.Type if t.IsString() { if hasmax { yyerror("invalid operation %v (3-index slice of string)", n) n.Type = nil return n } n.Type = t n.Op = OSLICESTR } else if t.IsPtr() && t.Elem().IsArray() { tp = t.Elem() n.Type = types.NewSlice(tp.Elem()) dowidth(n.Type) if hasmax { n.Op = OSLICE3ARR } else { n.Op = OSLICEARR } } else if t.IsSlice() { n.Type = t } else { yyerror("cannot slice %v (type %v)", l, t) n.Type = nil return n } if low != nil && !checksliceindex(l, low, tp) { n.Type = nil return n } if high != nil && !checksliceindex(l, high, tp) { n.Type = nil return n } if max != nil && !checksliceindex(l, max, tp) { n.Type = nil return n } if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) { n.Type = nil return n } // call and call like case OCALL: typecheckslice(n.Ninit.Slice(), ctxStmt) // imported rewritten f(g()) calls (#30907) n.Left = typecheck(n.Left, ctxExpr|ctxType|ctxCallee) if n.Left.Diag() { n.SetDiag(true) } l := n.Left if l.Op == ONAME && l.SubOp() != 0 { if n.IsDDD() && l.SubOp() != OAPPEND { yyerror("invalid use of ... with builtin %v", l) } // builtin: OLEN, OCAP, etc. n.Op = l.SubOp() n.Left = n.Right n.Right = nil n = typecheck1(n, top) return n } n.Left = defaultlit(n.Left, nil) l = n.Left if l.Op == OTYPE { if n.IsDDD() { if !l.Type.Broke() { yyerror("invalid use of ... in type conversion to %v", l.Type) } n.SetDiag(true) } // pick off before type-checking arguments ok |= ctxExpr // turn CALL(type, arg) into CONV(arg) w/ type n.Left = nil n.Op = OCONV n.Type = l.Type if !onearg(n, "conversion to %v", l.Type) { n.Type = nil return n } n = typecheck1(n, top) return n } typecheckargs(n) t := l.Type if t == nil { n.Type = nil return n } checkwidth(t) switch l.Op { case ODOTINTER: n.Op = OCALLINTER case ODOTMETH: n.Op = OCALLMETH // typecheckaste was used here but there wasn't enough // information further down the call chain to know if we // were testing a method receiver for unexported fields. // It isn't necessary, so just do a sanity check. tp := t.Recv().Type if l.Left == nil || !types.Identical(l.Left.Type, tp) { Fatalf("method receiver") } default: n.Op = OCALLFUNC if t.Etype != TFUNC { name := l.String() if isBuiltinFuncName(name) && l.Name.Defn != nil { // be more specific when the function // name matches a predeclared function yyerror("cannot call non-function %s (type %v), declared at %s", name, t, linestr(l.Name.Defn.Pos)) } else { yyerror("cannot call non-function %s (type %v)", name, t) } n.Type = nil return n } } typecheckaste(OCALL, n.Left, n.IsDDD(), t.Params(), n.List, func() string { return fmt.Sprintf("argument to %v", n.Left) }) ok |= ctxStmt if t.NumResults() == 0 { break } ok |= ctxExpr if t.NumResults() == 1 { n.Type = l.Type.Results().Field(0).Type if n.Op == OCALLFUNC && n.Left.Op == ONAME && isRuntimePkg(n.Left.Sym.Pkg) && n.Left.Sym.Name == "getg" { // Emit code for runtime.getg() directly instead of calling function. // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, // so that the ordering pass can make sure to preserve the semantics of the original code // (in particular, the exact time of the function call) by introducing temporaries. // In this case, we know getg() always returns the same result within a given function // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. n.Op = OGETG } break } // multiple return if top&(ctxMultiOK|ctxStmt) == 0 { yyerror("multiple-value %v() in single-value context", l) break } n.Type = l.Type.Results() case OALIGNOF, OOFFSETOF, OSIZEOF: ok |= ctxExpr if !onearg(n, "%v", n.Op) { n.Type = nil return n } n.Type = types.Types[TUINTPTR] case OCAP, OLEN: ok |= ctxExpr if !onearg(n, "%v", n.Op) { n.Type = nil return n } n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) n.Left = implicitstar(n.Left) l := n.Left t := l.Type if t == nil { n.Type = nil return n } var ok bool if n.Op == OLEN { ok = okforlen[t.Etype] } else { ok = okforcap[t.Etype] } if !ok { yyerror("invalid argument %L for %v", l, n.Op) n.Type = nil return n } n.Type = types.Types[TINT] case OREAL, OIMAG: ok |= ctxExpr if !onearg(n, "%v", n.Op) { n.Type = nil return n } n.Left = typecheck(n.Left, ctxExpr) l := n.Left t := l.Type if t == nil { n.Type = nil return n } // Determine result type. switch t.Etype { case TIDEAL: n.Type = types.Idealfloat case TCOMPLEX64: n.Type = types.Types[TFLOAT32] case TCOMPLEX128: n.Type = types.Types[TFLOAT64] default: yyerror("invalid argument %L for %v", l, n.Op) n.Type = nil return n } case OCOMPLEX: ok |= ctxExpr typecheckargs(n) if !twoarg(n) { n.Type = nil return n } l := n.Left r := n.Right if l.Type == nil || r.Type == nil { n.Type = nil return n } l, r = defaultlit2(l, r, false) if l.Type == nil || r.Type == nil { n.Type = nil return n } n.Left = l n.Right = r if !types.Identical(l.Type, r.Type) { yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) n.Type = nil return n } var t *types.Type switch l.Type.Etype { default: yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type) n.Type = nil return n case TIDEAL: t = types.Idealcomplex case TFLOAT32: t = types.Types[TCOMPLEX64] case TFLOAT64: t = types.Types[TCOMPLEX128] } n.Type = t case OCLOSE: if !onearg(n, "%v", n.Op) { n.Type = nil return n } n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) l := n.Left t := l.Type if t == nil { n.Type = nil return n } if !t.IsChan() { yyerror("invalid operation: %v (non-chan type %v)", n, t) n.Type = nil return n } if !t.ChanDir().CanSend() { yyerror("invalid operation: %v (cannot close receive-only channel)", n) n.Type = nil return n } ok |= ctxStmt case ODELETE: ok |= ctxStmt typecheckargs(n) args := n.List if args.Len() == 0 { yyerror("missing arguments to delete") n.Type = nil return n } if args.Len() == 1 { yyerror("missing second (key) argument to delete") n.Type = nil return n } if args.Len() != 2 { yyerror("too many arguments to delete") n.Type = nil return n } l := args.First() r := args.Second() if l.Type != nil && !l.Type.IsMap() { yyerror("first argument to delete must be map; have %L", l.Type) n.Type = nil return n } args.SetSecond(assignconv(r, l.Type.Key(), "delete")) case OAPPEND: ok |= ctxExpr typecheckargs(n) args := n.List if args.Len() == 0 { yyerror("missing arguments to append") n.Type = nil return n } t := args.First().Type if t == nil { n.Type = nil return n } n.Type = t if !t.IsSlice() { if Isconst(args.First(), CTNIL) { yyerror("first argument to append must be typed slice; have untyped nil") n.Type = nil return n } yyerror("first argument to append must be slice; have %L", t) n.Type = nil return n } if n.IsDDD() { if args.Len() == 1 { yyerror("cannot use ... on first argument to append") n.Type = nil return n } if args.Len() != 2 { yyerror("too many arguments to append") n.Type = nil return n } if t.Elem().IsKind(TUINT8) && args.Second().Type.IsString() { args.SetSecond(defaultlit(args.Second(), types.Types[TSTRING])) break } args.SetSecond(assignconv(args.Second(), t.Orig, "append")) break } as := args.Slice()[1:] for i, n := range as { if n.Type == nil { continue } as[i] = assignconv(n, t.Elem(), "append") checkwidth(as[i].Type) // ensure width is calculated for backend } case OCOPY: ok |= ctxStmt | ctxExpr typecheckargs(n) if !twoarg(n) { n.Type = nil return n } n.Type = types.Types[TINT] if n.Left.Type == nil || n.Right.Type == nil { n.Type = nil return n } n.Left = defaultlit(n.Left, nil) n.Right = defaultlit(n.Right, nil) if n.Left.Type == nil || n.Right.Type == nil { n.Type = nil return n } // copy([]byte, string) if n.Left.Type.IsSlice() && n.Right.Type.IsString() { if types.Identical(n.Left.Type.Elem(), types.Bytetype) { break } yyerror("arguments to copy have different element types: %L and string", n.Left.Type) n.Type = nil return n } if !n.Left.Type.IsSlice() || !n.Right.Type.IsSlice() { if !n.Left.Type.IsSlice() && !n.Right.Type.IsSlice() { yyerror("arguments to copy must be slices; have %L, %L", n.Left.Type, n.Right.Type) } else if !n.Left.Type.IsSlice() { yyerror("first argument to copy should be slice; have %L", n.Left.Type) } else { yyerror("second argument to copy should be slice or string; have %L", n.Right.Type) } n.Type = nil return n } if !types.Identical(n.Left.Type.Elem(), n.Right.Type.Elem()) { yyerror("arguments to copy have different element types: %L and %L", n.Left.Type, n.Right.Type) n.Type = nil return n } case OCONV: ok |= ctxExpr checkwidth(n.Type) // ensure width is calculated for backend n.Left = typecheck(n.Left, ctxExpr) n.Left = convlit1(n.Left, n.Type, true, nil) t := n.Left.Type if t == nil || n.Type == nil { n.Type = nil return n } var why string n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why) if n.Op == 0 { if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() { yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why) n.SetDiag(true) } n.Op = OCONV n.Type = nil return n } switch n.Op { case OCONVNOP: if t.Etype == n.Type.Etype { switch t.Etype { case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128: // Floating point casts imply rounding and // so the conversion must be kept. n.Op = OCONV } } // do not convert to []byte literal. See CL 125796. // generated code and compiler memory footprint is better without it. case OSTR2BYTES: break case OSTR2RUNES: if n.Left.Op == OLITERAL { n = stringtoruneslit(n) } } case OMAKE: ok |= ctxExpr args := n.List.Slice() if len(args) == 0 { yyerror("missing argument to make") n.Type = nil return n } n.List.Set(nil) l := args[0] l = typecheck(l, ctxType) t := l.Type if t == nil { n.Type = nil return n } i := 1 switch t.Etype { default: yyerror("cannot make type %v", t) n.Type = nil return n case TSLICE: if i >= len(args) { yyerror("missing len argument to make(%v)", t) n.Type = nil return n } l = args[i] i++ l = typecheck(l, ctxExpr) var r *Node if i < len(args) { r = args[i] i++ r = typecheck(r, ctxExpr) } if l.Type == nil || (r != nil && r.Type == nil) { n.Type = nil return n } if !checkmake(t, "len", l) || r != nil && !checkmake(t, "cap", r) { n.Type = nil return n } if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && l.Val().U.(*Mpint).Cmp(r.Val().U.(*Mpint)) > 0 { yyerror("len larger than cap in make(%v)", t) n.Type = nil return n } n.Left = l n.Right = r n.Op = OMAKESLICE case TMAP: if i < len(args) { l = args[i] i++ l = typecheck(l, ctxExpr) l = defaultlit(l, types.Types[TINT]) if l.Type == nil { n.Type = nil return n } if !checkmake(t, "size", l) { n.Type = nil return n } n.Left = l } else { n.Left = nodintconst(0) } n.Op = OMAKEMAP case TCHAN: l = nil if i < len(args) { l = args[i] i++ l = typecheck(l, ctxExpr) l = defaultlit(l, types.Types[TINT]) if l.Type == nil { n.Type = nil return n } if !checkmake(t, "buffer", l) { n.Type = nil return n } n.Left = l } else { n.Left = nodintconst(0) } n.Op = OMAKECHAN } if i < len(args) { yyerror("too many arguments to make(%v)", t) n.Op = OMAKE n.Type = nil return n } n.Type = t case ONEW: ok |= ctxExpr args := n.List if args.Len() == 0 { yyerror("missing argument to new") n.Type = nil return n } l := args.First() l = typecheck(l, ctxType) t := l.Type if t == nil { n.Type = nil return n } if args.Len() > 1 { yyerror("too many arguments to new(%v)", t) n.Type = nil return n } n.Left = l n.Type = types.NewPtr(t) case OPRINT, OPRINTN: ok |= ctxStmt typecheckargs(n) ls := n.List.Slice() for i1, n1 := range ls { // Special case for print: int constant is int64, not int. if Isconst(n1, CTINT) { ls[i1] = defaultlit(ls[i1], types.Types[TINT64]) } else { ls[i1] = defaultlit(ls[i1], nil) } } case OPANIC: ok |= ctxStmt if !onearg(n, "panic") { n.Type = nil return n } n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, types.Types[TINTER]) if n.Left.Type == nil { n.Type = nil return n } case ORECOVER: ok |= ctxExpr | ctxStmt if n.List.Len() != 0 { yyerror("too many arguments to recover") n.Type = nil return n } n.Type = types.Types[TINTER] case OCLOSURE: ok |= ctxExpr typecheckclosure(n, top) if n.Type == nil { return n } case OITAB: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) t := n.Left.Type if t == nil { n.Type = nil return n } if !t.IsInterface() { Fatalf("OITAB of %v", t) } n.Type = types.NewPtr(types.Types[TUINTPTR]) case OIDATA: // Whoever creates the OIDATA node must know a priori the concrete type at that moment, // usually by just having checked the OITAB. Fatalf("cannot typecheck interface data %v", n) case OSPTR: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) t := n.Left.Type if t == nil { n.Type = nil return n } if !t.IsSlice() && !t.IsString() { Fatalf("OSPTR of %v", t) } if t.IsString() { n.Type = types.NewPtr(types.Types[TUINT8]) } else { n.Type = types.NewPtr(t.Elem()) } case OCLOSUREVAR: ok |= ctxExpr case OCFUNC: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) n.Type = types.Types[TUINTPTR] case OCONVNOP: ok |= ctxExpr n.Left = typecheck(n.Left, ctxExpr) // statements case OAS: ok |= ctxStmt typecheckas(n) // Code that creates temps does not bother to set defn, so do it here. if n.Left.Op == ONAME && n.Left.IsAutoTmp() { n.Left.Name.Defn = n } case OAS2: ok |= ctxStmt typecheckas2(n) case OBREAK, OCONTINUE, ODCL, OEMPTY, OGOTO, OFALL, OVARKILL, OVARLIVE: ok |= ctxStmt case OLABEL: ok |= ctxStmt decldepth++ if n.Sym.IsBlank() { // Empty identifier is valid but useless. // Eliminate now to simplify life later. // See issues 7538, 11589, 11593. n.Op = OEMPTY n.Left = nil } case ODEFER: ok |= ctxStmt n.Left = typecheck(n.Left, ctxStmt|ctxExpr) if !n.Left.Diag() { checkdefergo(n) } case OGO: ok |= ctxStmt n.Left = typecheck(n.Left, ctxStmt|ctxExpr) checkdefergo(n) case OFOR, OFORUNTIL: ok |= ctxStmt typecheckslice(n.Ninit.Slice(), ctxStmt) decldepth++ n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) if n.Left != nil { t := n.Left.Type if t != nil && !t.IsBoolean() { yyerror("non-bool %L used as for condition", n.Left) } } n.Right = typecheck(n.Right, ctxStmt) if n.Op == OFORUNTIL { typecheckslice(n.List.Slice(), ctxStmt) } typecheckslice(n.Nbody.Slice(), ctxStmt) decldepth-- case OIF: ok |= ctxStmt typecheckslice(n.Ninit.Slice(), ctxStmt) n.Left = typecheck(n.Left, ctxExpr) n.Left = defaultlit(n.Left, nil) if n.Left != nil { t := n.Left.Type if t != nil && !t.IsBoolean() { yyerror("non-bool %L used as if condition", n.Left) } } typecheckslice(n.Nbody.Slice(), ctxStmt) typecheckslice(n.Rlist.Slice(), ctxStmt) case ORETURN: ok |= ctxStmt typecheckargs(n) if Curfn == nil { yyerror("return outside function") n.Type = nil return n } if Curfn.Type.FuncType().Outnamed && n.List.Len() == 0 { break } typecheckaste(ORETURN, nil, false, Curfn.Type.Results(), n.List, func() string { return "return argument" }) case ORETJMP: ok |= ctxStmt case OSELECT: ok |= ctxStmt typecheckselect(n) case OSWITCH: ok |= ctxStmt typecheckswitch(n) case ORANGE: ok |= ctxStmt typecheckrange(n) case OTYPESW: yyerror("use of .(type) outside type switch") n.Type = nil return n case OCASE: ok |= ctxStmt typecheckslice(n.List.Slice(), ctxExpr) typecheckslice(n.Nbody.Slice(), ctxStmt) case ODCLFUNC: ok |= ctxStmt typecheckfunc(n) case ODCLCONST: ok |= ctxStmt n.Left = typecheck(n.Left, ctxExpr) case ODCLTYPE: ok |= ctxStmt n.Left = typecheck(n.Left, ctxType) checkwidth(n.Left.Type) } t := n.Type if t != nil && !t.IsFuncArgStruct() && n.Op != OTYPE { switch t.Etype { case TFUNC, // might have TANY; wait until it's called TANY, TFORW, TIDEAL, TNIL, TBLANK: break default: checkwidth(t) } } evconst(n) if n.Op == OTYPE && top&ctxType == 0 { if !n.Type.Broke() { yyerror("type %v is not an expression", n.Type) } n.Type = nil return n } if top&(ctxExpr|ctxType) == ctxType && n.Op != OTYPE { yyerror("%v is not a type", n) n.Type = nil return n } // TODO(rsc): simplify if (top&(ctxCallee|ctxExpr|ctxType) != 0) && top&ctxStmt == 0 && ok&(ctxExpr|ctxType|ctxCallee) == 0 { yyerror("%v used as value", n) n.Type = nil return n } if (top&ctxStmt != 0) && top&(ctxCallee|ctxExpr|ctxType) == 0 && ok&ctxStmt == 0 { if !n.Diag() { yyerror("%v evaluated but not used", n) n.SetDiag(true) } n.Type = nil return n } return n } func typecheckargs(n *Node) { if n.List.Len() != 1 || n.IsDDD() { typecheckslice(n.List.Slice(), ctxExpr) return } typecheckslice(n.List.Slice(), ctxExpr|ctxMultiOK) t := n.List.First().Type if t == nil || !t.IsFuncArgStruct() { return } // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...). // Save n as n.Orig for fmt.go. if n.Orig == n { n.Orig = n.sepcopy() } as := nod(OAS2, nil, nil) as.Rlist.AppendNodes(&n.List) // If we're outside of function context, then this call will // be executed during the generated init function. However, // init.go hasn't yet created it. Instead, associate the // temporary variables with dummyInitFn for now, and init.go // will reassociate them later when it's appropriate. static := Curfn == nil if static { Curfn = dummyInitFn } for _, f := range t.FieldSlice() { t := temp(f.Type) as.Ninit.Append(nod(ODCL, t, nil)) as.List.Append(t) n.List.Append(t) } if static { Curfn = nil } as = typecheck(as, ctxStmt) n.Ninit.Append(as) } func checksliceindex(l *Node, r *Node, tp *types.Type) bool { t := r.Type if t == nil { return false } if !t.IsInteger() { yyerror("invalid slice index %v (type %v)", r, t) return false } if r.Op == OLITERAL { if r.Int64() < 0 { yyerror("invalid slice index %v (index must be non-negative)", r) return false } else if tp != nil && tp.NumElem() >= 0 && r.Int64() > tp.NumElem() { yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem()) return false } else if Isconst(l, CTSTR) && r.Int64() > int64(len(strlit(l))) { yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(strlit(l))) return false } else if r.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { yyerror("invalid slice index %v (index too large)", r) return false } } return true } func checksliceconst(lo *Node, hi *Node) bool { if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && lo.Val().U.(*Mpint).Cmp(hi.Val().U.(*Mpint)) > 0 { yyerror("invalid slice index: %v > %v", lo, hi) return false } return true } func checkdefergo(n *Node) { what := "defer" if n.Op == OGO { what = "go" } switch n.Left.Op { // ok case OCALLINTER, OCALLMETH, OCALLFUNC, OCLOSE, OCOPY, ODELETE, OPANIC, OPRINT, OPRINTN, ORECOVER: return case OAPPEND, OCAP, OCOMPLEX, OIMAG, OLEN, OMAKE, OMAKESLICE, OMAKECHAN, OMAKEMAP, ONEW, OREAL, OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof if n.Left.Orig != nil && n.Left.Orig.Op == OCONV { break } yyerrorl(n.Pos, "%s discards result of %v", what, n.Left) return } // type is broken or missing, most likely a method call on a broken type // we will warn about the broken type elsewhere. no need to emit a potentially confusing error if n.Left.Type == nil || n.Left.Type.Broke() { return } if !n.Diag() { // The syntax made sure it was a call, so this must be // a conversion. n.SetDiag(true) yyerrorl(n.Pos, "%s requires function call, not conversion", what) } } // The result of implicitstar MUST be assigned back to n, e.g. // n.Left = implicitstar(n.Left) func implicitstar(n *Node) *Node { // insert implicit * if needed for fixed array t := n.Type if t == nil || !t.IsPtr() { return n } t = t.Elem() if t == nil { return n } if !t.IsArray() { return n } n = nod(ODEREF, n, nil) n.SetImplicit(true) n = typecheck(n, ctxExpr) return n } func onearg(n *Node, f string, args ...interface{}) bool { if n.Left != nil { return true } if n.List.Len() == 0 { p := fmt.Sprintf(f, args...) yyerror("missing argument to %s: %v", p, n) return false } if n.List.Len() > 1 { p := fmt.Sprintf(f, args...) yyerror("too many arguments to %s: %v", p, n) n.Left = n.List.First() n.List.Set(nil) return false } n.Left = n.List.First() n.List.Set(nil) return true } func twoarg(n *Node) bool { if n.Left != nil { return true } if n.List.Len() != 2 { if n.List.Len() < 2 { yyerror("not enough arguments in call to %v", n) } else { yyerror("too many arguments in call to %v", n) } return false } n.Left = n.List.First() n.Right = n.List.Second() n.List.Set(nil) return true } func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dostrcmp int) *types.Field { var r *types.Field for _, f := range fs.Slice() { if dostrcmp != 0 && f.Sym.Name == s.Name { return f } if dostrcmp == 2 && strings.EqualFold(f.Sym.Name, s.Name) { return f } if f.Sym != s { continue } if r != nil { if errnode != nil { yyerror("ambiguous selector %v", errnode) } else if t.IsPtr() { yyerror("ambiguous selector (%v).%v", t, s) } else { yyerror("ambiguous selector %v.%v", t, s) } break } r = f } return r } // typecheckMethodExpr checks selector expressions (ODOT) where the // base expression is a type expression (OTYPE). func typecheckMethodExpr(n *Node) (res *Node) { if enableTrace && trace { defer tracePrint("typecheckMethodExpr", n)(&res) } t := n.Left.Type // Compute the method set for t. var ms *types.Fields if t.IsInterface() { ms = t.Fields() } else { mt := methtype(t) if mt == nil { yyerror("%v undefined (type %v has no method %v)", n, t, n.Sym) n.Type = nil return n } expandmeth(mt) ms = mt.AllMethods() // The method expression T.m requires a wrapper when T // is different from m's declared receiver type. We // normally generate these wrappers while writing out // runtime type descriptors, which is always done for // types declared at package scope. However, we need // to make sure to generate wrappers for anonymous // receiver types too. if mt.Sym == nil { addsignat(t) } } s := n.Sym m := lookdot1(n, s, t, ms, 0) if m == nil { if lookdot1(n, s, t, ms, 1) != nil { yyerror("%v undefined (cannot refer to unexported method %v)", n, s) } else if _, ambig := dotpath(s, t, nil, false); ambig { yyerror("%v undefined (ambiguous selector)", n) // method or field } else { yyerror("%v undefined (type %v has no method %v)", n, t, s) } n.Type = nil return n } if !isMethodApplicable(t, m) { yyerror("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, s) n.Type = nil return n } n.Op = ONAME if n.Name == nil { n.Name = new(Name) } n.Right = newname(n.Sym) n.Sym = methodSym(t, n.Sym) n.Type = methodfunc(m.Type, n.Left.Type) n.Xoffset = 0 n.SetClass(PFUNC) // methodSym already marked n.Sym as a function. // Issue 25065. Make sure that we emit the symbol for a local method. if Ctxt.Flag_dynlink && !inimport && (t.Sym == nil || t.Sym.Pkg == localpkg) { makefuncsym(n.Sym) } return n } // isMethodApplicable reports whether method m can be called on a // value of type t. This is necessary because we compute a single // method set for both T and *T, but some *T methods are not // applicable to T receivers. func isMethodApplicable(t *types.Type, m *types.Field) bool { return t.IsPtr() || !m.Type.Recv().Type.IsPtr() || isifacemethod(m.Type) || m.Embedded == 2 } func derefall(t *types.Type) *types.Type { for t != nil && t.IsPtr() { t = t.Elem() } return t } type typeSymKey struct { t *types.Type s *types.Sym } // dotField maps (*types.Type, *types.Sym) pairs to the corresponding struct field (*types.Type with Etype==TFIELD). // It is a cache for use during usefield in walk.go, only enabled when field tracking. var dotField = map[typeSymKey]*types.Field{} func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { s := n.Sym dowidth(t) var f1 *types.Field if t.IsStruct() || t.IsInterface() { f1 = lookdot1(n, s, t, t.Fields(), dostrcmp) } var f2 *types.Field if n.Left.Type == t || n.Left.Type.Sym == nil { mt := methtype(t) if mt != nil { f2 = lookdot1(n, s, mt, mt.Methods(), dostrcmp) } } if f1 != nil { if dostrcmp > 1 || f1.Broke() { // Already in the process of diagnosing an error. return f1 } if f2 != nil { yyerror("%v is both field and method", n.Sym) } if f1.Offset == BADWIDTH { Fatalf("lookdot badwidth %v %p", f1, f1) } n.Xoffset = f1.Offset n.Type = f1.Type if objabi.Fieldtrack_enabled > 0 { dotField[typeSymKey{t.Orig, s}] = f1 } if t.IsInterface() { if n.Left.Type.IsPtr() { n.Left = nod(ODEREF, n.Left, nil) // implicitstar n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxExpr) } n.Op = ODOTINTER } return f1 } if f2 != nil { if dostrcmp > 1 { // Already in the process of diagnosing an error. return f2 } tt := n.Left.Type dowidth(tt) rcvr := f2.Type.Recv().Type if !types.Identical(rcvr, tt) { if rcvr.IsPtr() && types.Identical(rcvr.Elem(), tt) { checklvalue(n.Left, "call pointer method on") n.Left = nod(OADDR, n.Left, nil) n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxType|ctxExpr) } else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) { n.Left = nod(ODEREF, n.Left, nil) n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxType|ctxExpr) } else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) { yyerror("calling method %v with receiver %L requires explicit dereference", n.Sym, n.Left) for tt.IsPtr() { // Stop one level early for method with pointer receiver. if rcvr.IsPtr() && !tt.Elem().IsPtr() { break } n.Left = nod(ODEREF, n.Left, nil) n.Left.SetImplicit(true) n.Left = typecheck(n.Left, ctxType|ctxExpr) tt = tt.Elem() } } else { Fatalf("method mismatch: %v for %v", rcvr, tt) } } pll := n ll := n.Left for ll.Left != nil && (ll.Op == ODOT || ll.Op == ODOTPTR || ll.Op == ODEREF) { pll = ll ll = ll.Left } if pll.Implicit() && ll.Type.IsPtr() && ll.Type.Sym != nil && asNode(ll.Type.Sym.Def) != nil && asNode(ll.Type.Sym.Def).Op == OTYPE { // It is invalid to automatically dereference a named pointer type when selecting a method. // Make n.Left == ll to clarify error message. n.Left = ll return nil } n.Sym = methodSym(n.Left.Type, f2.Sym) n.Xoffset = f2.Offset n.Type = f2.Type n.Op = ODOTMETH return f2 } return nil } func nokeys(l Nodes) bool { for _, n := range l.Slice() { if n.Op == OKEY || n.Op == OSTRUCTKEY { return false } } return true } func hasddd(t *types.Type) bool { for _, tl := range t.Fields().Slice() { if tl.IsDDD() { return true } } return false } // typecheck assignment: type list = expression list func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes, desc func() string) { var t *types.Type var i int lno := lineno defer func() { lineno = lno }() if tstruct.Broke() { return } var n *Node if nl.Len() == 1 { n = nl.First() } n1 := tstruct.NumFields() n2 := nl.Len() if !hasddd(tstruct) { if n2 > n1 { goto toomany } if n2 < n1 { goto notenough } } else { if !isddd { if n2 < n1-1 { goto notenough } } else { if n2 > n1 { goto toomany } if n2 < n1 { goto notenough } } } i = 0 for _, tl := range tstruct.Fields().Slice() { t = tl.Type if tl.IsDDD() { if isddd { if i >= nl.Len() { goto notenough } if nl.Len()-i > 1 { goto toomany } n = nl.Index(i) setlineno(n) if n.Type != nil { nl.SetIndex(i, assignconvfn(n, t, desc)) } return } // TODO(mdempsky): Make into ... call with implicit slice. for ; i < nl.Len(); i++ { n = nl.Index(i) setlineno(n) if n.Type != nil { nl.SetIndex(i, assignconvfn(n, t.Elem(), desc)) } } return } if i >= nl.Len() { goto notenough } n = nl.Index(i) setlineno(n) if n.Type != nil { nl.SetIndex(i, assignconvfn(n, t, desc)) } i++ } if i < nl.Len() { goto toomany } if isddd { if call != nil { yyerror("invalid use of ... in call to %v", call) } else { yyerror("invalid use of ... in %v", op) } } return notenough: if n == nil || !n.Diag() { details := errorDetails(nl, tstruct, isddd) if call != nil { // call is the expression being called, not the overall call. // Method expressions have the form T.M, and the compiler has // rewritten those to ONAME nodes but left T in Left. if call.isMethodExpression() { yyerror("not enough arguments in call to method expression %v%s", call, details) } else { yyerror("not enough arguments in call to %v%s", call, details) } } else { yyerror("not enough arguments to %v%s", op, details) } if n != nil { n.SetDiag(true) } } return toomany: details := errorDetails(nl, tstruct, isddd) if call != nil { yyerror("too many arguments in call to %v%s", call, details) } else { yyerror("too many arguments to %v%s", op, details) } } func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string { // If we don't know any type at a call site, let's suppress any return // message signatures. See Issue https://golang.org/issues/19012. if tstruct == nil { return "" } // If any node has an unknown type, suppress it as well for _, n := range nl.Slice() { if n.Type == nil { return "" } } return fmt.Sprintf("\n\thave %s\n\twant %v", nl.retsigerr(isddd), tstruct) } // sigrepr is a type's representation to the outside world, // in string representations of return signatures // e.g in error messages about wrong arguments to return. func sigrepr(t *types.Type) string { switch t { case types.Idealstring: return "string" case types.Idealbool: return "bool" } if t.Etype == TIDEAL { // "untyped number" is not commonly used // outside of the compiler, so let's use "number". // TODO(mdempsky): Revisit this. return "number" } return t.String() } // retsigerr returns the signature of the types // at the respective return call site of a function. func (nl Nodes) retsigerr(isddd bool) string { if nl.Len() < 1 { return "()" } var typeStrings []string for _, n := range nl.Slice() { typeStrings = append(typeStrings, sigrepr(n.Type)) } ddd := "" if isddd { ddd = "..." } return fmt.Sprintf("(%s%s)", strings.Join(typeStrings, ", "), ddd) } // type check composite func fielddup(name string, hash map[string]bool) { if hash[name] { yyerror("duplicate field name in struct literal: %s", name) return } hash[name] = true } // iscomptype reports whether type t is a composite literal type. func iscomptype(t *types.Type) bool { switch t.Etype { case TARRAY, TSLICE, TSTRUCT, TMAP: return true default: return false } } // pushtype adds elided type information for composite literals if // appropriate, and returns the resulting expression. func pushtype(n *Node, t *types.Type) *Node { if n == nil || n.Op != OCOMPLIT || n.Right != nil { return n } switch { case iscomptype(t): // For T, return T{...}. n.Right = typenod(t) case t.IsPtr() && iscomptype(t.Elem()): // For *T, return &T{...}. n.Right = typenod(t.Elem()) n = nodl(n.Pos, OADDR, n, nil) n.SetImplicit(true) } return n } // The result of typecheckcomplit MUST be assigned back to n, e.g. // n.Left = typecheckcomplit(n.Left) func typecheckcomplit(n *Node) (res *Node) { if enableTrace && trace { defer tracePrint("typecheckcomplit", n)(&res) } lno := lineno defer func() { lineno = lno }() if n.Right == nil { yyerrorl(n.Pos, "missing type in composite literal") n.Type = nil return n } // Save original node (including n.Right) n.Orig = n.copy() setlineno(n.Right) // Need to handle [...]T arrays specially. if n.Right.Op == OTARRAY && n.Right.Left != nil && n.Right.Left.Op == ODDD { n.Right.Right = typecheck(n.Right.Right, ctxType) if n.Right.Right.Type == nil { n.Type = nil return n } elemType := n.Right.Right.Type length := typecheckarraylit(elemType, -1, n.List.Slice(), "array literal") n.Op = OARRAYLIT n.Type = types.NewArray(elemType, length) n.Right = nil return n } n.Right = typecheck(n.Right, ctxType) t := n.Right.Type if t == nil { n.Type = nil return n } n.Type = t switch t.Etype { default: yyerror("invalid composite literal type %v", t) n.Type = nil case TARRAY: typecheckarraylit(t.Elem(), t.NumElem(), n.List.Slice(), "array literal") n.Op = OARRAYLIT n.Right = nil case TSLICE: length := typecheckarraylit(t.Elem(), -1, n.List.Slice(), "slice literal") n.Op = OSLICELIT n.Right = nodintconst(length) case TMAP: var cs constSet for i3, l := range n.List.Slice() { setlineno(l) if l.Op != OKEY { n.List.SetIndex(i3, typecheck(l, ctxExpr)) yyerror("missing key in map literal") continue } r := l.Left r = pushtype(r, t.Key()) r = typecheck(r, ctxExpr) l.Left = assignconv(r, t.Key(), "map key") cs.add(lineno, l.Left, "key", "map literal") r = l.Right r = pushtype(r, t.Elem()) r = typecheck(r, ctxExpr) l.Right = assignconv(r, t.Elem(), "map value") } n.Op = OMAPLIT n.Right = nil case TSTRUCT: // Need valid field offsets for Xoffset below. dowidth(t) errored := false if n.List.Len() != 0 && nokeys(n.List) { // simple list of variables ls := n.List.Slice() for i, n1 := range ls { setlineno(n1) n1 = typecheck(n1, ctxExpr) ls[i] = n1 if i >= t.NumFields() { if !errored { yyerror("too many values in %v", n) errored = true } continue } f := t.Field(i) s := f.Sym if s != nil && !types.IsExported(s.Name) && s.Pkg != localpkg { yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, t) } // No pushtype allowed here. Must name fields for that. n1 = assignconv(n1, f.Type, "field value") n1 = nodSym(OSTRUCTKEY, n1, f.Sym) n1.Xoffset = f.Offset ls[i] = n1 } if len(ls) < t.NumFields() { yyerror("too few values in %v", n) } } else { hash := make(map[string]bool) // keyed list ls := n.List.Slice() for i, l := range ls { setlineno(l) if l.Op == OKEY { key := l.Left l.Op = OSTRUCTKEY l.Left = l.Right l.Right = nil // An OXDOT uses the Sym field to hold // the field to the right of the dot, // so s will be non-nil, but an OXDOT // is never a valid struct literal key. if key.Sym == nil || key.Op == OXDOT || key.Sym.IsBlank() { yyerror("invalid field name %v in struct initializer", key) l.Left = typecheck(l.Left, ctxExpr) continue } // Sym might have resolved to name in other top-level // package, because of import dot. Redirect to correct sym // before we do the lookup. s := key.Sym if s.Pkg != localpkg && types.IsExported(s.Name) { s1 := lookup(s.Name) if s1.Origpkg == s.Pkg { s = s1 } } l.Sym = s } if l.Op != OSTRUCTKEY { if !errored { yyerror("mixture of field:value and value initializers") errored = true } ls[i] = typecheck(ls[i], ctxExpr) continue } f := lookdot1(nil, l.Sym, t, t.Fields(), 0) if f == nil { if ci := lookdot1(nil, l.Sym, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup. if visible(ci.Sym) { yyerror("unknown field '%v' in struct literal of type %v (but does have %v)", l.Sym, t, ci.Sym) } else if nonexported(l.Sym) && l.Sym.Name == ci.Sym.Name { // Ensure exactness before the suggestion. yyerror("cannot refer to unexported field '%v' in struct literal of type %v", l.Sym, t) } else { yyerror("unknown field '%v' in struct literal of type %v", l.Sym, t) } continue } var f *types.Field p, _ := dotpath(l.Sym, t, &f, true) if p == nil || f.IsMethod() { yyerror("unknown field '%v' in struct literal of type %v", l.Sym, t) continue } // dotpath returns the parent embedded types in reverse order. var ep []string for ei := len(p) - 1; ei >= 0; ei-- { ep = append(ep, p[ei].field.Sym.Name) } ep = append(ep, l.Sym.Name) yyerror("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t) continue } fielddup(f.Sym.Name, hash) l.Xoffset = f.Offset // No pushtype allowed here. Tried and rejected. l.Left = typecheck(l.Left, ctxExpr) l.Left = assignconv(l.Left, f.Type, "field value") } } n.Op = OSTRUCTLIT n.Right = nil } return n } // typecheckarraylit type-checks a sequence of slice/array literal elements. func typecheckarraylit(elemType *types.Type, bound int64, elts []*Node, ctx string) int64 { // If there are key/value pairs, create a map to keep seen // keys so we can check for duplicate indices. var indices map[int64]bool for _, elt := range elts { if elt.Op == OKEY { indices = make(map[int64]bool) break } } var key, length int64 for i, elt := range elts { setlineno(elt) vp := &elts[i] if elt.Op == OKEY { elt.Left = typecheck(elt.Left, ctxExpr) key = indexconst(elt.Left) if key < 0 { if !elt.Left.Diag() { if key == -2 { yyerror("index too large") } else { yyerror("index must be non-negative integer constant") } elt.Left.SetDiag(true) } key = -(1 << 30) // stay negative for a while } vp = &elt.Right } r := *vp r = pushtype(r, elemType) r = typecheck(r, ctxExpr) *vp = assignconv(r, elemType, ctx) if key >= 0 { if indices != nil { if indices[key] { yyerror("duplicate index in %s: %d", ctx, key) } else { indices[key] = true } } if bound >= 0 && key >= bound { yyerror("array index %d out of bounds [0:%d]", key, bound) bound = -1 } } key++ if key > length { length = key } } return length } // visible reports whether sym is exported or locally defined. func visible(sym *types.Sym) bool { return sym != nil && (types.IsExported(sym.Name) || sym.Pkg == localpkg) } // nonexported reports whether sym is an unexported field. func nonexported(sym *types.Sym) bool { return sym != nil && !types.IsExported(sym.Name) } // lvalue etc func islvalue(n *Node) bool { switch n.Op { case OINDEX: if n.Left.Type != nil && n.Left.Type.IsArray() { return islvalue(n.Left) } if n.Left.Type != nil && n.Left.Type.IsString() { return false } fallthrough case ODEREF, ODOTPTR, OCLOSUREVAR: return true case ODOT: return islvalue(n.Left) case ONAME: if n.Class() == PFUNC { return false } return true } return false } func checklvalue(n *Node, verb string) { if !islvalue(n) { yyerror("cannot %s %v", verb, n) } } func checkassign(stmt *Node, n *Node) { // Variables declared in ORANGE are assigned on every iteration. if n.Name == nil || n.Name.Defn != stmt || stmt.Op == ORANGE { r := outervalue(n) if r.Op == ONAME { r.Name.SetAssigned(true) if r.Name.IsClosureVar() { r.Name.Defn.Name.SetAssigned(true) } } } if islvalue(n) { return } if n.Op == OINDEXMAP { n.SetIndexMapLValue(true) return } // have already complained about n being invalid if n.Type == nil { return } if n.Op == ODOT && n.Left.Op == OINDEXMAP { yyerror("cannot assign to struct field %v in map", n) } else { yyerror("cannot assign to %v", n) } n.Type = nil } func checkassignlist(stmt *Node, l Nodes) { for _, n := range l.Slice() { checkassign(stmt, n) } } // samesafeexpr checks whether it is safe to reuse one of l and r // instead of computing both. samesafeexpr assumes that l and r are // used in the same statement or expression. In order for it to be // safe to reuse l or r, they must: // * be the same expression // * not have side-effects (no function calls, no channel ops); // however, panics are ok // * not cause inappropriate aliasing; e.g. two string to []byte // conversions, must result in two distinct slices // // The handling of OINDEXMAP is subtle. OINDEXMAP can occur both // as an lvalue (map assignment) and an rvalue (map access). This is // currently OK, since the only place samesafeexpr gets used on an // lvalue expression is for OSLICE and OAPPEND optimizations, and it // is correct in those settings. func samesafeexpr(l *Node, r *Node) bool { if l.Op != r.Op || !types.Identical(l.Type, r.Type) { return false } switch l.Op { case ONAME, OCLOSUREVAR: return l == r case ODOT, ODOTPTR: return l.Sym != nil && r.Sym != nil && l.Sym == r.Sym && samesafeexpr(l.Left, r.Left) case ODEREF, OCONVNOP, ONOT, OBITNOT, OPLUS, ONEG: return samesafeexpr(l.Left, r.Left) case OCONV: // Some conversions can't be reused, such as []byte(str). // Allow only numeric-ish types. This is a bit conservative. return issimple[l.Type.Etype] && samesafeexpr(l.Left, r.Left) case OINDEX, OINDEXMAP, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD: return samesafeexpr(l.Left, r.Left) && samesafeexpr(l.Right, r.Right) case OLITERAL: return eqval(l.Val(), r.Val()) } return false } // type check assignment. // if this assignment is the definition of a var on the left side, // fill in the var's type. func typecheckas(n *Node) { if enableTrace && trace { defer tracePrint("typecheckas", n)(nil) } // delicate little dance. // the definition of n may refer to this assignment // as its definition, in which case it will call typecheckas. // in that case, do not call typecheck back, or it will cycle. // if the variable has a type (ntype) then typechecking // will not look at defn, so it is okay (and desirable, // so that the conversion below happens). n.Left = resolve(n.Left) if n.Left.Name == nil || n.Left.Name.Defn != n || n.Left.Name.Param.Ntype != nil { n.Left = typecheck(n.Left, ctxExpr|ctxAssign) } // Use ctxMultiOK so we can emit an "N variables but M values" error // to be consistent with typecheckas2 (#26616). n.Right = typecheck(n.Right, ctxExpr|ctxMultiOK) checkassign(n, n.Left) if n.Right != nil && n.Right.Type != nil { if n.Right.Type.IsFuncArgStruct() { yyerror("assignment mismatch: 1 variable but %v returns %d values", n.Right.Left, n.Right.Type.NumFields()) // Multi-value RHS isn't actually valid for OAS; nil out // to indicate failed typechecking. n.Right.Type = nil } else if n.Left.Type != nil { n.Right = assignconv(n.Right, n.Left.Type, "assignment") } } if n.Left.Name != nil && n.Left.Name.Defn == n && n.Left.Name.Param.Ntype == nil { n.Right = defaultlit(n.Right, nil) n.Left.Type = n.Right.Type } // second half of dance. // now that right is done, typecheck the left // just to get it over with. see dance above. n.SetTypecheck(1) if n.Left.Typecheck() == 0 { n.Left = typecheck(n.Left, ctxExpr|ctxAssign) } if !n.Left.isBlank() { checkwidth(n.Left.Type) // ensure width is calculated for backend } } func checkassignto(src *types.Type, dst *Node) { var why string if assignop(src, dst.Type, &why) == 0 { yyerror("cannot assign %v to %L in multiple assignment%s", src, dst, why) return } } func typecheckas2(n *Node) { if enableTrace && trace { defer tracePrint("typecheckas2", n)(nil) } ls := n.List.Slice() for i1, n1 := range ls { // delicate little dance. n1 = resolve(n1) ls[i1] = n1 if n1.Name == nil || n1.Name.Defn != n || n1.Name.Param.Ntype != nil { ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign) } } cl := n.List.Len() cr := n.Rlist.Len() if cl > 1 && cr == 1 { n.Rlist.SetFirst(typecheck(n.Rlist.First(), ctxExpr|ctxMultiOK)) } else { typecheckslice(n.Rlist.Slice(), ctxExpr) } checkassignlist(n, n.List) var l *Node var r *Node if cl == cr { // easy ls := n.List.Slice() rs := n.Rlist.Slice() for il, nl := range ls { nr := rs[il] if nl.Type != nil && nr.Type != nil { rs[il] = assignconv(nr, nl.Type, "assignment") } if nl.Name != nil && nl.Name.Defn == n && nl.Name.Param.Ntype == nil { rs[il] = defaultlit(rs[il], nil) nl.Type = rs[il].Type } } goto out } l = n.List.First() r = n.Rlist.First() // x,y,z = f() if cr == 1 { if r.Type == nil { goto out } switch r.Op { case OCALLMETH, OCALLINTER, OCALLFUNC: if !r.Type.IsFuncArgStruct() { break } cr = r.Type.NumFields() if cr != cl { goto mismatch } n.Op = OAS2FUNC n.Right = r n.Rlist.Set(nil) for i, l := range n.List.Slice() { f := r.Type.Field(i) if f.Type != nil && l.Type != nil { checkassignto(f.Type, l) } if l.Name != nil && l.Name.Defn == n && l.Name.Param.Ntype == nil { l.Type = f.Type } } goto out } } // x, ok = y if cl == 2 && cr == 1 { if r.Type == nil { goto out } switch r.Op { case OINDEXMAP, ORECV, ODOTTYPE: switch r.Op { case OINDEXMAP: n.Op = OAS2MAPR case ORECV: n.Op = OAS2RECV case ODOTTYPE: n.Op = OAS2DOTTYPE r.Op = ODOTTYPE2 } n.Right = r n.Rlist.Set(nil) if l.Type != nil { checkassignto(r.Type, l) } if l.Name != nil && l.Name.Defn == n { l.Type = r.Type } l := n.List.Second() if l.Type != nil && !l.Type.IsBoolean() { checkassignto(types.Types[TBOOL], l) } if l.Name != nil && l.Name.Defn == n && l.Name.Param.Ntype == nil { l.Type = types.Types[TBOOL] } goto out } } mismatch: switch r.Op { default: yyerror("assignment mismatch: %d variables but %d values", cl, cr) case OCALLFUNC, OCALLMETH, OCALLINTER: yyerror("assignment mismatch: %d variables but %v returns %d values", cl, r.Left, cr) } // second half of dance out: n.SetTypecheck(1) ls = n.List.Slice() for i1, n1 := range ls { if n1.Typecheck() == 0 { ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign) } } } // type check function definition func typecheckfunc(n *Node) { if enableTrace && trace { defer tracePrint("typecheckfunc", n)(nil) } for _, ln := range n.Func.Dcl { if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) { ln.Name.Decldepth = 1 } } n.Func.Nname = typecheck(n.Func.Nname, ctxExpr|ctxAssign) t := n.Func.Nname.Type if t == nil { return } n.Type = t t.FuncType().Nname = asTypesNode(n.Func.Nname) rcvr := t.Recv() if rcvr != nil && n.Func.Shortname != nil { m := addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0) if m == nil { return } n.Func.Nname.Sym = methodSym(rcvr.Type, n.Func.Shortname) declare(n.Func.Nname, PFUNC) } if Ctxt.Flag_dynlink && !inimport && n.Func.Nname != nil { makefuncsym(n.Func.Nname.Sym) } } // The result of stringtoruneslit MUST be assigned back to n, e.g. // n.Left = stringtoruneslit(n.Left) func stringtoruneslit(n *Node) *Node { if n.Left.Op != OLITERAL || n.Left.Val().Ctype() != CTSTR { Fatalf("stringtoarraylit %v", n) } var l []*Node s := strlit(n.Left) i := 0 for _, r := range s { l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r)))) i++ } nn := nod(OCOMPLIT, nil, typenod(n.Type)) nn.List.Set(l) nn = typecheck(nn, ctxExpr) return nn } var mapqueue []*Node func checkMapKeys() { for _, n := range mapqueue { k := n.Type.MapType().Key if !k.Broke() && !IsComparable(k) { yyerrorl(n.Pos, "invalid map key type %v", k) } } mapqueue = nil } func setUnderlying(t, underlying *types.Type) { if underlying.Etype == TFORW { // This type isn't computed yet; when it is, update n. underlying.ForwardType().Copyto = append(underlying.ForwardType().Copyto, t) return } n := asNode(t.Nod) ft := t.ForwardType() cache := t.Cache // TODO(mdempsky): Fix Type rekinding. *t = *underlying // Restore unnecessarily clobbered attributes. t.Nod = asTypesNode(n) t.Sym = n.Sym if n.Name != nil { t.Vargen = n.Name.Vargen } t.Cache = cache t.SetDeferwidth(false) // spec: "The declared type does not inherit any methods bound // to the existing type, but the method set of an interface // type [...] remains unchanged." if !t.IsInterface() { *t.Methods() = types.Fields{} *t.AllMethods() = types.Fields{} } // Propagate go:notinheap pragma from the Name to the Type. if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma&NotInHeap != 0 { t.SetNotInHeap(true) } // Update types waiting on this type. for _, w := range ft.Copyto { setUnderlying(w, t) } // Double-check use of type as embedded type. if ft.Embedlineno.IsKnown() { if t.IsPtr() || t.IsUnsafePtr() { yyerrorl(ft.Embedlineno, "embedded type cannot be a pointer") } } } func typecheckdeftype(n *Node) { if enableTrace && trace { defer tracePrint("typecheckdeftype", n)(nil) } n.SetTypecheck(1) n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, ctxType) t := n.Name.Param.Ntype.Type if t == nil { n.SetDiag(true) n.Type = nil } else if n.Type == nil { n.SetDiag(true) } else { // copy new type and clear fields // that don't come along. setUnderlying(n.Type, t) } } func typecheckdef(n *Node) { if enableTrace && trace { defer tracePrint("typecheckdef", n)(nil) } lno := setlineno(n) if n.Op == ONONAME { if !n.Diag() { n.SetDiag(true) // Note: adderrorname looks for this string and // adds context about the outer expression yyerrorl(lineno, "undefined: %v", n.Sym) } lineno = lno return } if n.Walkdef() == 1 { lineno = lno return } typecheckdefstack = append(typecheckdefstack, n) if n.Walkdef() == 2 { flusherrors() fmt.Printf("typecheckdef loop:") for i := len(typecheckdefstack) - 1; i >= 0; i-- { n := typecheckdefstack[i] fmt.Printf(" %v", n.Sym) } fmt.Printf("\n") Fatalf("typecheckdef loop") } n.SetWalkdef(2) if n.Type != nil || n.Sym == nil { // builtin or no name goto ret } switch n.Op { default: Fatalf("typecheckdef %v", n.Op) case OLITERAL: if n.Name.Param.Ntype != nil { n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, ctxType) n.Type = n.Name.Param.Ntype.Type n.Name.Param.Ntype = nil if n.Type == nil { n.SetDiag(true) goto ret } } e := n.Name.Defn n.Name.Defn = nil if e == nil { Dump("typecheckdef nil defn", n) yyerrorl(n.Pos, "xxx") } e = typecheck(e, ctxExpr) if e.Type == nil { goto ret } if !e.isGoConst() { if !e.Diag() { if Isconst(e, CTNIL) { yyerrorl(n.Pos, "const initializer cannot be nil") } else { yyerrorl(n.Pos, "const initializer %v is not a constant", e) } e.SetDiag(true) } goto ret } t := n.Type if t != nil { if !okforconst[t.Etype] { yyerrorl(n.Pos, "invalid constant type %v", t) goto ret } if !e.Type.IsUntyped() && !types.Identical(t, e.Type) { yyerrorl(n.Pos, "cannot use %L as type %v in const initializer", e, t) goto ret } e = convlit(e, t) } n.SetVal(e.Val()) n.Type = e.Type case ONAME: if n.Name.Param.Ntype != nil { n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, ctxType) n.Type = n.Name.Param.Ntype.Type if n.Type == nil { n.SetDiag(true) goto ret } } if n.Type != nil { break } if n.Name.Defn == nil { if n.SubOp() != 0 { // like OPRINTN break } if nsavederrors+nerrors > 0 { // Can have undefined variables in x := foo // that make x have an n.name.Defn == nil. // If there are other errors anyway, don't // bother adding to the noise. break } Fatalf("var without type, init: %v", n.Sym) } if n.Name.Defn.Op == ONAME { n.Name.Defn = typecheck(n.Name.Defn, ctxExpr) n.Type = n.Name.Defn.Type break } n.Name.Defn = typecheck(n.Name.Defn, ctxStmt) // fills in n.Type case OTYPE: if p := n.Name.Param; p.Alias { // Type alias declaration: Simply use the rhs type - no need // to create a new type. // If we have a syntax error, p.Ntype may be nil. if p.Ntype != nil { p.Ntype = typecheck(p.Ntype, ctxType) n.Type = p.Ntype.Type if n.Type == nil { n.SetDiag(true) goto ret } // For package-level type aliases, set n.Sym.Def so we can identify // it as a type alias during export. See also #31959. if n.Name.Curfn == nil { n.Sym.Def = asTypesNode(p.Ntype) } } break } // regular type declaration defercheckwidth() n.SetWalkdef(1) setTypeNode(n, types.New(TFORW)) n.Type.Sym = n.Sym nerrors0 := nerrors typecheckdeftype(n) if n.Type.Etype == TFORW && nerrors > nerrors0 { // Something went wrong during type-checking, // but it was reported. Silence future errors. n.Type.SetBroke(true) } resumecheckwidth() } ret: if n.Op != OLITERAL && n.Type != nil && n.Type.IsUntyped() { Fatalf("got %v for %v", n.Type, n) } last := len(typecheckdefstack) - 1 if typecheckdefstack[last] != n { Fatalf("typecheckdefstack mismatch") } typecheckdefstack[last] = nil typecheckdefstack = typecheckdefstack[:last] lineno = lno n.SetWalkdef(1) } func checkmake(t *types.Type, arg string, n *Node) bool { if !n.Type.IsInteger() && n.Type.Etype != TIDEAL { yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type) return false } // Do range checks for constants before defaultlit // to avoid redundant "constant NNN overflows int" errors. switch consttype(n) { case CTINT, CTRUNE, CTFLT, CTCPLX: n.SetVal(toint(n.Val())) if n.Val().U.(*Mpint).CmpInt64(0) < 0 { yyerror("negative %s argument in make(%v)", arg, t) return false } if n.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 { yyerror("%s argument too large in make(%v)", arg, t) return false } } // defaultlit is necessary for non-constants too: n might be 1.1< 0 { return } switch n.Op { case OIF: if !Isconst(n.Left, CTBOOL) || n.Nbody.Len() > 0 || n.Rlist.Len() > 0 { return } case OFOR: if !Isconst(n.Left, CTBOOL) || n.Left.Bool() { return } default: return } } fn.Nbody.Set([]*Node{nod(OEMPTY, nil, nil)}) } func deadcodeslice(nn Nodes) { var lastLabel = -1 for i, n := range nn.Slice() { if n != nil && n.Op == OLABEL { lastLabel = i } } for i, n := range nn.Slice() { // Cut is set to true when all nodes after i'th position // should be removed. // In other words, it marks whole slice "tail" as dead. cut := false if n == nil { continue } if n.Op == OIF { n.Left = deadcodeexpr(n.Left) if Isconst(n.Left, CTBOOL) { var body Nodes if n.Left.Bool() { n.Rlist = Nodes{} body = n.Nbody } else { n.Nbody = Nodes{} body = n.Rlist } // If "then" or "else" branch ends with panic or return statement, // it is safe to remove all statements after this node. // isterminating is not used to avoid goto-related complications. // We must be careful not to deadcode-remove labels, as they // might be the target of a goto. See issue 28616. if body := body.Slice(); len(body) != 0 { switch body[(len(body) - 1)].Op { case ORETURN, ORETJMP, OPANIC: if i > lastLabel { cut = true } } } } } deadcodeslice(n.Ninit) deadcodeslice(n.Nbody) deadcodeslice(n.List) deadcodeslice(n.Rlist) if cut { *nn.slice = nn.Slice()[:i+1] break } } } func deadcodeexpr(n *Node) *Node { // Perform dead-code elimination on short-circuited boolean // expressions involving constants with the intent of // producing a constant 'if' condition. switch n.Op { case OANDAND: n.Left = deadcodeexpr(n.Left) n.Right = deadcodeexpr(n.Right) if Isconst(n.Left, CTBOOL) { if n.Left.Bool() { return n.Right // true && x => x } else { return n.Left // false && x => false } } case OOROR: n.Left = deadcodeexpr(n.Left) n.Right = deadcodeexpr(n.Right) if Isconst(n.Left, CTBOOL) { if n.Left.Bool() { return n.Left // true || x => true } else { return n.Right // false || x => x } } } return n } // setTypeNode sets n to an OTYPE node representing t. func setTypeNode(n *Node, t *types.Type) { n.Op = OTYPE n.Type = t n.Type.Nod = asTypesNode(n) } // getIotaValue returns the current value for "iota", // or -1 if not within a ConstSpec. func getIotaValue() int64 { if i := len(typecheckdefstack); i > 0 { if x := typecheckdefstack[i-1]; x.Op == OLITERAL { return x.Iota() } } if Curfn != nil && Curfn.Iota() >= 0 { return Curfn.Iota() } return -1 } // curpkg returns the current package, based on Curfn. func curpkg() *types.Pkg { fn := Curfn if fn == nil { // Initialization expressions for package-scope variables. return localpkg } // TODO(mdempsky): Standardize on either ODCLFUNC or ONAME for // Curfn, rather than mixing them. if fn.Op == ODCLFUNC { fn = fn.Func.Nname } return fnpkg(fn) }