Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
4019 lines (3518 sloc)
88.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 { | |
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 | |
} | |