| // 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/internal/obj" |
| "fmt" |
| "math" |
| "strings" |
| ) |
| |
| /* |
| * type check 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 *NodeList |
| |
| /* |
| * resolve ONONAME to definition, if any. |
| */ |
| func resolve(n *Node) *Node { |
| if n != nil && n.Op == ONONAME && n.Sym != nil { |
| r := n.Sym.Def |
| if r != nil { |
| if r.Op != OIOTA { |
| n = r |
| } else if n.Iota >= 0 { |
| n = Nodintconst(int64(n.Iota)) |
| } |
| } |
| } |
| |
| return n |
| } |
| |
| func typechecklist(l *NodeList, top int) { |
| for ; l != nil; l = l.Next { |
| typecheck(&l.N, 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", |
| TPTR32: "pointer", |
| TPTR64: "pointer", |
| TUNSAFEPTR: "unsafe.Pointer", |
| TSTRUCT: "struct", |
| TINTER: "interface", |
| TCHAN: "chan", |
| TMAP: "map", |
| TARRAY: "array", |
| TFUNC: "func", |
| TNIL: "nil", |
| TIDEAL: "untyped number", |
| } |
| |
| var typekind_buf string |
| |
| func typekind(t *Type) string { |
| if Isslice(t) { |
| return "slice" |
| } |
| et := int(t.Etype) |
| if 0 <= et && et < len(_typekind) { |
| s := _typekind[et] |
| if s != "" { |
| return s |
| } |
| } |
| typekind_buf = fmt.Sprintf("etype=%d", et) |
| return typekind_buf |
| } |
| |
| /* |
| * sprint_depchain prints a dependency chain |
| * of nodes into fmt. |
| * It is used by typecheck in the case of OLITERAL nodes |
| * to print constant definition loops. |
| */ |
| func sprint_depchain(fmt_ *string, stack *NodeList, cur *Node, first *Node) { |
| for l := stack; l != nil; l = l.Next { |
| if l.N.Op == cur.Op { |
| if l.N != first { |
| sprint_depchain(fmt_, l.Next, l.N, first) |
| } |
| *fmt_ += fmt.Sprintf("\n\t%v: %v uses %v", l.N.Line(), Nconv(l.N, 0), Nconv(cur, 0)) |
| return |
| } |
| } |
| } |
| |
| /* |
| * type check node *np. |
| * replaces *np with a new pointer in some cases. |
| * returns the final value of *np as a convenience. |
| */ |
| |
| var typecheck_tcstack *NodeList |
| var typecheck_tcfree *NodeList |
| |
| func typecheck(np **Node, top int) *Node { |
| // cannot type check until all the source has been parsed |
| if typecheckok == 0 { |
| Fatal("early typecheck") |
| } |
| |
| n := *np |
| if n == nil { |
| return nil |
| } |
| |
| lno := int(setlineno(n)) |
| |
| // Skip over parens. |
| for n.Op == OPAREN { |
| n = n.Left |
| } |
| |
| // Resolve definition of name and value of iota lazily. |
| n = resolve(n) |
| |
| *np = 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 = int32(lno) |
| return n |
| } |
| } |
| |
| if n.Typecheck == 2 { |
| // Typechecking loop. Trying printing a meaningful message, |
| // otherwise a stack trace of typechecking. |
| var fmt_ string |
| switch n.Op { |
| // We can already diagnose variables used as types. |
| case ONAME: |
| if top&(Erv|Etype) == Etype { |
| Yyerror("%v is not a type", Nconv(n, 0)) |
| } |
| |
| case OLITERAL: |
| if top&(Erv|Etype) == Etype { |
| Yyerror("%v is not a type", Nconv(n, 0)) |
| break |
| } |
| |
| fmt_ = "" |
| sprint_depchain(&fmt_, typecheck_tcstack, n, n) |
| yyerrorl(int(n.Lineno), "constant definition loop%s", fmt_) |
| } |
| |
| if nsavederrors+nerrors == 0 { |
| fmt_ = "" |
| for l := typecheck_tcstack; l != nil; l = l.Next { |
| fmt_ += fmt.Sprintf("\n\t%v %v", l.N.Line(), Nconv(l.N, 0)) |
| } |
| Yyerror("typechecking loop involving %v%s", Nconv(n, 0), fmt_) |
| } |
| |
| lineno = int32(lno) |
| return n |
| } |
| |
| n.Typecheck = 2 |
| |
| var l *NodeList |
| if typecheck_tcfree != nil { |
| l = typecheck_tcfree |
| typecheck_tcfree = l.Next |
| } else { |
| l = new(NodeList) |
| } |
| l.Next = typecheck_tcstack |
| l.N = n |
| typecheck_tcstack = l |
| |
| typecheck1(&n, top) |
| *np = n |
| n.Typecheck = 1 |
| |
| if typecheck_tcstack != l { |
| Fatal("typecheck stack out of sync") |
| } |
| typecheck_tcstack = l.Next |
| l.Next = typecheck_tcfree |
| typecheck_tcfree = l |
| |
| lineno = int32(lno) |
| return n |
| } |
| |
| /* |
| * does n contain a call or receive operation? |
| */ |
| func callrecv(n *Node) bool { |
| if n == nil { |
| return false |
| } |
| |
| switch n.Op { |
| case OCALL, |
| OCALLMETH, |
| OCALLINTER, |
| OCALLFUNC, |
| ORECV, |
| OCAP, |
| OLEN, |
| OCOPY, |
| ONEW, |
| OAPPEND, |
| ODELETE: |
| return true |
| } |
| |
| return callrecv(n.Left) || callrecv(n.Right) || callrecv(n.Ntest) || callrecv(n.Nincr) || callrecvlist(n.Ninit) || callrecvlist(n.Nbody) || callrecvlist(n.Nelse) || callrecvlist(n.List) || callrecvlist(n.Rlist) |
| } |
| |
| func callrecvlist(l *NodeList) bool { |
| for ; l != nil; l = l.Next { |
| if callrecv(l.N) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // indexlit implements typechecking of untyped values as |
| // array/slice indexes. It is equivalent to defaultlit |
| // except for constants of numerical kind, which are acceptable |
| // whenever they can be represented by a value of type int. |
| func indexlit(np **Node) { |
| n := *np |
| if n == nil || !isideal(n.Type) { |
| return |
| } |
| switch consttype(n) { |
| case CTINT, |
| CTRUNE, |
| CTFLT, |
| CTCPLX: |
| defaultlit(np, Types[TINT]) |
| } |
| |
| defaultlit(np, nil) |
| } |
| |
| func typecheck1(np **Node, top int) { |
| var et int |
| var aop int |
| var op int |
| var ptr int |
| var l *Node |
| var r *Node |
| var lo *Node |
| var mid *Node |
| var hi *Node |
| var ok int |
| var ntop int |
| var t *Type |
| var tp *Type |
| var missing *Type |
| var have *Type |
| var badtype *Type |
| var v Val |
| var why string |
| var x int64 |
| |
| n := *np |
| |
| if n.Sym != nil { |
| if n.Op == ONAME && n.Etype != 0 && top&Ecall == 0 { |
| Yyerror("use of builtin %v not in function call", Sconv(n.Sym, 0)) |
| goto error |
| } |
| |
| typecheckdef(n) |
| if n.Op == ONONAME { |
| goto error |
| } |
| } |
| |
| *np = n |
| |
| reswitch: |
| ok = 0 |
| switch n.Op { |
| // until typecheck is complete, do nothing. |
| default: |
| Dump("typecheck", n) |
| |
| Fatal("typecheck %v", Oconv(int(n.Op), 0)) |
| |
| /* |
| * names |
| */ |
| case OLITERAL: |
| ok |= Erv |
| |
| if n.Type == nil && n.Val.Ctype == CTSTR { |
| n.Type = idealstring |
| } |
| goto ret |
| |
| case ONONAME: |
| ok |= Erv |
| goto ret |
| |
| case ONAME: |
| if n.Decldepth == 0 { |
| n.Decldepth = decldepth |
| } |
| if n.Etype != 0 { |
| ok |= Ecall |
| goto ret |
| } |
| |
| if top&Easgn == 0 { |
| // not a write to the variable |
| if isblank(n) { |
| Yyerror("cannot use _ as value") |
| goto error |
| } |
| |
| n.Used = 1 |
| } |
| |
| if top&Ecall == 0 && isunsafebuiltin(n) { |
| Yyerror("%v is not an expression, must be called", Nconv(n, 0)) |
| goto error |
| } |
| |
| ok |= Erv |
| goto ret |
| |
| case OPACK: |
| Yyerror("use of package %v without selector", Sconv(n.Sym, 0)) |
| goto error |
| |
| case ODDD: |
| break |
| |
| /* |
| * types (OIND is with exprs) |
| */ |
| case OTYPE: |
| ok |= Etype |
| |
| if n.Type == nil { |
| goto error |
| } |
| |
| case OTARRAY: |
| ok |= Etype |
| t = typ(TARRAY) |
| l = n.Left |
| r = n.Right |
| if l == nil { |
| t.Bound = -1 // slice |
| } else if l.Op == ODDD { |
| t.Bound = -100 // to be filled in |
| if top&Ecomplit == 0 && n.Diag == 0 { |
| t.Broke = 1 |
| n.Diag = 1 |
| Yyerror("use of [...] array outside of array literal") |
| } |
| } else { |
| l = typecheck(&n.Left, Erv) |
| switch consttype(l) { |
| case CTINT, |
| CTRUNE: |
| v = l.Val |
| |
| case CTFLT: |
| v = toint(l.Val) |
| |
| default: |
| if l.Type != nil && Isint[l.Type.Etype] != 0 && l.Op != OLITERAL { |
| Yyerror("non-constant array bound %v", Nconv(l, 0)) |
| } else { |
| Yyerror("invalid array bound %v", Nconv(l, 0)) |
| } |
| goto error |
| } |
| |
| t.Bound = Mpgetfix(v.U.Xval) |
| if doesoverflow(v, Types[TINT]) { |
| Yyerror("array bound is too large") |
| goto error |
| } else if t.Bound < 0 { |
| Yyerror("array bound must be non-negative") |
| goto error |
| } |
| } |
| |
| typecheck(&r, Etype) |
| if r.Type == nil { |
| goto error |
| } |
| t.Type = r.Type |
| n.Op = OTYPE |
| n.Type = t |
| n.Left = nil |
| n.Right = nil |
| if t.Bound != -100 { |
| checkwidth(t) |
| } |
| |
| case OTMAP: |
| ok |= Etype |
| l = typecheck(&n.Left, Etype) |
| r = typecheck(&n.Right, Etype) |
| if l.Type == nil || r.Type == nil { |
| goto error |
| } |
| n.Op = OTYPE |
| n.Type = maptype(l.Type, r.Type) |
| n.Left = nil |
| n.Right = nil |
| |
| case OTCHAN: |
| ok |= Etype |
| l = typecheck(&n.Left, Etype) |
| if l.Type == nil { |
| goto error |
| } |
| t = typ(TCHAN) |
| t.Type = l.Type |
| t.Chan = n.Etype |
| n.Op = OTYPE |
| n.Type = t |
| n.Left = nil |
| n.Etype = 0 |
| |
| case OTSTRUCT: |
| ok |= Etype |
| n.Op = OTYPE |
| n.Type = tostruct(n.List) |
| if n.Type == nil || n.Type.Broke != 0 { |
| goto error |
| } |
| n.List = nil |
| |
| case OTINTER: |
| ok |= Etype |
| n.Op = OTYPE |
| n.Type = tointerface(n.List) |
| if n.Type == nil { |
| goto error |
| } |
| |
| case OTFUNC: |
| ok |= Etype |
| n.Op = OTYPE |
| n.Type = functype(n.Left, n.List, n.Rlist) |
| if n.Type == nil { |
| goto error |
| } |
| |
| /* |
| * type or expr |
| */ |
| case OIND: |
| ntop = Erv | Etype |
| |
| if top&Eaddr == 0 { |
| ntop |= Eindir |
| } |
| ntop |= top & Ecomplit |
| l = typecheck(&n.Left, ntop) |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| if l.Op == OTYPE { |
| ok |= Etype |
| n.Op = OTYPE |
| n.Type = Ptrto(l.Type) |
| n.Left = nil |
| goto ret |
| } |
| |
| if Isptr[t.Etype] == 0 { |
| if top&(Erv|Etop) != 0 { |
| Yyerror("invalid indirect of %v", Nconv(n.Left, obj.FmtLong)) |
| goto error |
| } |
| |
| goto ret |
| } |
| |
| ok |= Erv |
| n.Type = t.Type |
| goto ret |
| |
| /* |
| * arithmetic exprs |
| */ |
| case OASOP: |
| ok |= Etop |
| |
| l = typecheck(&n.Left, Erv) |
| r = typecheck(&n.Right, Erv) |
| checkassign(n, n.Left) |
| if l.Type == nil || r.Type == nil { |
| goto error |
| } |
| op = int(n.Etype) |
| goto arith |
| |
| case OADD, |
| OAND, |
| OANDAND, |
| OANDNOT, |
| ODIV, |
| OEQ, |
| OGE, |
| OGT, |
| OLE, |
| OLT, |
| OLSH, |
| ORSH, |
| OMOD, |
| OMUL, |
| ONE, |
| OOR, |
| OOROR, |
| OSUB, |
| OXOR: |
| ok |= Erv |
| l = typecheck(&n.Left, Erv|top&Eiota) |
| r = typecheck(&n.Right, Erv|top&Eiota) |
| if l.Type == nil || r.Type == nil { |
| goto error |
| } |
| op = int(n.Op) |
| goto arith |
| |
| case OCOM, |
| OMINUS, |
| ONOT, |
| OPLUS: |
| ok |= Erv |
| l = typecheck(&n.Left, Erv|top&Eiota) |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| if okfor[n.Op][t.Etype] == 0 { |
| Yyerror("invalid operation: %v %v", Oconv(int(n.Op), 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| n.Type = t |
| goto ret |
| |
| /* |
| * exprs |
| */ |
| case OADDR: |
| ok |= Erv |
| |
| typecheck(&n.Left, Erv|Eaddr) |
| if n.Left.Type == nil { |
| goto error |
| } |
| checklvalue(n.Left, "take the address of") |
| r = outervalue(n.Left) |
| for l = n.Left; l != r; l = l.Left { |
| l.Addrtaken = 1 |
| if l.Closure != nil { |
| l.Closure.Addrtaken = 1 |
| } |
| } |
| |
| if l.Orig != l && l.Op == ONAME { |
| Fatal("found non-orig name node %v", Nconv(l, 0)) |
| } |
| l.Addrtaken = 1 |
| if l.Closure != nil { |
| l.Closure.Addrtaken = 1 |
| } |
| defaultlit(&n.Left, nil) |
| l = n.Left |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| n.Type = Ptrto(t) |
| goto ret |
| |
| case OCOMPLIT: |
| ok |= Erv |
| typecheckcomplit(&n) |
| if n.Type == nil { |
| goto error |
| } |
| goto ret |
| |
| case OXDOT: |
| n = adddot(n) |
| n.Op = ODOT |
| if n.Left == nil { |
| goto error |
| } |
| fallthrough |
| |
| // fall through |
| case ODOT: |
| typecheck(&n.Left, Erv|Etype) |
| |
| defaultlit(&n.Left, nil) |
| if n.Right.Op != ONAME { |
| Yyerror("rhs of . must be a name") // impossible |
| goto error |
| } |
| |
| t = n.Left.Type |
| if t == nil { |
| adderrorname(n) |
| goto error |
| } |
| |
| r = n.Right |
| |
| if n.Left.Op == OTYPE { |
| if !looktypedot(n, t, 0) { |
| if looktypedot(n, t, 1) { |
| Yyerror("%v undefined (cannot refer to unexported method %v)", Nconv(n, 0), Sconv(n.Right.Sym, 0)) |
| } else { |
| Yyerror("%v undefined (type %v has no method %v)", Nconv(n, 0), Tconv(t, 0), Sconv(n.Right.Sym, 0)) |
| } |
| goto error |
| } |
| |
| if n.Type.Etype != TFUNC || n.Type.Thistuple != 1 { |
| Yyerror("type %v has no method %v", Tconv(n.Left.Type, 0), Sconv(n.Right.Sym, obj.FmtShort)) |
| n.Type = nil |
| goto error |
| } |
| |
| n.Op = ONAME |
| n.Sym = n.Right.Sym |
| n.Type = methodfunc(n.Type, n.Left.Type) |
| n.Xoffset = 0 |
| n.Class = PFUNC |
| ok = Erv |
| goto ret |
| } |
| |
| if Isptr[t.Etype] != 0 && t.Type.Etype != TINTER { |
| t = t.Type |
| if t == nil { |
| goto error |
| } |
| n.Op = ODOTPTR |
| checkwidth(t) |
| } |
| |
| if isblank(n.Right) { |
| Yyerror("cannot refer to blank field or method") |
| goto error |
| } |
| |
| if !lookdot(n, t, 0) { |
| if lookdot(n, t, 1) { |
| Yyerror("%v undefined (cannot refer to unexported field or method %v)", Nconv(n, 0), Sconv(n.Right.Sym, 0)) |
| } else { |
| Yyerror("%v undefined (type %v has no field or method %v)", Nconv(n, 0), Tconv(n.Left.Type, 0), Sconv(n.Right.Sym, 0)) |
| } |
| goto error |
| } |
| |
| switch n.Op { |
| case ODOTINTER, |
| ODOTMETH: |
| if top&Ecall != 0 { |
| ok |= Ecall |
| } else { |
| typecheckpartialcall(n, r) |
| ok |= Erv |
| } |
| |
| default: |
| ok |= Erv |
| } |
| |
| goto ret |
| |
| case ODOTTYPE: |
| ok |= Erv |
| typecheck(&n.Left, Erv) |
| defaultlit(&n.Left, nil) |
| l = n.Left |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| if !Isinter(t) { |
| Yyerror("invalid type assertion: %v (non-interface type %v on left)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| if n.Right != nil { |
| typecheck(&n.Right, Etype) |
| n.Type = n.Right.Type |
| n.Right = nil |
| if n.Type == nil { |
| goto error |
| } |
| } |
| |
| if n.Type != nil && n.Type.Etype != TINTER { |
| 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%v\n\t\twant %v%v", Tconv(n.Type, 0), Tconv(t, 0), Sconv(missing.Sym, 0), Sconv(have.Sym, 0), Tconv(have.Type, obj.FmtShort|obj.FmtByte), Sconv(missing.Sym, 0), Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) |
| } else if ptr != 0 { |
| Yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", Tconv(n.Type, 0), Tconv(t, 0), Sconv(missing.Sym, 0)) |
| } else if have != nil { |
| Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", Tconv(n.Type, 0), Tconv(t, 0), Sconv(missing.Sym, 0), Sconv(have.Sym, 0), Tconv(have.Type, obj.FmtShort|obj.FmtByte), Sconv(missing.Sym, 0), Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) |
| } else { |
| Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", Tconv(n.Type, 0), Tconv(t, 0), Sconv(missing.Sym, 0)) |
| } |
| goto error |
| } |
| } |
| |
| goto ret |
| |
| case OINDEX: |
| ok |= Erv |
| typecheck(&n.Left, Erv) |
| defaultlit(&n.Left, nil) |
| implicitstar(&n.Left) |
| l = n.Left |
| typecheck(&n.Right, Erv) |
| r = n.Right |
| t = l.Type |
| if t == nil || r.Type == nil { |
| goto error |
| } |
| switch t.Etype { |
| default: |
| Yyerror("invalid operation: %v (type %v does not support indexing)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| |
| case TSTRING, |
| TARRAY: |
| indexlit(&n.Right) |
| if t.Etype == TSTRING { |
| n.Type = Types[TUINT8] |
| } else { |
| n.Type = t.Type |
| } |
| why = "string" |
| if t.Etype == TARRAY { |
| if Isfixedarray(t) { |
| why = "array" |
| } else { |
| why = "slice" |
| } |
| } |
| |
| if n.Right.Type != nil && Isint[n.Right.Type.Etype] == 0 { |
| Yyerror("non-integer %s index %v", why, Nconv(n.Right, 0)) |
| break |
| } |
| |
| if Isconst(n.Right, CTINT) { |
| x = Mpgetfix(n.Right.Val.U.Xval) |
| if x < 0 { |
| Yyerror("invalid %s index %v (index must be non-negative)", why, Nconv(n.Right, 0)) |
| } else if Isfixedarray(t) && t.Bound > 0 && x >= t.Bound { |
| Yyerror("invalid array index %v (out of bounds for %d-element array)", Nconv(n.Right, 0), t.Bound) |
| } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val.U.Sval.S)) { |
| Yyerror("invalid string index %v (out of bounds for %d-byte string)", Nconv(n.Right, 0), len(n.Left.Val.U.Sval.S)) |
| } else if Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 { |
| Yyerror("invalid %s index %v (index too large)", why, Nconv(n.Right, 0)) |
| } |
| } |
| |
| case TMAP: |
| n.Etype = 0 |
| defaultlit(&n.Right, t.Down) |
| if n.Right.Type != nil { |
| n.Right = assignconv(n.Right, t.Down, "map index") |
| } |
| n.Type = t.Type |
| n.Op = OINDEXMAP |
| } |
| |
| goto ret |
| |
| case ORECV: |
| ok |= Etop | Erv |
| typecheck(&n.Left, Erv) |
| defaultlit(&n.Left, nil) |
| l = n.Left |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| if t.Etype != TCHAN { |
| Yyerror("invalid operation: %v (receive from non-chan type %v)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| if t.Chan&Crecv == 0 { |
| Yyerror("invalid operation: %v (receive from send-only type %v)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| n.Type = t.Type |
| goto ret |
| |
| case OSEND: |
| ok |= Etop |
| l = typecheck(&n.Left, Erv) |
| typecheck(&n.Right, Erv) |
| defaultlit(&n.Left, nil) |
| l = n.Left |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| if t.Etype != TCHAN { |
| Yyerror("invalid operation: %v (send to non-chan type %v)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| if t.Chan&Csend == 0 { |
| Yyerror("invalid operation: %v (send to receive-only type %v)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| defaultlit(&n.Right, t.Type) |
| r = n.Right |
| if r.Type == nil { |
| goto error |
| } |
| n.Right = assignconv(r, l.Type.Type, "send") |
| |
| // TODO: more aggressive |
| n.Etype = 0 |
| |
| n.Type = nil |
| goto ret |
| |
| case OSLICE: |
| ok |= Erv |
| typecheck(&n.Left, top) |
| typecheck(&n.Right.Left, Erv) |
| typecheck(&n.Right.Right, Erv) |
| defaultlit(&n.Left, nil) |
| indexlit(&n.Right.Left) |
| indexlit(&n.Right.Right) |
| l = n.Left |
| if Isfixedarray(l.Type) { |
| if !islvalue(n.Left) { |
| Yyerror("invalid operation %v (slice of unaddressable value)", Nconv(n, 0)) |
| goto error |
| } |
| |
| n.Left = Nod(OADDR, n.Left, nil) |
| n.Left.Implicit = 1 |
| typecheck(&n.Left, Erv) |
| l = n.Left |
| } |
| |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| tp = nil |
| if Istype(t, TSTRING) { |
| n.Type = t |
| n.Op = OSLICESTR |
| } else if Isptr[t.Etype] != 0 && Isfixedarray(t.Type) { |
| tp = t.Type |
| n.Type = typ(TARRAY) |
| n.Type.Type = tp.Type |
| n.Type.Bound = -1 |
| dowidth(n.Type) |
| n.Op = OSLICEARR |
| } else if Isslice(t) { |
| n.Type = t |
| } else { |
| Yyerror("cannot slice %v (type %v)", Nconv(l, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| lo = n.Right.Left |
| if lo != nil && checksliceindex(l, lo, tp) < 0 { |
| goto error |
| } |
| hi = n.Right.Right |
| if hi != nil && checksliceindex(l, hi, tp) < 0 { |
| goto error |
| } |
| if checksliceconst(lo, hi) < 0 { |
| goto error |
| } |
| goto ret |
| |
| case OSLICE3: |
| ok |= Erv |
| typecheck(&n.Left, top) |
| typecheck(&n.Right.Left, Erv) |
| typecheck(&n.Right.Right.Left, Erv) |
| typecheck(&n.Right.Right.Right, Erv) |
| defaultlit(&n.Left, nil) |
| indexlit(&n.Right.Left) |
| indexlit(&n.Right.Right.Left) |
| indexlit(&n.Right.Right.Right) |
| l = n.Left |
| if Isfixedarray(l.Type) { |
| if !islvalue(n.Left) { |
| Yyerror("invalid operation %v (slice of unaddressable value)", Nconv(n, 0)) |
| goto error |
| } |
| |
| n.Left = Nod(OADDR, n.Left, nil) |
| n.Left.Implicit = 1 |
| typecheck(&n.Left, Erv) |
| l = n.Left |
| } |
| |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| tp = nil |
| if Istype(t, TSTRING) { |
| Yyerror("invalid operation %v (3-index slice of string)", Nconv(n, 0)) |
| goto error |
| } |
| |
| if Isptr[t.Etype] != 0 && Isfixedarray(t.Type) { |
| tp = t.Type |
| n.Type = typ(TARRAY) |
| n.Type.Type = tp.Type |
| n.Type.Bound = -1 |
| dowidth(n.Type) |
| n.Op = OSLICE3ARR |
| } else if Isslice(t) { |
| n.Type = t |
| } else { |
| Yyerror("cannot slice %v (type %v)", Nconv(l, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| lo = n.Right.Left |
| if lo != nil && checksliceindex(l, lo, tp) < 0 { |
| goto error |
| } |
| mid = n.Right.Right.Left |
| if mid != nil && checksliceindex(l, mid, tp) < 0 { |
| goto error |
| } |
| hi = n.Right.Right.Right |
| if hi != nil && checksliceindex(l, hi, tp) < 0 { |
| goto error |
| } |
| if checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0 { |
| goto error |
| } |
| goto ret |
| |
| /* |
| * call and call like |
| */ |
| case OCALL: |
| l = n.Left |
| |
| if l.Op == ONAME { |
| r = unsafenmagic(n) |
| if r != nil { |
| if n.Isddd != 0 { |
| Yyerror("invalid use of ... with builtin %v", Nconv(l, 0)) |
| } |
| n = r |
| goto reswitch |
| } |
| } |
| |
| typecheck(&n.Left, Erv|Etype|Ecall|top&Eproc) |
| n.Diag |= n.Left.Diag |
| l = n.Left |
| if l.Op == ONAME && l.Etype != 0 { |
| if n.Isddd != 0 && l.Etype != OAPPEND { |
| Yyerror("invalid use of ... with builtin %v", Nconv(l, 0)) |
| } |
| |
| // builtin: OLEN, OCAP, etc. |
| n.Op = l.Etype |
| |
| n.Left = n.Right |
| n.Right = nil |
| goto reswitch |
| } |
| |
| defaultlit(&n.Left, nil) |
| l := n.Left |
| if l.Op == OTYPE { |
| if n.Isddd != 0 || l.Type.Bound == -100 { |
| if l.Type.Broke == 0 { |
| Yyerror("invalid use of ... in type conversion", l) |
| } |
| n.Diag = 1 |
| } |
| |
| // pick off before type-checking arguments |
| ok |= Erv |
| |
| // 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", Tconv(l.Type, 0)) < 0 { |
| goto error |
| } |
| goto doconv |
| } |
| |
| if count(n.List) == 1 && n.Isddd == 0 { |
| typecheck(&n.List.N, Erv|Efnstruct) |
| } else { |
| typechecklist(n.List, Erv) |
| } |
| t := l.Type |
| if t == nil { |
| goto error |
| } |
| 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 := getthisx(t).Type.Type |
| |
| if l.Left == nil || !Eqtype(l.Left.Type, tp) { |
| Fatal("method receiver") |
| } |
| |
| default: |
| n.Op = OCALLFUNC |
| if t.Etype != TFUNC { |
| Yyerror("cannot call non-function %v (type %v)", Nconv(l, 0), Tconv(t, 0)) |
| goto error |
| } |
| } |
| |
| descbuf := fmt.Sprintf("argument to %v", Nconv(n.Left, 0)) |
| desc := descbuf |
| typecheckaste(OCALL, n.Left, int(n.Isddd), getinargx(t), n.List, desc) |
| ok |= Etop |
| if t.Outtuple == 0 { |
| goto ret |
| } |
| ok |= Erv |
| if t.Outtuple == 1 { |
| t := getoutargx(l.Type).Type |
| if t == nil { |
| goto error |
| } |
| if t.Etype == TFIELD { |
| t = t.Type |
| } |
| n.Type = t |
| goto ret |
| } |
| |
| // multiple return |
| if top&(Efnstruct|Etop) == 0 { |
| Yyerror("multiple-value %v() in single-value context", Nconv(l, 0)) |
| goto ret |
| } |
| |
| n.Type = getoutargx(l.Type) |
| goto ret |
| |
| case OCAP, |
| OLEN, |
| OREAL, |
| OIMAG: |
| ok |= Erv |
| if onearg(n, "%v", Oconv(int(n.Op), 0)) < 0 { |
| goto error |
| } |
| typecheck(&n.Left, Erv) |
| defaultlit(&n.Left, nil) |
| implicitstar(&n.Left) |
| l := n.Left |
| t := l.Type |
| if t == nil { |
| goto error |
| } |
| switch n.Op { |
| case OCAP: |
| if okforcap[t.Etype] == 0 { |
| goto badcall1 |
| } |
| |
| case OLEN: |
| if okforlen[t.Etype] == 0 { |
| goto badcall1 |
| } |
| |
| case OREAL, |
| OIMAG: |
| if Iscomplex[t.Etype] == 0 { |
| goto badcall1 |
| } |
| if Isconst(l, CTCPLX) { |
| r := n |
| if n.Op == OREAL { |
| n = nodfltconst(&l.Val.U.Cval.Real) |
| } else { |
| n = nodfltconst(&l.Val.U.Cval.Imag) |
| } |
| n.Orig = r |
| } |
| |
| n.Type = Types[cplxsubtype(int(t.Etype))] |
| goto ret |
| } |
| |
| // might be constant |
| switch t.Etype { |
| case TSTRING: |
| if Isconst(l, CTSTR) { |
| r := Nod(OXXX, nil, nil) |
| Nodconst(r, Types[TINT], int64(len(l.Val.U.Sval.S))) |
| r.Orig = n |
| n = r |
| } |
| |
| case TARRAY: |
| if t.Bound < 0 { // slice |
| break |
| } |
| if callrecv(l) { // has call or receive |
| break |
| } |
| r := Nod(OXXX, nil, nil) |
| Nodconst(r, Types[TINT], t.Bound) |
| r.Orig = n |
| n = r |
| } |
| |
| n.Type = Types[TINT] |
| goto ret |
| |
| case OCOMPLEX: |
| ok |= Erv |
| var r *Node |
| var l *Node |
| if count(n.List) == 1 { |
| typechecklist(n.List, Efnstruct) |
| if n.List.N.Op != OCALLFUNC && n.List.N.Op != OCALLMETH { |
| Yyerror("invalid operation: complex expects two arguments") |
| goto error |
| } |
| |
| t := n.List.N.Left.Type |
| if t.Outtuple != 2 { |
| Yyerror("invalid operation: complex expects two arguments, %v returns %d results", Nconv(n.List.N, 0), t.Outtuple) |
| goto error |
| } |
| |
| t = n.List.N.Type.Type |
| l = t.Nname |
| r = t.Down.Nname |
| } else { |
| if twoarg(n) < 0 { |
| goto error |
| } |
| l = typecheck(&n.Left, Erv|top&Eiota) |
| r = typecheck(&n.Right, Erv|top&Eiota) |
| if l.Type == nil || r.Type == nil { |
| goto error |
| } |
| defaultlit2(&l, &r, 0) |
| if l.Type == nil || r.Type == nil { |
| goto error |
| } |
| n.Left = l |
| n.Right = r |
| } |
| |
| if !Eqtype(l.Type, r.Type) { |
| Yyerror("invalid operation: %v (mismatched types %v and %v)", Nconv(n, 0), Tconv(l.Type, 0), Tconv(r.Type, 0)) |
| goto error |
| } |
| |
| var t *Type |
| switch l.Type.Etype { |
| default: |
| Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", Nconv(n, 0), Tconv(l.Type, 0), r.Type) |
| goto error |
| |
| case TIDEAL: |
| t = Types[TIDEAL] |
| |
| case TFLOAT32: |
| t = Types[TCOMPLEX64] |
| |
| case TFLOAT64: |
| t = Types[TCOMPLEX128] |
| } |
| |
| if l.Op == OLITERAL && r.Op == OLITERAL { |
| // make it a complex literal |
| r = nodcplxlit(l.Val, r.Val) |
| |
| r.Orig = n |
| n = r |
| } |
| |
| n.Type = t |
| goto ret |
| |
| case OCLOSE: |
| if onearg(n, "%v", Oconv(int(n.Op), 0)) < 0 { |
| goto error |
| } |
| typecheck(&n.Left, Erv) |
| defaultlit(&n.Left, nil) |
| l := n.Left |
| t := l.Type |
| if t == nil { |
| goto error |
| } |
| if t.Etype != TCHAN { |
| Yyerror("invalid operation: %v (non-chan type %v)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| if t.Chan&Csend == 0 { |
| Yyerror("invalid operation: %v (cannot close receive-only channel)", Nconv(n, 0)) |
| goto error |
| } |
| |
| ok |= Etop |
| goto ret |
| |
| case ODELETE: |
| args := n.List |
| if args == nil { |
| Yyerror("missing arguments to delete") |
| goto error |
| } |
| |
| if args.Next == nil { |
| Yyerror("missing second (key) argument to delete") |
| goto error |
| } |
| |
| if args.Next.Next != nil { |
| Yyerror("too many arguments to delete") |
| goto error |
| } |
| |
| ok |= Etop |
| typechecklist(args, Erv) |
| l := args.N |
| r := args.Next.N |
| if l.Type != nil && l.Type.Etype != TMAP { |
| Yyerror("first argument to delete must be map; have %v", Tconv(l.Type, obj.FmtLong)) |
| goto error |
| } |
| |
| args.Next.N = assignconv(r, l.Type.Down, "delete") |
| goto ret |
| |
| case OAPPEND: |
| ok |= Erv |
| args := n.List |
| if args == nil { |
| Yyerror("missing arguments to append") |
| goto error |
| } |
| |
| if count(args) == 1 && n.Isddd == 0 { |
| typecheck(&args.N, Erv|Efnstruct) |
| } else { |
| typechecklist(args, Erv) |
| } |
| |
| t := args.N.Type |
| if t == nil { |
| goto error |
| } |
| |
| // Unpack multiple-return result before type-checking. |
| if Istype(t, TSTRUCT) && t.Funarg != 0 { |
| t = t.Type |
| if Istype(t, TFIELD) { |
| t = t.Type |
| } |
| } |
| |
| n.Type = t |
| if !Isslice(t) { |
| if Isconst(args.N, CTNIL) { |
| Yyerror("first argument to append must be typed slice; have untyped nil", t) |
| goto error |
| } |
| |
| Yyerror("first argument to append must be slice; have %v", Tconv(t, obj.FmtLong)) |
| goto error |
| } |
| |
| if n.Isddd != 0 { |
| if args.Next == nil { |
| Yyerror("cannot use ... on first argument to append") |
| goto error |
| } |
| |
| if args.Next.Next != nil { |
| Yyerror("too many arguments to append") |
| goto error |
| } |
| |
| if Istype(t.Type, TUINT8) && Istype(args.Next.N.Type, TSTRING) { |
| defaultlit(&args.Next.N, Types[TSTRING]) |
| goto ret |
| } |
| |
| args.Next.N = assignconv(args.Next.N, t.Orig, "append") |
| goto ret |
| } |
| |
| for args = args.Next; args != nil; args = args.Next { |
| if args.N.Type == nil { |
| continue |
| } |
| args.N = assignconv(args.N, t.Type, "append") |
| } |
| |
| goto ret |
| |
| case OCOPY: |
| ok |= Etop | Erv |
| args := n.List |
| if args == nil || args.Next == nil { |
| Yyerror("missing arguments to copy") |
| goto error |
| } |
| |
| if args.Next.Next != nil { |
| Yyerror("too many arguments to copy") |
| goto error |
| } |
| |
| n.Left = args.N |
| n.Right = args.Next.N |
| n.List = nil |
| n.Type = Types[TINT] |
| typecheck(&n.Left, Erv) |
| typecheck(&n.Right, Erv) |
| if n.Left.Type == nil || n.Right.Type == nil { |
| goto error |
| } |
| defaultlit(&n.Left, nil) |
| defaultlit(&n.Right, nil) |
| if n.Left.Type == nil || n.Right.Type == nil { |
| goto error |
| } |
| |
| // copy([]byte, string) |
| if Isslice(n.Left.Type) && n.Right.Type.Etype == TSTRING { |
| if Eqtype(n.Left.Type.Type, bytetype) { |
| goto ret |
| } |
| Yyerror("arguments to copy have different element types: %v and string", Tconv(n.Left.Type, obj.FmtLong)) |
| goto error |
| } |
| |
| if !Isslice(n.Left.Type) || !Isslice(n.Right.Type) { |
| if !Isslice(n.Left.Type) && !Isslice(n.Right.Type) { |
| Yyerror("arguments to copy must be slices; have %v, %v", Tconv(n.Left.Type, obj.FmtLong), Tconv(n.Right.Type, obj.FmtLong)) |
| } else if !Isslice(n.Left.Type) { |
| Yyerror("first argument to copy should be slice; have %v", Tconv(n.Left.Type, obj.FmtLong)) |
| } else { |
| Yyerror("second argument to copy should be slice or string; have %v", Tconv(n.Right.Type, obj.FmtLong)) |
| } |
| goto error |
| } |
| |
| if !Eqtype(n.Left.Type.Type, n.Right.Type.Type) { |
| Yyerror("arguments to copy have different element types: %v and %v", Tconv(n.Left.Type, obj.FmtLong), Tconv(n.Right.Type, obj.FmtLong)) |
| goto error |
| } |
| |
| goto ret |
| |
| case OCONV: |
| goto doconv |
| |
| case OMAKE: |
| ok |= Erv |
| args := n.List |
| if args == nil { |
| Yyerror("missing argument to make") |
| goto error |
| } |
| |
| n.List = nil |
| l := args.N |
| args = args.Next |
| typecheck(&l, Etype) |
| t := l.Type |
| if t == nil { |
| goto error |
| } |
| |
| switch t.Etype { |
| default: |
| Yyerror("cannot make type %v", Tconv(t, 0)) |
| goto error |
| |
| case TARRAY: |
| if !Isslice(t) { |
| Yyerror("cannot make type %v", Tconv(t, 0)) |
| goto error |
| } |
| |
| if args == nil { |
| Yyerror("missing len argument to make(%v)", Tconv(t, 0)) |
| goto error |
| } |
| |
| l = args.N |
| args = args.Next |
| typecheck(&l, Erv) |
| r := (*Node)(nil) |
| if args != nil { |
| r = args.N |
| args = args.Next |
| typecheck(&r, Erv) |
| } |
| |
| if l.Type == nil || (r != nil && r.Type == nil) { |
| goto error |
| } |
| et := bool2int(checkmake(t, "len", l) < 0) |
| et |= bool2int(r != nil && checkmake(t, "cap", r) < 0) |
| if et != 0 { |
| goto error |
| } |
| if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && Mpcmpfixfix(l.Val.U.Xval, r.Val.U.Xval) > 0 { |
| Yyerror("len larger than cap in make(%v)", Tconv(t, 0)) |
| goto error |
| } |
| |
| n.Left = l |
| n.Right = r |
| n.Op = OMAKESLICE |
| |
| case TMAP: |
| if args != nil { |
| l = args.N |
| args = args.Next |
| typecheck(&l, Erv) |
| defaultlit(&l, Types[TINT]) |
| if l.Type == nil { |
| goto error |
| } |
| if checkmake(t, "size", l) < 0 { |
| goto error |
| } |
| n.Left = l |
| } else { |
| n.Left = Nodintconst(0) |
| } |
| n.Op = OMAKEMAP |
| |
| case TCHAN: |
| l = nil |
| if args != nil { |
| l = args.N |
| args = args.Next |
| typecheck(&l, Erv) |
| defaultlit(&l, Types[TINT]) |
| if l.Type == nil { |
| goto error |
| } |
| if checkmake(t, "buffer", l) < 0 { |
| goto error |
| } |
| n.Left = l |
| } else { |
| n.Left = Nodintconst(0) |
| } |
| n.Op = OMAKECHAN |
| } |
| |
| if args != nil { |
| Yyerror("too many arguments to make(%v)", Tconv(t, 0)) |
| n.Op = OMAKE |
| goto error |
| } |
| |
| n.Type = t |
| goto ret |
| |
| case ONEW: |
| ok |= Erv |
| args := n.List |
| if args == nil { |
| Yyerror("missing argument to new") |
| goto error |
| } |
| |
| l := args.N |
| typecheck(&l, Etype) |
| t := l.Type |
| if t == nil { |
| goto error |
| } |
| if args.Next != nil { |
| Yyerror("too many arguments to new(%v)", Tconv(t, 0)) |
| goto error |
| } |
| |
| n.Left = l |
| n.Type = Ptrto(t) |
| goto ret |
| |
| case OPRINT, |
| OPRINTN: |
| ok |= Etop |
| typechecklist(n.List, Erv|Eindir) // Eindir: address does not escape |
| for args := n.List; args != nil; args = args.Next { |
| // Special case for print: int constant is int64, not int. |
| if Isconst(args.N, CTINT) { |
| defaultlit(&args.N, Types[TINT64]) |
| } else { |
| defaultlit(&args.N, nil) |
| } |
| } |
| |
| goto ret |
| |
| case OPANIC: |
| ok |= Etop |
| if onearg(n, "panic") < 0 { |
| goto error |
| } |
| typecheck(&n.Left, Erv) |
| defaultlit(&n.Left, Types[TINTER]) |
| if n.Left.Type == nil { |
| goto error |
| } |
| goto ret |
| |
| case ORECOVER: |
| ok |= Erv | Etop |
| if n.List != nil { |
| Yyerror("too many arguments to recover") |
| goto error |
| } |
| |
| n.Type = Types[TINTER] |
| goto ret |
| |
| case OCLOSURE: |
| ok |= Erv |
| typecheckclosure(n, top) |
| if n.Type == nil { |
| goto error |
| } |
| goto ret |
| |
| case OITAB: |
| ok |= Erv |
| typecheck(&n.Left, Erv) |
| t := n.Left.Type |
| if t == nil { |
| goto error |
| } |
| if t.Etype != TINTER { |
| Fatal("OITAB of %v", Tconv(t, 0)) |
| } |
| n.Type = Ptrto(Types[TUINTPTR]) |
| goto ret |
| |
| case OSPTR: |
| ok |= Erv |
| typecheck(&n.Left, Erv) |
| t := n.Left.Type |
| if t == nil { |
| goto error |
| } |
| if !Isslice(t) && t.Etype != TSTRING { |
| Fatal("OSPTR of %v", Tconv(t, 0)) |
| } |
| if t.Etype == TSTRING { |
| n.Type = Ptrto(Types[TUINT8]) |
| } else { |
| n.Type = Ptrto(t.Type) |
| } |
| goto ret |
| |
| case OCLOSUREVAR: |
| ok |= Erv |
| goto ret |
| |
| case OCFUNC: |
| ok |= Erv |
| typecheck(&n.Left, Erv) |
| n.Type = Types[TUINTPTR] |
| goto ret |
| |
| case OCONVNOP: |
| ok |= Erv |
| typecheck(&n.Left, Erv) |
| goto ret |
| |
| /* |
| * statements |
| */ |
| case OAS: |
| ok |= Etop |
| |
| typecheckas(n) |
| |
| // Code that creates temps does not bother to set defn, so do it here. |
| if n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { |
| n.Left.Defn = n |
| } |
| goto ret |
| |
| case OAS2: |
| ok |= Etop |
| typecheckas2(n) |
| goto ret |
| |
| case OBREAK, |
| OCONTINUE, |
| ODCL, |
| OEMPTY, |
| OGOTO, |
| OXFALL, |
| OVARKILL: |
| ok |= Etop |
| goto ret |
| |
| case OLABEL: |
| ok |= Etop |
| decldepth++ |
| goto ret |
| |
| case ODEFER: |
| ok |= Etop |
| typecheck(&n.Left, Etop|Erv) |
| if n.Left.Diag == 0 { |
| checkdefergo(n) |
| } |
| goto ret |
| |
| case OPROC: |
| ok |= Etop |
| typecheck(&n.Left, Etop|Eproc|Erv) |
| checkdefergo(n) |
| goto ret |
| |
| case OFOR: |
| ok |= Etop |
| typechecklist(n.Ninit, Etop) |
| decldepth++ |
| typecheck(&n.Ntest, Erv) |
| if n.Ntest != nil { |
| t := n.Ntest.Type |
| if t != nil && t.Etype != TBOOL { |
| Yyerror("non-bool %v used as for condition", Nconv(n.Ntest, obj.FmtLong)) |
| } |
| } |
| typecheck(&n.Nincr, Etop) |
| typechecklist(n.Nbody, Etop) |
| decldepth-- |
| goto ret |
| |
| case OIF: |
| ok |= Etop |
| typechecklist(n.Ninit, Etop) |
| typecheck(&n.Ntest, Erv) |
| if n.Ntest != nil { |
| t := n.Ntest.Type |
| if t != nil && t.Etype != TBOOL { |
| Yyerror("non-bool %v used as if condition", Nconv(n.Ntest, obj.FmtLong)) |
| } |
| } |
| typechecklist(n.Nbody, Etop) |
| typechecklist(n.Nelse, Etop) |
| goto ret |
| |
| case ORETURN: |
| ok |= Etop |
| if count(n.List) == 1 { |
| typechecklist(n.List, Erv|Efnstruct) |
| } else { |
| typechecklist(n.List, Erv) |
| } |
| if Curfn == nil { |
| Yyerror("return outside function") |
| goto error |
| } |
| |
| if Curfn.Type.Outnamed != 0 && n.List == nil { |
| goto ret |
| } |
| typecheckaste(ORETURN, nil, 0, getoutargx(Curfn.Type), n.List, "return argument") |
| goto ret |
| |
| case ORETJMP: |
| ok |= Etop |
| goto ret |
| |
| case OSELECT: |
| ok |= Etop |
| typecheckselect(n) |
| goto ret |
| |
| case OSWITCH: |
| ok |= Etop |
| typecheckswitch(n) |
| goto ret |
| |
| case ORANGE: |
| ok |= Etop |
| typecheckrange(n) |
| goto ret |
| |
| case OTYPESW: |
| Yyerror("use of .(type) outside type switch") |
| goto error |
| |
| case OXCASE: |
| ok |= Etop |
| typechecklist(n.List, Erv) |
| typechecklist(n.Nbody, Etop) |
| goto ret |
| |
| case ODCLFUNC: |
| ok |= Etop |
| typecheckfunc(n) |
| goto ret |
| |
| case ODCLCONST: |
| ok |= Etop |
| typecheck(&n.Left, Erv) |
| goto ret |
| |
| case ODCLTYPE: |
| ok |= Etop |
| typecheck(&n.Left, Etype) |
| if incannedimport == 0 { |
| checkwidth(n.Left.Type) |
| } |
| goto ret |
| } |
| |
| goto ret |
| |
| arith: |
| if op == OLSH || op == ORSH { |
| goto shift |
| } |
| |
| // ideal mixed with non-ideal |
| defaultlit2(&l, &r, 0) |
| |
| n.Left = l |
| n.Right = r |
| if l.Type == nil || r.Type == nil { |
| goto error |
| } |
| t = l.Type |
| if t.Etype == TIDEAL { |
| t = r.Type |
| } |
| et = int(t.Etype) |
| if et == TIDEAL { |
| et = TINT |
| } |
| aop = 0 |
| if iscmp[n.Op] != 0 && t.Etype != TIDEAL && !Eqtype(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. |
| if r.Type.Etype != TBLANK { |
| aop = assignop(l.Type, r.Type, nil) |
| if aop != 0 { |
| if Isinter(r.Type) && !Isinter(l.Type) && algtype1(l.Type, nil) == ANOEQ { |
| Yyerror("invalid operation: %v (operator %v not defined on %s)", Nconv(n, 0), Oconv(int(op), 0), typekind(l.Type)) |
| goto error |
| } |
| |
| dowidth(l.Type) |
| if Isinter(r.Type) == Isinter(l.Type) || l.Type.Width >= 1<<16 { |
| l = Nod(aop, l, nil) |
| l.Type = r.Type |
| l.Typecheck = 1 |
| n.Left = l |
| } |
| |
| t = r.Type |
| goto converted |
| } |
| } |
| |
| if l.Type.Etype != TBLANK { |
| aop = assignop(r.Type, l.Type, nil) |
| if aop != 0 { |
| if Isinter(l.Type) && !Isinter(r.Type) && algtype1(r.Type, nil) == ANOEQ { |
| Yyerror("invalid operation: %v (operator %v not defined on %s)", Nconv(n, 0), Oconv(int(op), 0), typekind(r.Type)) |
| goto error |
| } |
| |
| dowidth(r.Type) |
| if Isinter(r.Type) == Isinter(l.Type) || r.Type.Width >= 1<<16 { |
| r = Nod(aop, r, nil) |
| r.Type = l.Type |
| r.Typecheck = 1 |
| n.Right = r |
| } |
| |
| t = l.Type |
| } |
| } |
| |
| converted: |
| et = int(t.Etype) |
| } |
| |
| if t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) { |
| defaultlit2(&l, &r, 1) |
| if n.Op == OASOP && n.Implicit != 0 { |
| Yyerror("invalid operation: %v (non-numeric type %v)", Nconv(n, 0), Tconv(l.Type, 0)) |
| goto error |
| } |
| |
| if Isinter(r.Type) == Isinter(l.Type) || aop == 0 { |
| Yyerror("invalid operation: %v (mismatched types %v and %v)", Nconv(n, 0), Tconv(l.Type, 0), Tconv(r.Type, 0)) |
| goto error |
| } |
| } |
| |
| if okfor[op][et] == 0 { |
| Yyerror("invalid operation: %v (operator %v not defined on %s)", Nconv(n, 0), Oconv(int(op), 0), typekind(t)) |
| goto error |
| } |
| |
| // okfor allows any array == array, map == map, func == func. |
| // restrict to slice/map/func == nil and nil == slice/map/func. |
| if Isfixedarray(l.Type) && algtype1(l.Type, nil) == ANOEQ { |
| Yyerror("invalid operation: %v (%v cannot be compared)", Nconv(n, 0), Tconv(l.Type, 0)) |
| goto error |
| } |
| |
| if Isslice(l.Type) && !isnil(l) && !isnil(r) { |
| Yyerror("invalid operation: %v (slice can only be compared to nil)", Nconv(n, 0)) |
| goto error |
| } |
| |
| if l.Type.Etype == TMAP && !isnil(l) && !isnil(r) { |
| Yyerror("invalid operation: %v (map can only be compared to nil)", Nconv(n, 0)) |
| goto error |
| } |
| |
| if l.Type.Etype == TFUNC && !isnil(l) && !isnil(r) { |
| Yyerror("invalid operation: %v (func can only be compared to nil)", Nconv(n, 0)) |
| goto error |
| } |
| |
| if l.Type.Etype == TSTRUCT && algtype1(l.Type, &badtype) == ANOEQ { |
| Yyerror("invalid operation: %v (struct containing %v cannot be compared)", Nconv(n, 0), Tconv(badtype, 0)) |
| goto error |
| } |
| |
| t = l.Type |
| if iscmp[n.Op] != 0 { |
| evconst(n) |
| t = idealbool |
| if n.Op != OLITERAL { |
| defaultlit2(&l, &r, 1) |
| n.Left = l |
| n.Right = r |
| } |
| } else if n.Op == OANDAND || n.Op == OOROR { |
| if l.Type == r.Type { |
| t = l.Type |
| } else if l.Type == idealbool { |
| t = r.Type |
| } else if r.Type == idealbool { |
| t = l.Type |
| } |
| } else |
| // non-comparison operators on ideal bools should make them lose their ideal-ness |
| if t == idealbool { |
| t = Types[TBOOL] |
| } |
| |
| if et == TSTRING { |
| if iscmp[n.Op] != 0 { |
| n.Etype = n.Op |
| n.Op = OCMPSTR |
| } else if 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 = l.List |
| } else { |
| n.List = list1(l) |
| } |
| if r.Op == OADDSTR { |
| n.List = concat(n.List, r.List) |
| } else { |
| n.List = list(n.List, r) |
| } |
| n.Left = nil |
| n.Right = nil |
| } |
| } |
| |
| if et == TINTER { |
| if l.Op == OLITERAL && l.Val.Ctype == CTNIL { |
| // swap for back end |
| n.Left = r |
| |
| n.Right = l |
| } else if r.Op == OLITERAL && r.Val.Ctype == CTNIL { |
| } else // leave alone for back end |
| if Isinter(r.Type) == Isinter(l.Type) { |
| n.Etype = n.Op |
| n.Op = OCMPIFACE |
| } |
| } |
| |
| if (op == ODIV || op == OMOD) && Isconst(r, CTINT) { |
| if mpcmpfixc(r.Val.U.Xval, 0) == 0 { |
| Yyerror("division by zero") |
| goto error |
| } |
| } |
| |
| n.Type = t |
| goto ret |
| |
| shift: |
| defaultlit(&r, Types[TUINT]) |
| n.Right = r |
| t = r.Type |
| if Isint[t.Etype] == 0 || Issigned[t.Etype] != 0 { |
| Yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", Nconv(n, 0), Tconv(r.Type, 0)) |
| goto error |
| } |
| |
| t = l.Type |
| if t != nil && t.Etype != TIDEAL && Isint[t.Etype] == 0 { |
| Yyerror("invalid operation: %v (shift of type %v)", Nconv(n, 0), Tconv(t, 0)) |
| goto error |
| } |
| |
| // no defaultlit for left |
| // the outer context gives the type |
| n.Type = l.Type |
| |
| goto ret |
| |
| doconv: |
| ok |= Erv |
| saveorignode(n) |
| typecheck(&n.Left, Erv|top&(Eindir|Eiota)) |
| convlit1(&n.Left, n.Type, true) |
| t = n.Left.Type |
| if t == nil || n.Type == nil { |
| goto error |
| } |
| n.Op = uint8(convertop(t, n.Type, &why)) |
| if (n.Op) == 0 { |
| if n.Diag == 0 && n.Type.Broke == 0 { |
| Yyerror("cannot convert %v to type %v%s", Nconv(n.Left, obj.FmtLong), Tconv(n.Type, 0), why) |
| n.Diag = 1 |
| } |
| |
| n.Op = OCONV |
| } |
| |
| switch n.Op { |
| case OCONVNOP: |
| if n.Left.Op == OLITERAL && n.Type != Types[TBOOL] { |
| r := Nod(OXXX, nil, nil) |
| n.Op = OCONV |
| n.Orig = r |
| *r = *n |
| n.Op = OLITERAL |
| n.Val = n.Left.Val |
| } |
| |
| // do not use stringtoarraylit. |
| // generated code and compiler memory footprint is better without it. |
| case OSTRARRAYBYTE: |
| break |
| |
| case OSTRARRAYRUNE: |
| if n.Left.Op == OLITERAL { |
| stringtoarraylit(&n) |
| } |
| } |
| |
| goto ret |
| |
| ret: |
| t = n.Type |
| if t != nil && t.Funarg == 0 && n.Op != OTYPE { |
| switch t.Etype { |
| case TFUNC, // might have TANY; wait until its called |
| TANY, |
| TFORW, |
| TIDEAL, |
| TNIL, |
| TBLANK: |
| break |
| |
| default: |
| checkwidth(t) |
| } |
| } |
| |
| if safemode != 0 && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR { |
| Yyerror("cannot use unsafe.Pointer") |
| } |
| |
| evconst(n) |
| if n.Op == OTYPE && top&Etype == 0 { |
| Yyerror("type %v is not an expression", Tconv(n.Type, 0)) |
| goto error |
| } |
| |
| if top&(Erv|Etype) == Etype && n.Op != OTYPE { |
| Yyerror("%v is not a type", Nconv(n, 0)) |
| goto error |
| } |
| |
| // TODO(rsc): simplify |
| if (top&(Ecall|Erv|Etype) != 0) && top&Etop == 0 && ok&(Erv|Etype|Ecall) == 0 { |
| Yyerror("%v used as value", Nconv(n, 0)) |
| goto error |
| } |
| |
| if (top&Etop != 0) && top&(Ecall|Erv|Etype) == 0 && ok&Etop == 0 { |
| if n.Diag == 0 { |
| Yyerror("%v evaluated but not used", Nconv(n, 0)) |
| n.Diag = 1 |
| } |
| |
| goto error |
| } |
| |
| /* TODO |
| if(n->type == T) |
| fatal("typecheck nil type"); |
| */ |
| goto out |
| |
| badcall1: |
| Yyerror("invalid argument %v for %v", Nconv(n.Left, obj.FmtLong), Oconv(int(n.Op), 0)) |
| goto error |
| |
| error: |
| n.Type = nil |
| |
| out: |
| *np = n |
| } |
| |
| func checksliceindex(l *Node, r *Node, tp *Type) int { |
| t := r.Type |
| if t == nil { |
| return -1 |
| } |
| if Isint[t.Etype] == 0 { |
| Yyerror("invalid slice index %v (type %v)", Nconv(r, 0), Tconv(t, 0)) |
| return -1 |
| } |
| |
| if r.Op == OLITERAL { |
| if Mpgetfix(r.Val.U.Xval) < 0 { |
| Yyerror("invalid slice index %v (index must be non-negative)", Nconv(r, 0)) |
| return -1 |
| } else if tp != nil && tp.Bound > 0 && Mpgetfix(r.Val.U.Xval) > tp.Bound { |
| Yyerror("invalid slice index %v (out of bounds for %d-element array)", Nconv(r, 0), tp.Bound) |
| return -1 |
| } else if Isconst(l, CTSTR) && Mpgetfix(r.Val.U.Xval) > int64(len(l.Val.U.Sval.S)) { |
| Yyerror("invalid slice index %v (out of bounds for %d-byte string)", Nconv(r, 0), len(l.Val.U.Sval.S)) |
| return -1 |
| } else if Mpcmpfixfix(r.Val.U.Xval, Maxintval[TINT]) > 0 { |
| Yyerror("invalid slice index %v (index too large)", Nconv(r, 0)) |
| return -1 |
| } |
| } |
| |
| return 0 |
| } |
| |
| func checksliceconst(lo *Node, hi *Node) int { |
| if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && Mpcmpfixfix(lo.Val.U.Xval, hi.Val.U.Xval) > 0 { |
| Yyerror("invalid slice index: %v > %v", Nconv(lo, 0), Nconv(hi, 0)) |
| return -1 |
| } |
| |
| return 0 |
| } |
| |
| func checkdefergo(n *Node) { |
| what := "defer" |
| if n.Op == OPROC { |
| 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 |
| } |
| Yyerror("%s discards result of %v", what, Nconv(n.Left, 0)) |
| 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 != 0 { |
| return |
| } |
| |
| if n.Diag == 0 { |
| // The syntax made sure it was a call, so this must be |
| // a conversion. |
| n.Diag = 1 |
| |
| Yyerror("%s requires function call, not conversion", what) |
| } |
| } |
| |
| func implicitstar(nn **Node) { |
| // insert implicit * if needed for fixed array |
| n := *nn |
| |
| t := n.Type |
| if t == nil || Isptr[t.Etype] == 0 { |
| return |
| } |
| t = t.Type |
| if t == nil { |
| return |
| } |
| if !Isfixedarray(t) { |
| return |
| } |
| n = Nod(OIND, n, nil) |
| n.Implicit = 1 |
| typecheck(&n, Erv) |
| *nn = n |
| } |
| |
| func onearg(n *Node, f string, args ...interface{}) int { |
| if n.Left != nil { |
| return 0 |
| } |
| if n.List == nil { |
| p := fmt.Sprintf(f, args...) |
| Yyerror("missing argument to %s: %v", p, Nconv(n, 0)) |
| return -1 |
| } |
| |
| if n.List.Next != nil { |
| p := fmt.Sprintf(f, args...) |
| Yyerror("too many arguments to %s: %v", p, Nconv(n, 0)) |
| n.Left = n.List.N |
| n.List = nil |
| return -1 |
| } |
| |
| n.Left = n.List.N |
| n.List = nil |
| return 0 |
| } |
| |
| func twoarg(n *Node) int { |
| if n.Left != nil { |
| return 0 |
| } |
| if n.List == nil { |
| Yyerror("missing argument to %v - %v", Oconv(int(n.Op), 0), Nconv(n, 0)) |
| return -1 |
| } |
| |
| n.Left = n.List.N |
| if n.List.Next == nil { |
| Yyerror("missing argument to %v - %v", Oconv(int(n.Op), 0), Nconv(n, 0)) |
| n.List = nil |
| return -1 |
| } |
| |
| if n.List.Next.Next != nil { |
| Yyerror("too many arguments to %v - %v", Oconv(int(n.Op), 0), Nconv(n, 0)) |
| n.List = nil |
| return -1 |
| } |
| |
| n.Right = n.List.Next.N |
| n.List = nil |
| return 0 |
| } |
| |
| func lookdot1(errnode *Node, s *Sym, t *Type, f *Type, dostrcmp int) *Type { |
| r := (*Type)(nil) |
| for ; f != nil; f = f.Down { |
| if dostrcmp != 0 && f.Sym.Name == s.Name { |
| return f |
| } |
| if f.Sym != s { |
| continue |
| } |
| if r != nil { |
| if errnode != nil { |
| Yyerror("ambiguous selector %v", Nconv(errnode, 0)) |
| } else if Isptr[t.Etype] != 0 { |
| Yyerror("ambiguous selector (%v).%v", Tconv(t, 0), Sconv(s, 0)) |
| } else { |
| Yyerror("ambiguous selector %v.%v", Tconv(t, 0), Sconv(s, 0)) |
| } |
| break |
| } |
| |
| r = f |
| } |
| |
| return r |
| } |
| |
| func looktypedot(n *Node, t *Type, dostrcmp int) bool { |
| s := n.Right.Sym |
| |
| if t.Etype == TINTER { |
| f1 := lookdot1(n, s, t, t.Type, dostrcmp) |
| if f1 == nil { |
| return false |
| } |
| |
| n.Right = methodname(n.Right, t) |
| n.Xoffset = f1.Width |
| n.Type = f1.Type |
| n.Op = ODOTINTER |
| return true |
| } |
| |
| // Find the base type: methtype will fail if t |
| // is not of the form T or *T. |
| f2 := methtype(t, 0) |
| |
| if f2 == nil { |
| return false |
| } |
| |
| expandmeth(f2) |
| f2 = lookdot1(n, s, f2, f2.Xmethod, dostrcmp) |
| if f2 == nil { |
| return false |
| } |
| |
| // disallow T.m if m requires *T receiver |
| if Isptr[getthisx(f2.Type).Type.Type.Etype] != 0 && Isptr[t.Etype] == 0 && f2.Embedded != 2 && !isifacemethod(f2.Type) { |
| Yyerror("invalid method expression %v (needs pointer receiver: (*%v).%v)", Nconv(n, 0), Tconv(t, 0), Sconv(f2.Sym, obj.FmtShort)) |
| return false |
| } |
| |
| n.Right = methodname(n.Right, t) |
| n.Xoffset = f2.Width |
| n.Type = f2.Type |
| n.Op = ODOTMETH |
| return true |
| } |
| |
| func derefall(t *Type) *Type { |
| for t != nil && int(t.Etype) == Tptr { |
| t = t.Type |
| } |
| return t |
| } |
| |
| func lookdot(n *Node, t *Type, dostrcmp int) bool { |
| s := n.Right.Sym |
| |
| dowidth(t) |
| f1 := (*Type)(nil) |
| if t.Etype == TSTRUCT || t.Etype == TINTER { |
| f1 = lookdot1(n, s, t, t.Type, dostrcmp) |
| } |
| |
| f2 := (*Type)(nil) |
| if n.Left.Type == t || n.Left.Type.Sym == nil { |
| f2 = methtype(t, 0) |
| if f2 != nil { |
| // Use f2->method, not f2->xmethod: adddot has |
| // already inserted all the necessary embedded dots. |
| f2 = lookdot1(n, s, f2, f2.Method, dostrcmp) |
| } |
| } |
| |
| if f1 != nil { |
| if f2 != nil { |
| Yyerror("%v is both field and method", Sconv(n.Right.Sym, 0)) |
| } |
| if f1.Width == BADWIDTH { |
| Fatal("lookdot badwidth %v %p", Tconv(f1, 0), f1) |
| } |
| n.Xoffset = f1.Width |
| n.Type = f1.Type |
| n.Paramfld = f1 |
| if t.Etype == TINTER { |
| if Isptr[n.Left.Type.Etype] != 0 { |
| n.Left = Nod(OIND, n.Left, nil) // implicitstar |
| n.Left.Implicit = 1 |
| typecheck(&n.Left, Erv) |
| } |
| |
| n.Op = ODOTINTER |
| } |
| |
| return true |
| } |
| |
| if f2 != nil { |
| tt := n.Left.Type |
| dowidth(tt) |
| rcvr := getthisx(f2.Type).Type.Type |
| if !Eqtype(rcvr, tt) { |
| if int(rcvr.Etype) == Tptr && Eqtype(rcvr.Type, tt) { |
| checklvalue(n.Left, "call pointer method on") |
| n.Left = Nod(OADDR, n.Left, nil) |
| n.Left.Implicit = 1 |
| typecheck(&n.Left, Etype|Erv) |
| } else if int(tt.Etype) == Tptr && int(rcvr.Etype) != Tptr && Eqtype(tt.Type, rcvr) { |
| n.Left = Nod(OIND, n.Left, nil) |
| n.Left.Implicit = 1 |
| typecheck(&n.Left, Etype|Erv) |
| } else if int(tt.Etype) == Tptr && int(tt.Type.Etype) == Tptr && Eqtype(derefall(tt), derefall(rcvr)) { |
| Yyerror("calling method %v with receiver %v requires explicit dereference", Nconv(n.Right, 0), Nconv(n.Left, obj.FmtLong)) |
| for int(tt.Etype) == Tptr { |
| // Stop one level early for method with pointer receiver. |
| if int(rcvr.Etype) == Tptr && int(tt.Type.Etype) != Tptr { |
| break |
| } |
| n.Left = Nod(OIND, n.Left, nil) |
| n.Left.Implicit = 1 |
| typecheck(&n.Left, Etype|Erv) |
| tt = tt.Type |
| } |
| } else { |
| Fatal("method mismatch: %v for %v", Tconv(rcvr, 0), Tconv(tt, 0)) |
| } |
| } |
| |
| n.Right = methodname(n.Right, n.Left.Type) |
| n.Xoffset = f2.Width |
| n.Type = f2.Type |
| |
| // print("lookdot found [%p] %T\n", f2->type, f2->type); |
| n.Op = ODOTMETH |
| |
| return true |
| } |
| |
| return false |
| } |
| |
| func nokeys(l *NodeList) bool { |
| for ; l != nil; l = l.Next { |
| if l.N.Op == OKEY { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func hasddd(t *Type) bool { |
| for tl := t.Type; tl != nil; tl = tl.Down { |
| if tl.Isddd != 0 { |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| func downcount(t *Type) int { |
| n := 0 |
| for tl := t.Type; tl != nil; tl = tl.Down { |
| n++ |
| } |
| |
| return n |
| } |
| |
| /* |
| * typecheck assignment: type list = expression list |
| */ |
| func typecheckaste(op int, call *Node, isddd int, tstruct *Type, nl *NodeList, desc string) { |
| var t *Type |
| var n *Node |
| var n1 int |
| var n2 int |
| |
| lno := int(lineno) |
| |
| if tstruct.Broke != 0 { |
| goto out |
| } |
| |
| n = nil |
| if nl != nil && nl.Next == nil { |
| n = nl.N |
| if n.Type != nil { |
| if n.Type.Etype == TSTRUCT && n.Type.Funarg != 0 { |
| if !hasddd(tstruct) { |
| n1 := downcount(tstruct) |
| n2 := downcount(n.Type) |
| if n2 > n1 { |
| goto toomany |
| } |
| if n2 < n1 { |
| goto notenough |
| } |
| } |
| |
| tn := n.Type.Type |
| var why string |
| for tl := tstruct.Type; tl != nil; tl = tl.Down { |
| if tl.Isddd != 0 { |
| for ; tn != nil; tn = tn.Down { |
| if assignop(tn.Type, tl.Type.Type, &why) == 0 { |
| if call != nil { |
| Yyerror("cannot use %v as type %v in argument to %v%s", Tconv(tn.Type, 0), Tconv(tl.Type.Type, 0), Nconv(call, 0), why) |
| } else { |
| Yyerror("cannot use %v as type %v in %s%s", Tconv(tn.Type, 0), Tconv(tl.Type.Type, 0), desc, why) |
| } |
| } |
| } |
| |
| goto out |
| } |
| |
| if tn == nil { |
| goto notenough |
| } |
| if assignop(tn.Type, tl.Type, &why) == 0 { |
| if call != nil { |
| Yyerror("cannot use %v as type %v in argument to %v%s", Tconv(tn.Type, 0), Tconv(tl.Type, 0), Nconv(call, 0), why) |
| } else { |
| Yyerror("cannot use %v as type %v in %s%s", Tconv(tn.Type, 0), Tconv(tl.Type, 0), desc, why) |
| } |
| } |
| |
| tn = tn.Down |
| } |
| |
| if tn != nil { |
| goto toomany |
| } |
| goto out |
| } |
| } |
| } |
| |
| n1 = downcount(tstruct) |
| n2 = count(nl) |
| if !hasddd(tstruct) { |
| if n2 > n1 { |
| goto toomany |
| } |
| if n2 < n1 { |
| goto notenough |
| } |
| } else { |
| if isddd == 0 { |
| if n2 < n1-1 { |
| goto notenough |
| } |
| } else { |
| if n2 > n1 { |
| goto toomany |
| } |
| if n2 < n1 { |
| goto notenough |
| } |
| } |
| } |
| |
| for tl := tstruct.Type; tl != nil; tl = tl.Down { |
| t = tl.Type |
| if tl.Isddd != 0 { |
| if isddd != 0 { |
| if nl == nil { |
| goto notenough |
| } |
| if nl.Next != nil { |
| goto toomany |
| } |
| n = nl.N |
| setlineno(n) |
| if n.Type != nil { |
| nl.N = assignconv(n, t, desc) |
| } |
| goto out |
| } |
| |
| for ; nl != nil; nl = nl.Next { |
| n = nl.N |
| setlineno(nl.N) |
| if n.Type != nil { |
| nl.N = assignconv(n, t.Type, desc) |
| } |
| } |
| |
| goto out |
| } |
| |
| if nl == nil { |
| goto notenough |
| } |
| n = nl.N |
| setlineno(n) |
| if n.Type != nil { |
| nl.N = assignconv(n, t, desc) |
| } |
| nl = nl.Next |
| } |
| |
| if nl != nil { |
| goto toomany |
| } |
| if isddd != 0 { |
| if call != nil { |
| Yyerror("invalid use of ... in call to %v", Nconv(call, 0)) |
| } else { |
| Yyerror("invalid use of ... in %v", Oconv(int(op), 0)) |
| } |
| } |
| |
| out: |
| lineno = int32(lno) |
| return |
| |
| notenough: |
| if n == nil || n.Diag == 0 { |
| if call != nil { |
| Yyerror("not enough arguments in call to %v", Nconv(call, 0)) |
| } else { |
| Yyerror("not enough arguments to %v", Oconv(int(op), 0)) |
| } |
| if n != nil { |
| n.Diag = 1 |
| } |
| } |
| |
| goto out |
| |
| toomany: |
| if call != nil { |
| Yyerror("too many arguments in call to %v", Nconv(call, 0)) |
| } else { |
| Yyerror("too many arguments to %v", Oconv(int(op), 0)) |
| } |
| goto out |
| } |
| |
| /* |
| * type check composite |
| */ |
| func fielddup(n *Node, hash []*Node) { |
| if n.Op != ONAME { |
| Fatal("fielddup: not ONAME") |
| } |
| s := n.Sym.Name |
| h := uint(stringhash(s) % uint32(len(hash))) |
| for a := hash[h]; a != nil; a = a.Ntest { |
| if a.Sym.Name == s { |
| Yyerror("duplicate field name in struct literal: %s", s) |
| return |
| } |
| } |
| |
| n.Ntest = hash[h] |
| hash[h] = n |
| } |
| |
| func keydup(n *Node, hash []*Node) { |
| orign := n |
| if n.Op == OCONVIFACE { |
| n = n.Left |
| } |
| evconst(n) |
| if n.Op != OLITERAL { |
| return // we dont check variables |
| } |
| |
| var b uint32 |
| switch n.Val.Ctype { |
| default: // unknown, bool, nil |
| b = 23 |
| |
| case CTINT, |
| CTRUNE: |
| b = uint32(Mpgetfix(n.Val.U.Xval)) |
| |
| case CTFLT: |
| d := mpgetflt(n.Val.U.Fval) |
| x := math.Float64bits(d) |
| for i := 0; i < 8; i++ { |
| b = b*PRIME1 + uint32(x&0xFF) |
| x >>= 8 |
| } |
| |
| case CTSTR: |
| b = 0 |
| s := n.Val.U.Sval.S |
| for i := len(n.Val.U.Sval.S); i > 0; i-- { |
| b = b*PRIME1 + uint32(s[0]) |
| s = s[1:] |
| } |
| } |
| |
| h := uint(b % uint32(len(hash))) |
| cmp := Node{} |
| for a := hash[h]; a != nil; a = a.Ntest { |
| cmp.Op = OEQ |
| cmp.Left = n |
| b = 0 |
| if a.Op == OCONVIFACE && orign.Op == OCONVIFACE { |
| if Eqtype(a.Left.Type, n.Type) { |
| cmp.Right = a.Left |
| evconst(&cmp) |
| b = uint32(cmp.Val.U.Bval) |
| } |
| } else if Eqtype(a.Type, n.Type) { |
| cmp.Right = a |
| evconst(&cmp) |
| b = uint32(cmp.Val.U.Bval) |
| } |
| |
| if b != 0 { |
| Yyerror("duplicate key %v in map literal", Nconv(n, 0)) |
| return |
| } |
| } |
| |
| orign.Ntest = hash[h] |
| hash[h] = orign |
| } |
| |
| func indexdup(n *Node, hash []*Node) { |
| if n.Op != OLITERAL { |
| Fatal("indexdup: not OLITERAL") |
| } |
| |
| b := uint32(Mpgetfix(n.Val.U.Xval)) |
| h := uint(b % uint32(len(hash))) |
| var c uint32 |
| for a := hash[h]; a != nil; a = a.Ntest { |
| c = uint32(Mpgetfix(a.Val.U.Xval)) |
| if b == c { |
| Yyerror("duplicate index in array literal: %d", b) |
| return |
| } |
| } |
| |
| n.Ntest = hash[h] |
| hash[h] = n |
| } |
| |
| func prime(h uint32, sr uint32) bool { |
| for n := uint32(3); n <= sr; n += 2 { |
| if h%n == 0 { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func inithash(n *Node, autohash []*Node) []*Node { |
| // count the number of entries |
| h := uint32(0) |
| |
| for ll := n.List; ll != nil; ll = ll.Next { |
| h++ |
| } |
| |
| // if the auto hash table is |
| // large enough use it. |
| if h <= uint32(len(autohash)) { |
| for i := range autohash { |
| autohash[i] = nil |
| } |
| return autohash |
| } |
| |
| // make hash size odd and 12% larger than entries |
| h += h / 8 |
| |
| h |= 1 |
| |
| // calculate sqrt of h |
| sr := h / 2 |
| |
| for i := 0; i < 5; i++ { |
| sr = (sr + h/sr) / 2 |
| } |
| |
| // check for primeality |
| for !prime(h, sr) { |
| h += 2 |
| } |
| |
| // build and return a throw-away hash table |
| return make([]*Node, h) |
| } |
| |
| func iscomptype(t *Type) bool { |
| switch t.Etype { |
| case TARRAY, |
| TSTRUCT, |
| TMAP: |
| return true |
| |
| case TPTR32, |
| TPTR64: |
| switch t.Type.Etype { |
| case TARRAY, |
| TSTRUCT, |
| TMAP: |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| func pushtype(n *Node, t *Type) { |
| if n == nil || n.Op != OCOMPLIT || !iscomptype(t) { |
| return |
| } |
| |
| if n.Right == nil { |
| n.Right = typenod(t) |
| n.Implicit = 1 // don't print |
| n.Right.Implicit = 1 // * is okay |
| } else if Debug['s'] != 0 { |
| typecheck(&n.Right, Etype) |
| if n.Right.Type != nil && Eqtype(n.Right.Type, t) { |
| fmt.Printf("%v: redundant type: %v\n", n.Line(), Tconv(t, 0)) |
| } |
| } |
| } |
| |
| func typecheckcomplit(np **Node) { |
| var nerr int |
| var l *Node |
| var norig *Node |
| var r *Node |
| var t *Type |
| |
| n := *np |
| lno := lineno |
| |
| if n.Right == nil { |
| if n.List != nil { |
| setlineno(n.List.N) |
| } |
| Yyerror("missing type in composite literal") |
| goto error |
| } |
| |
| // Save original node (including n->right) |
| norig = Nod(int(n.Op), nil, nil) |
| |
| *norig = *n |
| |
| setlineno(n.Right) |
| l = typecheck(&n.Right, Etype|Ecomplit) /* sic */ |
| t = l.Type |
| if t == nil { |
| goto error |
| } |
| nerr = nerrors |
| n.Type = t |
| |
| if Isptr[t.Etype] != 0 { |
| // For better or worse, we don't allow pointers as the composite literal type, |
| // except when using the &T syntax, which sets implicit on the OIND. |
| if n.Right.Implicit == 0 { |
| Yyerror("invalid pointer type %v for composite literal (use &%v instead)", Tconv(t, 0), Tconv(t.Type, 0)) |
| goto error |
| } |
| |
| // Also, the underlying type must be a struct, map, slice, or array. |
| if !iscomptype(t) { |
| Yyerror("invalid pointer type %v for composite literal", Tconv(t, 0)) |
| goto error |
| } |
| |
| t = t.Type |
| } |
| |
| switch t.Etype { |
| default: |
| Yyerror("invalid type for composite literal: %v", Tconv(t, 0)) |
| n.Type = nil |
| |
| case TARRAY: |
| var autohash [101]*Node |
| hash := inithash(n, autohash[:]) |
| |
| length := int64(0) |
| i := 0 |
| var l *Node |
| for ll := n.List; ll != nil; ll = ll.Next { |
| l = ll.N |
| setlineno(l) |
| if l.Op != OKEY { |
| l = Nod(OKEY, Nodintconst(int64(i)), l) |
| l.Left.Type = Types[TINT] |
| l.Left.Typecheck = 1 |
| ll.N = l |
| } |
| |
| typecheck(&l.Left, Erv) |
| evconst(l.Left) |
| i = nonnegconst(l.Left) |
| if i < 0 && l.Left.Diag == 0 { |
| Yyerror("array index must be non-negative integer constant") |
| l.Left.Diag = 1 |
| i = -(1 << 30) // stay negative for a while |
| } |
| |
| if i >= 0 { |
| indexdup(l.Left, hash) |
| } |
| i++ |
| if int64(i) > length { |
| length = int64(i) |
| if t.Bound >= 0 && length > t.Bound { |
| setlineno(l) |
| Yyerror("array index %d out of bounds [0:%d]", length-1, t.Bound) |
| t.Bound = -1 // no more errors |
| } |
| } |
| |
| r = l.Right |
| pushtype(r, t.Type) |
| typecheck(&r, Erv) |
| defaultlit(&r, t.Type) |
| l.Right = assignconv(r, t.Type, "array element") |
| } |
| |
| if t.Bound == -100 { |
| t.Bound = length |
| } |
| if t.Bound < 0 { |
| n.Right = Nodintconst(length) |
| } |
| n.Op = OARRAYLIT |
| |
| case TMAP: |
| var autohash [101]*Node |
| hash := inithash(n, autohash[:]) |
| |
| var l *Node |
| for ll := n.List; ll != nil; ll = ll.Next { |
| l = ll.N |
| setlineno(l) |
| if l.Op != OKEY { |
| typecheck(&ll.N, Erv) |
| Yyerror("missing key in map literal") |
| continue |
| } |
| |
| typecheck(&l.Left, Erv) |
| defaultlit(&l.Left, t.Down) |
| l.Left = assignconv(l.Left, t.Down, "map key") |
| if l.Left.Op != OCONV { |
| keydup(l.Left, hash) |
| } |
| |
| r = l.Right |
| pushtype(r, t.Type) |
| typecheck(&r, Erv) |
| defaultlit(&r, t.Type) |
| l.Right = assignconv(r, t.Type, "map value") |
| } |
| |
| n.Op = OMAPLIT |
| |
| case TSTRUCT: |
| bad := 0 |
| if n.List != nil && nokeys(n.List) { |
| // simple list of variables |
| f := t.Type |
| |
| var s *Sym |
| for ll := n.List; ll != nil; ll = ll.Next { |
| setlineno(ll.N) |
| typecheck(&ll.N, Erv) |
| if f == nil { |
| tmp12 := bad |
| bad++ |
| if tmp12 == 0 { |
| Yyerror("too many values in struct initializer") |
| } |
| continue |
| } |
| |
| s = f.Sym |
| if s != nil && !exportname(s.Name) && s.Pkg != localpkg { |
| Yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, Tconv(t, 0)) |
| } |
| |
| // No pushtype allowed here. Must name fields for that. |
| ll.N = assignconv(ll.N, f.Type, "field value") |
| |
| ll.N = Nod(OKEY, newname(f.Sym), ll.N) |
| ll.N.Left.Type = f |
| ll.N.Left.Typecheck = 1 |
| f = f.Down |
| } |
| |
| if f != nil { |
| Yyerror("too few values in struct initializer") |
| } |
| } else { |
| var autohash [101]*Node |
| hash := inithash(n, autohash[:]) |
| |
| // keyed list |
| var s *Sym |
| var f *Type |
| var l *Node |
| var s1 *Sym |
| for ll := n.List; ll != nil; ll = ll.Next { |
| l = ll.N |
| setlineno(l) |
| if l.Op != OKEY { |
| tmp13 := bad |
| bad++ |
| if tmp13 == 0 { |
| Yyerror("mixture of field:value and value initializers") |
| } |
| typecheck(&ll.N, Erv) |
| continue |
| } |
| |
| s = l.Left.Sym |
| if s == nil { |
| Yyerror("invalid field name %v in struct initializer", Nconv(l.Left, 0)) |
| typecheck(&l.Right, Erv) |
| 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. |
| if s.Pkg != localpkg && exportname(s.Name) { |
| s1 = Lookup(s.Name) |
| if s1.Origpkg == s.Pkg { |
| s = s1 |
| } |
| } |
| |
| f = lookdot1(nil, s, t, t.Type, 0) |
| if f == nil { |
| Yyerror("unknown %v field '%v' in struct literal", Tconv(t, 0), Sconv(s, 0)) |
| continue |
| } |
| |
| l.Left = newname(s) |
| l.Left.Typecheck = 1 |
| l.Left.Type = f |
| s = f.Sym |
| fielddup(newname(s), hash) |
| r = l.Right |
| |
| // No pushtype allowed here. Tried and rejected. |
| typecheck(&r, Erv) |
| |
| l.Right = assignconv(r, f.Type, "field value") |
| } |
| } |
| |
| n.Op = OSTRUCTLIT |
| } |
| |
| if nerr != nerrors { |
| goto error |
| } |
| |
| n.Orig = norig |
| if Isptr[n.Type.Etype] != 0 { |
| n = Nod(OPTRLIT, n, nil) |
| n.Typecheck = 1 |
| n.Type = n.Left.Type |
| n.Left.Type = t |
| n.Left.Typecheck = 1 |
| } |
| |
| n.Orig = norig |
| *np = n |
| lineno = lno |
| return |
| |
| error: |
| n.Type = nil |
| *np = n |
| lineno = lno |
| } |
| |
| /* |
| * lvalue etc |
| */ |
| func islvalue(n *Node) bool { |
| switch n.Op { |
| case OINDEX: |
| if Isfixedarray(n.Left.Type) { |
| return islvalue(n.Left) |
| } |
| if n.Left.Type != nil && n.Left.Type.Etype == TSTRING { |
| return false |
| } |
| fallthrough |
| |
| // fall through |
| case OIND, |
| ODOTPTR, |
| OCLOSUREVAR, |
| OPARAM: |
| 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, Nconv(n, 0)) |
| } |
| } |
| |
| func checkassign(stmt *Node, n *Node) { |
| // Variables declared in ORANGE are assigned on every iteration. |
| if n.Defn != stmt || stmt.Op == ORANGE { |
| r := outervalue(n) |
| var l *Node |
| for l = n; l != r; l = l.Left { |
| l.Assigned = 1 |
| if l.Closure != nil { |
| l.Closure.Assigned = 1 |
| } |
| } |
| |
| l.Assigned = 1 |
| if l.Closure != nil { |
| l.Closure.Assigned = 1 |
| } |
| } |
| |
| if islvalue(n) { |
| return |
| } |
| if n.Op == OINDEXMAP { |
| n.Etype = 1 |
| return |
| } |
| |
| // have already complained about n being undefined |
| if n.Op == ONONAME { |
| return |
| } |
| |
| Yyerror("cannot assign to %v", Nconv(n, 0)) |
| } |
| |
| func checkassignlist(stmt *Node, l *NodeList) { |
| for ; l != nil; l = l.Next { |
| checkassign(stmt, l.N) |
| } |
| } |
| |
| // Check whether l and r are the same side effect-free expression, |
| // so that it is safe to reuse one instead of computing both. |
| func samesafeexpr(l *Node, r *Node) bool { |
| if l.Op != r.Op || !Eqtype(l.Type, r.Type) { |
| return false |
| } |
| |
| switch l.Op { |
| case ONAME, |
| OCLOSUREVAR: |
| return l == r |
| |
| case ODOT, |
| ODOTPTR: |
| return l.Right != nil && r.Right != nil && l.Right.Sym == r.Right.Sym && samesafeexpr(l.Left, r.Left) |
| |
| case OIND: |
| return samesafeexpr(l.Left, r.Left) |
| |
| case OINDEX: |
| return samesafeexpr(l.Left, r.Left) && samesafeexpr(l.Right, r.Right) |
| } |
| |
| 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) { |
| // 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.Defn != n || n.Left.Ntype != nil { |
| typecheck(&n.Left, Erv|Easgn) |
| } |
| |
| typecheck(&n.Right, Erv) |
| checkassign(n, n.Left) |
| if n.Right != nil && n.Right.Type != nil { |
| if n.Left.Type != nil { |
| n.Right = assignconv(n.Right, n.Left.Type, "assignment") |
| } |
| } |
| |
| if n.Left.Defn == n && n.Left.Ntype == nil { |
| 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.Typecheck = 1 |
| |
| if n.Left.Typecheck == 0 { |
| typecheck(&n.Left, Erv|Easgn) |
| } |
| |
| // Recognize slices being updated in place, for better code generation later. |
| // Don't rewrite if using race detector, to avoid needing to teach race detector |
| // about this optimization. |
| if n.Left != nil && n.Left.Op != OINDEXMAP && n.Right != nil && flag_race == 0 { |
| switch n.Right.Op { |
| // For x = x[0:y], x can be updated in place, without touching pointer. |
| // TODO(rsc): Reenable once it is actually updated in place without touching the pointer. |
| case OSLICE, |
| OSLICE3, |
| OSLICESTR: |
| if false && samesafeexpr(n.Left, n.Right.Left) && (n.Right.Right.Left == nil || iszero(n.Right.Right.Left)) { |
| n.Right.Reslice = 1 |
| } |
| |
| // For x = append(x, ...), x can be updated in place when there is capacity, |
| // without touching the pointer; otherwise the emitted code to growslice |
| // can take care of updating the pointer, and only in that case. |
| // TODO(rsc): Reenable once the emitted code does update the pointer. |
| case OAPPEND: |
| if false && n.Right.List != nil && samesafeexpr(n.Left, n.Right.List.N) { |
| n.Right.Reslice = 1 |
| } |
| } |
| } |
| } |
| |
| func checkassignto(src *Type, dst *Node) { |
| var why string |
| |
| if assignop(src, dst.Type, &why) == 0 { |
| Yyerror("cannot assign %v to %v in multiple assignment%s", Tconv(src, 0), Nconv(dst, obj.FmtLong), why) |
| return |
| } |
| } |
| |
| func typecheckas2(n *Node) { |
| var ll *NodeList |
| var lr *NodeList |
| |
| for ll = n.List; ll != nil; ll = ll.Next { |
| // delicate little dance. |
| ll.N = resolve(ll.N) |
| |
| if ll.N.Defn != n || ll.N.Ntype != nil { |
| typecheck(&ll.N, Erv|Easgn) |
| } |
| } |
| |
| cl := count(n.List) |
| cr := count(n.Rlist) |
| if cl > 1 && cr == 1 { |
| typecheck(&n.Rlist.N, Erv|Efnstruct) |
| } else { |
| typechecklist(n.Rlist, Erv) |
| } |
| checkassignlist(n, n.List) |
| |
| var l *Node |
| var r *Node |
| if cl == cr { |
| // easy |
| ll = n.List |
| lr = n.Rlist |
| for ; ll != nil; (func() { ll = ll.Next; lr = lr.Next })() { |
| if ll.N.Type != nil && lr.N.Type != nil { |
| lr.N = assignconv(lr.N, ll.N.Type, "assignment") |
| } |
| if ll.N.Defn == n && ll.N.Ntype == nil { |
| defaultlit(&lr.N, nil) |
| ll.N.Type = lr.N.Type |
| } |
| } |
| |
| goto out |
| } |
| |
| l = n.List.N |
| r = n.Rlist.N |
| |
| // x,y,z = f() |
| if cr == 1 { |
| if r.Type == nil { |
| goto out |
| } |
| switch r.Op { |
| case OCALLMETH, |
| OCALLINTER, |
| OCALLFUNC: |
| if r.Type.Etype != TSTRUCT || r.Type.Funarg == 0 { |
| break |
| } |
| cr = structcount(r.Type) |
| if cr != cl { |
| goto mismatch |
| } |
| n.Op = OAS2FUNC |
| var s Iter |
| t := Structfirst(&s, &r.Type) |
| for ll = n.List; ll != nil; ll = ll.Next { |
| if t.Type != nil && ll.N.Type != nil { |
| checkassignto(t.Type, ll.N) |
| } |
| if ll.N.Defn == n && ll.N.Ntype == nil { |
| ll.N.Type = t.Type |
| } |
| t = structnext(&s) |
| } |
| |
| 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 |
| } |
| |
| if l.Type != nil { |
| checkassignto(r.Type, l) |
| } |
| if l.Defn == n { |
| l.Type = r.Type |
| } |
| l := n.List.Next.N |
| if l.Type != nil && l.Type.Etype != TBOOL { |
| checkassignto(Types[TBOOL], l) |
| } |
| if l.Defn == n && l.Ntype == nil { |
| l.Type = Types[TBOOL] |
| } |
| goto out |
| } |
| } |
| |
| mismatch: |
| Yyerror("assignment count mismatch: %d = %d", cl, cr) |
| |
| // second half of dance |
| out: |
| n.Typecheck = 1 |
| |
| for ll = n.List; ll != nil; ll = ll.Next { |
| if ll.N.Typecheck == 0 { |
| typecheck(&ll.N, Erv|Easgn) |
| } |
| } |
| } |
| |
| /* |
| * type check function definition |
| */ |
| func typecheckfunc(n *Node) { |
| typecheck(&n.Nname, Erv|Easgn) |
| t := n.Nname.Type |
| if t == nil { |
| return |
| } |
| n.Type = t |
| t.Nname = n.Nname |
| rcvr := getthisx(t).Type |
| if rcvr != nil && n.Shortname != nil && !isblank(n.Shortname) { |
| addmethod(n.Shortname.Sym, t, true, n.Nname.Nointerface) |
| } |
| |
| for l := n.Dcl; l != nil; l = l.Next { |
| if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) { |
| l.N.Decldepth = 1 |
| } |
| } |
| } |
| |
| func stringtoarraylit(np **Node) { |
| n := *np |
| if n.Left.Op != OLITERAL || n.Left.Val.Ctype != CTSTR { |
| Fatal("stringtoarraylit %N", n) |
| } |
| |
| s := n.Left.Val.U.Sval.S |
| var l *NodeList |
| if n.Type.Type.Etype == TUINT8 { |
| // []byte |
| for i := 0; i < len(s); i++ { |
| l = list(l, Nod(OKEY, Nodintconst(int64(i)), Nodintconst(int64(s[0])))) |
| } |
| } else { |
| // []rune |
| i := 0 |
| for _, r := range s { |
| l = list(l, Nod(OKEY, Nodintconst(int64(i)), Nodintconst(int64(r)))) |
| i++ |
| } |
| } |
| |
| nn := Nod(OCOMPLIT, nil, typenod(n.Type)) |
| nn.List = l |
| typecheck(&nn, Erv) |
| *np = nn |
| } |
| |
| var ntypecheckdeftype int |
| |
| var methodqueue *NodeList |
| |
| func domethod(n *Node) { |
| nt := n.Type.Nname |
| typecheck(&nt, Etype) |
| if nt.Type == nil { |
| // type check failed; leave empty func |
| n.Type.Etype = TFUNC |
| |
| n.Type.Nod = nil |
| return |
| } |
| |
| // If we have |
| // type I interface { |
| // M(_ int) |
| // } |
| // then even though I.M looks like it doesn't care about the |
| // value of its argument, a specific implementation of I may |
| // care. The _ would suppress the assignment to that argument |
| // while generating a call, so remove it. |
| for t := getinargx(nt.Type).Type; t != nil; t = t.Down { |
| if t.Sym != nil && t.Sym.Name == "_" { |
| t.Sym = nil |
| } |
| } |
| |
| *n.Type = *nt.Type |
| n.Type.Nod = nil |
| checkwidth(n.Type) |
| } |
| |
| var mapqueue *NodeList |
| |
| func copytype(n *Node, t *Type) { |
| if t.Etype == TFORW { |
| // This type isn't computed yet; when it is, update n. |
| t.Copyto = list(t.Copyto, n) |
| |
| return |
| } |
| |
| maplineno := int(n.Type.Maplineno) |
| embedlineno := int(n.Type.Embedlineno) |
| |
| l := n.Type.Copyto |
| *n.Type = *t |
| |
| t = n.Type |
| t.Sym = n.Sym |
| t.Local = n.Local |
| t.Vargen = n.Vargen |
| t.Siggen = 0 |
| t.Method = nil |
| t.Xmethod = nil |
| t.Nod = nil |
| t.Printed = 0 |
| t.Deferwidth = 0 |
| t.Copyto = nil |
| |
| // Update nodes waiting on this type. |
| for ; l != nil; l = l.Next { |
| copytype(l.N, t) |
| } |
| |
| // Double-check use of type as embedded type. |
| lno := int(lineno) |
| |
| if embedlineno != 0 { |
| lineno = int32(embedlineno) |
| if Isptr[t.Etype] != 0 { |
| Yyerror("embedded type cannot be a pointer") |
| } |
| } |
| |
| lineno = int32(lno) |
| |
| // Queue check for map until all the types are done settling. |
| if maplineno != 0 { |
| t.Maplineno = int32(maplineno) |
| mapqueue = list(mapqueue, n) |
| } |
| } |
| |
| func typecheckdeftype(n *Node) { |
| ntypecheckdeftype++ |
| lno := int(lineno) |
| setlineno(n) |
| n.Type.Sym = n.Sym |
| n.Typecheck = 1 |
| typecheck(&n.Ntype, Etype) |
| t := n.Ntype.Type |
| if t == nil { |
| n.Diag = 1 |
| n.Type = nil |
| goto ret |
| } |
| |
| if n.Type == nil { |
| n.Diag = 1 |
| goto ret |
| } |
| |
| // copy new type and clear fields |
| // that don't come along. |
| // anything zeroed here must be zeroed in |
| // typedcl2 too. |
| copytype(n, t) |
| |
| ret: |
| lineno = int32(lno) |
| |
| // if there are no type definitions going on, it's safe to |
| // try to resolve the method types for the interfaces |
| // we just read. |
| if ntypecheckdeftype == 1 { |
| var l *NodeList |
| for { |
| l = methodqueue |
| if l == nil { |
| break |
| } |
| methodqueue = nil |
| for ; l != nil; l = l.Next { |
| domethod(l.N) |
| } |
| } |
| |
| for l := mapqueue; l != nil; l = l.Next { |
| lineno = l.N.Type.Maplineno |
| maptype(l.N.Type, Types[TBOOL]) |
| } |
| |
| lineno = int32(lno) |
| } |
| |
| ntypecheckdeftype-- |
| } |
| |
| func queuemethod(n *Node) { |
| if ntypecheckdeftype == 0 { |
| domethod(n) |
| return |
| } |
| |
| methodqueue = list(methodqueue, n) |
| } |
| |
| func typecheckdef(n *Node) *Node { |
| lno := int(lineno) |
| setlineno(n) |
| |
| if n.Op == ONONAME { |
| if n.Diag == 0 { |
| n.Diag = 1 |
| if n.Lineno != 0 { |
| lineno = n.Lineno |
| } |
| |
| // Note: adderrorname looks for this string and |
| // adds context about the outer expression |
| Yyerror("undefined: %v", Sconv(n.Sym, 0)) |
| } |
| |
| return n |
| } |
| |
| if n.Walkdef == 1 { |
| return n |
| } |
| |
| l := new(NodeList) |
| l.N = n |
| l.Next = typecheckdefstack |
| typecheckdefstack = l |
| |
| if n.Walkdef == 2 { |
| Flusherrors() |
| fmt.Printf("typecheckdef loop:") |
| for l := typecheckdefstack; l != nil; l = l.Next { |
| fmt.Printf(" %v", Sconv(l.N.Sym, 0)) |
| } |
| fmt.Printf("\n") |
| Fatal("typecheckdef loop") |
| } |
| |
| n.Walkdef = 2 |
| |
| if n.Type != nil || n.Sym == nil { // builtin or no name |
| goto ret |
| } |
| |
| switch n.Op { |
| default: |
| Fatal("typecheckdef %v", Oconv(int(n.Op), 0)) |
| |
| // not really syms |
| case OGOTO, |
| OLABEL: |
| break |
| |
| case OLITERAL: |
| if n.Ntype != nil { |
| typecheck(&n.Ntype, Etype) |
| n.Type = n.Ntype.Type |
| n.Ntype = nil |
| if n.Type == nil { |
| n.Diag = 1 |
| goto ret |
| } |
| } |
| |
| e := n.Defn |
| n.Defn = nil |
| if e == nil { |
| lineno = n.Lineno |
| Dump("typecheckdef nil defn", n) |
| Yyerror("xxx") |
| } |
| |
| typecheck(&e, Erv|Eiota) |
| if Isconst(e, CTNIL) { |
| Yyerror("const initializer cannot be nil") |
| goto ret |
| } |
| |
| if e.Type != nil && e.Op != OLITERAL || !isgoconst(e) { |
| if e.Diag == 0 { |
| Yyerror("const initializer %v is not a constant", Nconv(e, 0)) |
| e.Diag = 1 |
| } |
| |
| goto ret |
| } |
| |
| t := n.Type |
| if t != nil { |
| if okforconst[t.Etype] == 0 { |
| Yyerror("invalid constant type %v", Tconv(t, 0)) |
| goto ret |
| } |
| |
| if !isideal(e.Type) && !Eqtype(t, e.Type) { |
| Yyerror("cannot use %v as type %v in const initializer", Nconv(e, obj.FmtLong), Tconv(t, 0)) |
| goto ret |
| } |
| |
| Convlit(&e, t) |
| } |
| |
| n.Val = e.Val |
| n.Type = e.Type |
| |
| case ONAME: |
| if n.Ntype != nil { |
| typecheck(&n.Ntype, Etype) |
| n.Type = n.Ntype.Type |
| |
| if n.Type == nil { |
| n.Diag = 1 |
| goto ret |
| } |
| } |
| |
| if n.Type != nil { |
| break |
| } |
| if n.Defn == nil { |
| if n.Etype != 0 { // like OPRINTN |
| break |
| } |
| if nsavederrors+nerrors > 0 { |
| // Can have undefined variables in x := foo |
| // that make x have an n->ndefn == nil. |
| // If there are other errors anyway, don't |
| // bother adding to the noise. |
| break |
| } |
| |
| Fatal("var without type, init: %v", Sconv(n.Sym, 0)) |
| } |
| |
| if n.Defn.Op == ONAME { |
| typecheck(&n.Defn, Erv) |
| n.Type = n.Defn.Type |
| break |
| } |
| |
| typecheck(&n.Defn, Etop) // fills in n->type |
| |
| case OTYPE: |
| if Curfn != nil { |
| defercheckwidth() |
| } |
| n.Walkdef = 1 |
| n.Type = typ(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.Broke = 1 |
| } |
| |
| if Curfn != nil { |
| resumecheckwidth() |
| } |
| |
| // nothing to see here |
| case OPACK: |
| break |
| } |
| |
| ret: |
| if n.Op != OLITERAL && n.Type != nil && isideal(n.Type) { |
| Fatal("got %v for %v", Tconv(n.Type, 0), Nconv(n, 0)) |
| } |
| if typecheckdefstack.N != n { |
| Fatal("typecheckdefstack mismatch") |
| } |
| l = typecheckdefstack |
| typecheckdefstack = l.Next |
| |
| lineno = int32(lno) |
| n.Walkdef = 1 |
| return n |
| } |
| |
| func checkmake(t *Type, arg string, n *Node) int { |
| if n.Op == OLITERAL { |
| switch n.Val.Ctype { |
| case CTINT, |
| CTRUNE, |
| CTFLT, |
| CTCPLX: |
| n.Val = toint(n.Val) |
| if mpcmpfixc(n.Val.U.Xval, 0) < 0 { |
| Yyerror("negative %s argument in make(%v)", arg, Tconv(t, 0)) |
| return -1 |
| } |
| |
| if Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT]) > 0 { |
| Yyerror("%s argument too large in make(%v)", arg, Tconv(t, 0)) |
| return -1 |
| } |
| |
| // Delay defaultlit until after we've checked range, to avoid |
| // a redundant "constant NNN overflows int" error. |
| defaultlit(&n, Types[TINT]) |
| |
| return 0 |
| |
| default: |
| break |
| } |
| } |
| |
| if Isint[n.Type.Etype] == 0 && n.Type.Etype != TIDEAL { |
| Yyerror("non-integer %s argument in make(%v) - %v", arg, Tconv(t, 0), Tconv(n.Type, 0)) |
| return -1 |
| } |
| |
| // Defaultlit still necessary for non-constant: n might be 1<<k. |
| defaultlit(&n, Types[TINT]) |
| |
| return 0 |
| } |
| |
| func markbreak(n *Node, implicit *Node) { |
| if n == nil { |
| return |
| } |
| |
| switch n.Op { |
| case OBREAK: |
| if n.Left == nil { |
| if implicit != nil { |
| implicit.Hasbreak = 1 |
| } |
| } else { |
| lab := n.Left.Sym.Label |
| if lab != nil { |
| lab.Def.Hasbreak = 1 |
| } |
| } |
| |
| case OFOR, |
| OSWITCH, |
| OTYPESW, |
| OSELECT, |
| ORANGE: |
| implicit = n |
| fallthrough |
| |
| // fall through |
| default: |
| markbreak(n.Left, implicit) |
| |
| markbreak(n.Right, implicit) |
| markbreak(n.Ntest, implicit) |
| markbreak(n.Nincr, implicit) |
| markbreaklist(n.Ninit, implicit) |
| markbreaklist(n.Nbody, implicit) |
| markbreaklist(n.Nelse, implicit) |
| markbreaklist(n.List, implicit) |
| markbreaklist(n.Rlist, implicit) |
| } |
| } |
| |
| func markbreaklist(l *NodeList, implicit *Node) { |
| var n *Node |
| var lab *Label |
| |
| for ; l != nil; l = l.Next { |
| n = l.N |
| if n.Op == OLABEL && l.Next != nil && n.Defn == l.Next.N { |
| switch n.Defn.Op { |
| case OFOR, |
| OSWITCH, |
| OTYPESW, |
| OSELECT, |
| ORANGE: |
| lab = new(Label) |
| lab.Def = n.Defn |
| n.Left.Sym.Label = lab |
| markbreak(n.Defn, n.Defn) |
| n.Left.Sym.Label = nil |
| l = l.Next |
| continue |
| } |
| } |
| |
| markbreak(n, implicit) |
| } |
| } |
| |
| func isterminating(l *NodeList, top int) bool { |
| if l == nil { |
| return false |
| } |
| if top != 0 { |
| for l.Next != nil && l.N.Op != OLABEL { |
| l = l.Next |
| } |
| markbreaklist(l, nil) |
| } |
| |
| for l.Next != nil { |
| l = l.Next |
| } |
| n := l.N |
| |
| if n == nil { |
| return false |
| } |
| |
| switch n.Op { |
| // NOTE: OLABEL is treated as a separate statement, |
| // not a separate prefix, so skipping to the last statement |
| // in the block handles the labeled statement case by |
| // skipping over the label. No case OLABEL here. |
| |
| case OBLOCK: |
| return isterminating(n.List, 0) |
| |
| case OGOTO, |
| ORETURN, |
| ORETJMP, |
| OPANIC, |
| OXFALL: |
| return true |
| |
| case OFOR: |
| if n.Ntest != nil { |
| return false |
| } |
| if n.Hasbreak != 0 { |
| return false |
| } |
| return true |
| |
| case OIF: |
| return isterminating(n.Nbody, 0) && isterminating(n.Nelse, 0) |
| |
| case OSWITCH, |
| OTYPESW, |
| OSELECT: |
| if n.Hasbreak != 0 { |
| return false |
| } |
| def := 0 |
| for l = n.List; l != nil; l = l.Next { |
| if !isterminating(l.N.Nbody, 0) { |
| return false |
| } |
| if l.N.List == nil { // default |
| def = 1 |
| } |
| } |
| |
| if n.Op != OSELECT && def == 0 { |
| return false |
| } |
| return true |
| } |
| |
| return false |
| } |
| |
| func checkreturn(fn *Node) { |
| if fn.Type.Outtuple != 0 && fn.Nbody != nil { |
| if !isterminating(fn.Nbody, 1) { |
| yyerrorl(int(fn.Endlineno), "missing return at end of function") |
| } |
| } |
| } |