From 530585b713fa823f79af40a93773937a202f4b0d Mon Sep 17 00:00:00 2001 From: HowJMay Date: Fri, 22 Jan 2021 01:12:51 +0800 Subject: [PATCH] cmd/compile: better error msg for impossible type assertions Revise the error message under the condition that type assertion has mismatching pointer/non-pointer type, which means this PR adds a better error hint for the situation that a user writes x.(*I), but they meant to write x.(I). In this situation, compiler would check out whether the the missing methods are implemented by the dereferenced object. If the dereferenced object implements these missing methods, then return error message ` does not have (but does)` Fixes #43673 --- src/cmd/compile/internal/gc/typecheck.go | 4024 ++++++++++++++++++++++ test/fixedbugs/issue43673.go | 21 + 2 files changed, 4045 insertions(+) create mode 100644 src/cmd/compile/internal/gc/typecheck.go create mode 100644 test/fixedbugs/issue43673.go diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go new file mode 100644 index 0000000000..d80bfb95fd --- /dev/null +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -0,0 +1,4024 @@ +// 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" + "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.IsUntyped() { + return fmt.Sprintf("%v", t) + } + 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.UntypedString + } + + 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 + if (l.Type == types.UntypedFloat || l.Type == types.UntypedComplex) && r.Op == OLITERAL { + n.Type = types.UntypedInt + } + + break + } + + // For "x == x && len(s)", it's better to report that "len(s)" (type int) + // can't be used with "&&" than to report that "x == x" (type untyped bool) + // can't be converted to int (see issue #41500). + if n.Op == OANDAND || n.Op == OOROR { + if !n.Left.Type.IsBoolean() { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Left.Type)) + n.Type = nil + return n + } + if !n.Right.Type.IsBoolean() { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Right.Type)) + n.Type = nil + return n + } + } + + // 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) + if aop != OXXX { + 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) + if aop != OXXX { + 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 t.Etype == TIDEAL { + t = mixUntyped(l.Type, r.Type) + } + if dt := defaultType(t); !okfor[op][dt.Etype] { + 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 + } + } + + if iscmp[n.Op] { + evconst(n) + t = types.UntypedBool + 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][defaultType(t).Etype] { + yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(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 { + var missing2 *types.Field + if implements(derefall(n.Type), t, &missing2, &have, &ptr) && missing2 == nil { + yyerror("impossible type assertion:\n\t%v does not implement %v (but %v does)", n.Type, t, derefall(n.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.Int64Val() + 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(n.Left.StringVal())) { + yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.StringVal())) + } 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.Int64Val() < 0 { + Fatalf("len for OSLICEHEADER must be non-negative") + } + + if Isconst(c, CTINT) && c.Int64Val() < 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.Int64Val() < 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.UntypedFloat + 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.UntypedComplex + + 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, why = convertop(n.Left.Op == OLITERAL, t, n.Type) + if n.Op == OXXX { + 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 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.Int64Val() < 0 { + yyerror("invalid slice index %v (index must be non-negative)", r) + return false + } else if tp != nil && tp.NumElem() >= 0 && r.Int64Val() > 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.Int64Val() > int64(len(l.StringVal())) { + yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.StringVal())) + 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 +} + +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 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 + } else { + n.SetOpt(f1) + } + + 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() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && 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() && n.Type != nil) { + 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.sigerr(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, isddd bool) string { + switch t { + case types.UntypedString: + return "string" + case types.UntypedBool: + 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" + } + + // Turn []T... argument to ...T for clearer error message. + if isddd { + if !t.IsSlice() { + Fatalf("bad type for ... argument: %v", t) + } + return "..." + t.Elem().String() + } + return t.String() +} + +// sigerr returns the signature of the types at the call or return. +func (nl Nodes) sigerr(isddd bool) string { + if nl.Len() < 1 { + return "()" + } + + var typeStrings []string + for i, n := range nl.Slice() { + isdddArg := isddd && i == nl.Len()-1 + typeStrings = append(typeStrings, sigrepr(n.Type, isdddArg)) + } + + return fmt.Sprintf("(%s)", strings.Join(typeStrings, ", ")) +} + +// 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 + } + + switch { + case n.Op == ODOT && n.Left.Op == OINDEXMAP: + yyerror("cannot assign to struct field %v in map", n) + case (n.Op == OINDEX && n.Left.Type.IsString()) || n.Op == OSLICESTR: + yyerror("cannot assign to %v (strings are immutable)", n) + case n.Op == OLITERAL && n.Sym != nil && n.isGoConst(): + yyerror("cannot assign to %v (declared const)", n) + default: + 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) { + if op, why := assignop(src, dst.Type); op == OXXX { + 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 + i := 0 + for _, r := range n.Left.StringVal() { + 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, np **Node) bool { + n := *np + 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: + v := toint(n.Val()).U.(*Mpint) + if v.CmpInt64(0) < 0 { + yyerror("negative %s argument in make(%v)", arg, t) + return false + } + if v.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.BoolVal() { + 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.BoolVal() { + 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.BoolVal() { + 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.BoolVal() { + 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) +} diff --git a/test/fixedbugs/issue43673.go b/test/fixedbugs/issue43673.go new file mode 100644 index 0000000000..acc08d9c65 --- /dev/null +++ b/test/fixedbugs/issue43673.go @@ -0,0 +1,21 @@ +// errorcheck + +// Copyright 2021 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. + +// Issue 43673 +package main + +type I interface { + M() +} + +type T struct{} + +func (t *T) M() {} + +func main() { + var i I + _ = i.(*I) // ERROR "*I does not implement I (but I does)" +}