// 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 ( "bytes" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/src" "fmt" "strings" ) // Declaration stack & operations var externdcl []*Node func testdclstack() { if !types.IsDclstackValid() { if nerrors != 0 { errorexit() } Fatalf("mark left on the dclstack") } } // redeclare emits a diagnostic about symbol s being redeclared at pos. func redeclare(pos src.XPos, s *types.Sym, where string) { if !s.Lastlineno.IsKnown() { pkg := s.Origpkg if pkg == nil { pkg = s.Pkg } yyerrorl(pos, "%v redeclared %s\n"+ "\tprevious declaration during import %q", s, where, pkg.Path) } else { prevPos := s.Lastlineno // When an import and a declaration collide in separate files, // present the import as the "redeclared", because the declaration // is visible where the import is, but not vice versa. // See issue 4510. if s.Def == nil { pos, prevPos = prevPos, pos } yyerrorl(pos, "%v redeclared %s\n"+ "\tprevious declaration at %v", s, where, linestr(prevPos)) } } var vargen int // declare individual names - var, typ, const var declare_typegen int // declare records that Node n declares symbol n.Sym in the specified // declaration context. func declare(n *Node, ctxt Class) { if n.isBlank() { return } if n.Name == nil { // named OLITERAL needs Name; most OLITERALs don't. n.Name = new(Name) } s := n.Sym // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later. if !inimport && !typecheckok && s.Pkg != localpkg { yyerrorl(n.Pos, "cannot declare name %v", s) } gen := 0 if ctxt == PEXTERN { if s.Name == "init" { yyerrorl(n.Pos, "cannot declare init - must be func") } if s.Name == "main" && s.Pkg.Name == "main" { yyerrorl(n.Pos, "cannot declare main - must be func") } externdcl = append(externdcl, n) } else { if Curfn == nil && ctxt == PAUTO { lineno = n.Pos Fatalf("automatic outside function") } if Curfn != nil && ctxt != PFUNC { Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) } if n.Op == OTYPE { declare_typegen++ gen = declare_typegen } else if n.Op == ONAME && ctxt == PAUTO && !strings.Contains(s.Name, "·") { vargen++ gen = vargen } types.Pushdcl(s) n.Name.Curfn = Curfn } if ctxt == PAUTO { n.Xoffset = 0 } if s.Block == types.Block { // functype will print errors about duplicate function arguments. // Don't repeat the error here. if ctxt != PPARAM && ctxt != PPARAMOUT { redeclare(n.Pos, s, "in this block") } } s.Block = types.Block s.Lastlineno = lineno s.Def = asTypesNode(n) n.Name.Vargen = int32(gen) n.SetClass(ctxt) if ctxt == PFUNC { n.Sym.SetFunc(true) } autoexport(n, ctxt) } func addvar(n *Node, t *types.Type, ctxt Class) { if n == nil || n.Sym == nil || (n.Op != ONAME && n.Op != ONONAME) || t == nil { Fatalf("addvar: n=%v t=%v nil", n, t) } n.Op = ONAME declare(n, ctxt) n.Type = t } // declare variables from grammar // new_name_list (type | [type] = expr_list) func variter(vl []*Node, t *Node, el []*Node) []*Node { var init []*Node doexpr := len(el) > 0 if len(el) == 1 && len(vl) > 1 { e := el[0] as2 := nod(OAS2, nil, nil) as2.List.Set(vl) as2.Rlist.Set1(e) for _, v := range vl { v.Op = ONAME declare(v, dclcontext) v.Name.Param.Ntype = t v.Name.Defn = as2 if Curfn != nil { init = append(init, nod(ODCL, v, nil)) } } return append(init, as2) } nel := len(el) for _, v := range vl { var e *Node if doexpr { if len(el) == 0 { yyerror("assignment mismatch: %d variables but %d values", len(vl), nel) break } e = el[0] el = el[1:] } v.Op = ONAME declare(v, dclcontext) v.Name.Param.Ntype = t if e != nil || Curfn != nil || v.isBlank() { if Curfn != nil { init = append(init, nod(ODCL, v, nil)) } e = nod(OAS, v, e) init = append(init, e) if e.Right != nil { v.Name.Defn = e } } } if len(el) != 0 { yyerror("assignment mismatch: %d variables but %d values", len(vl), nel) } return init } // newnoname returns a new ONONAME Node associated with symbol s. func newnoname(s *types.Sym) *Node { if s == nil { Fatalf("newnoname nil") } n := nod(ONONAME, nil, nil) n.Sym = s n.Xoffset = 0 return n } // newfuncnamel generates a new name node for a function or method. // TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360. func newfuncnamel(pos src.XPos, s *types.Sym) *Node { n := newnamel(pos, s) n.Func = new(Func) n.Func.SetIsHiddenClosure(Curfn != nil) return n } // this generates a new name node for a name // being declared. func dclname(s *types.Sym) *Node { n := newname(s) n.Op = ONONAME // caller will correct it return n } func typenod(t *types.Type) *Node { return typenodl(src.NoXPos, t) } func typenodl(pos src.XPos, t *types.Type) *Node { // if we copied another type with *t = *u // then t->nod might be out of date, so // check t->nod->type too if asNode(t.Nod) == nil || asNode(t.Nod).Type != t { t.Nod = asTypesNode(nodl(pos, OTYPE, nil, nil)) asNode(t.Nod).Type = t asNode(t.Nod).Sym = t.Sym } return asNode(t.Nod) } func anonfield(typ *types.Type) *Node { return symfield(nil, typ) } func namedfield(s string, typ *types.Type) *Node { return symfield(lookup(s), typ) } func symfield(s *types.Sym, typ *types.Type) *Node { n := nodSym(ODCLFIELD, nil, s) n.Type = typ return n } // oldname returns the Node that declares symbol s in the current scope. // If no such Node currently exists, an ONONAME Node is returned instead. func oldname(s *types.Sym) *Node { n := asNode(s.Def) if n == nil { // Maybe a top-level declaration will come along later to // define s. resolve will check s.Def again once all input // source has been processed. return newnoname(s) } if Curfn != nil && n.Op == ONAME && n.Name.Curfn != nil && n.Name.Curfn != Curfn { // Inner func is referring to var in outer func. // // TODO(rsc): If there is an outer variable x and we // are parsing x := 5 inside the closure, until we get to // the := it looks like a reference to the outer x so we'll // make x a closure variable unnecessarily. c := n.Name.Param.Innermost if c == nil || c.Name.Curfn != Curfn { // Do not have a closure var for the active closure yet; make one. c = newname(s) c.SetClass(PAUTOHEAP) c.Name.SetIsClosureVar(true) c.SetIsDDD(n.IsDDD()) c.Name.Defn = n // Link into list of active closure variables. // Popped from list in func closurebody. c.Name.Param.Outer = n.Name.Param.Innermost n.Name.Param.Innermost = c Curfn.Func.Cvars.Append(c) } // return ref to closure var, not original return c } return n } // importName is like oldname, but it reports an error if sym is from another package and not exported. func importName(sym *types.Sym) *Node { n := oldname(sym) if !types.IsExported(sym.Name) && sym.Pkg != localpkg { n.SetDiag(true) yyerror("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name) } return n } // := declarations func colasname(n *Node) bool { switch n.Op { case ONAME, ONONAME, OPACK, OTYPE, OLITERAL: return n.Sym != nil } return false } func colasdefn(left []*Node, defn *Node) { for _, n := range left { if n.Sym != nil { n.Sym.SetUniq(true) } } var nnew, nerr int for i, n := range left { if n.isBlank() { continue } if !colasname(n) { yyerrorl(defn.Pos, "non-name %v on left side of :=", n) nerr++ continue } if !n.Sym.Uniq() { yyerrorl(defn.Pos, "%v repeated on left side of :=", n.Sym) n.SetDiag(true) nerr++ continue } n.Sym.SetUniq(false) if n.Sym.Block == types.Block { continue } nnew++ n = newname(n.Sym) declare(n, dclcontext) n.Name.Defn = defn defn.Ninit.Append(nod(ODCL, n, nil)) left[i] = n } if nnew == 0 && nerr == 0 { yyerrorl(defn.Pos, "no new variables on left side of :=") } } // declare the arguments in an // interface field declaration. func ifacedcl(n *Node) { if n.Op != ODCLFIELD || n.Left == nil { Fatalf("ifacedcl") } if n.Sym.IsBlank() { yyerror("methods must have a unique non-blank name") } } // declare the function proper // and declare the arguments. // called in extern-declaration context // returns in auto-declaration context. func funchdr(n *Node) { // change the declaration context from extern to auto funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext}) Curfn = n dclcontext = PAUTO types.Markdcl() if n.Func.Nname != nil { funcargs(n.Func.Nname.Name.Param.Ntype) } else if n.Func.Ntype != nil { funcargs(n.Func.Ntype) } else { funcargs2(n.Type) } } func funcargs(nt *Node) { if nt.Op != OTFUNC { Fatalf("funcargs %v", nt.Op) } // re-start the variable generation number // we want to use small numbers for the return variables, // so let them have the chunk starting at 1. // // TODO(mdempsky): This is ugly, and only necessary because // esc.go uses Vargen to figure out result parameters' index // within the result tuple. vargen = nt.Rlist.Len() // declare the receiver and in arguments. if nt.Left != nil { funcarg(nt.Left, PPARAM) } for _, n := range nt.List.Slice() { funcarg(n, PPARAM) } oldvargen := vargen vargen = 0 // declare the out arguments. gen := nt.List.Len() for _, n := range nt.Rlist.Slice() { if n.Sym == nil { // Name so that escape analysis can track it. ~r stands for 'result'. n.Sym = lookupN("~r", gen) gen++ } if n.Sym.IsBlank() { // Give it a name so we can assign to it during return. ~b stands for 'blank'. // The name must be different from ~r above because if you have // func f() (_ int) // func g() int // f is allowed to use a plain 'return' with no arguments, while g is not. // So the two cases must be distinguished. n.Sym = lookupN("~b", gen) gen++ } funcarg(n, PPARAMOUT) } vargen = oldvargen } func funcarg(n *Node, ctxt Class) { if n.Op != ODCLFIELD { Fatalf("funcarg %v", n.Op) } if n.Sym == nil { return } n.Right = newnamel(n.Pos, n.Sym) n.Right.Name.Param.Ntype = n.Left n.Right.SetIsDDD(n.IsDDD()) declare(n.Right, ctxt) vargen++ n.Right.Name.Vargen = int32(vargen) } // Same as funcargs, except run over an already constructed TFUNC. // This happens during import, where the hidden_fndcl rule has // used functype directly to parse the function's type. func funcargs2(t *types.Type) { if t.Etype != TFUNC { Fatalf("funcargs2 %v", t) } for _, f := range t.Recvs().Fields().Slice() { funcarg2(f, PPARAM) } for _, f := range t.Params().Fields().Slice() { funcarg2(f, PPARAM) } for _, f := range t.Results().Fields().Slice() { funcarg2(f, PPARAMOUT) } } func funcarg2(f *types.Field, ctxt Class) { if f.Sym == nil { return } n := newnamel(f.Pos, f.Sym) f.Nname = asTypesNode(n) n.Type = f.Type n.SetIsDDD(f.IsDDD()) declare(n, ctxt) } var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext type funcStackEnt struct { curfn *Node dclcontext Class } // finish the body. // called in auto-declaration context. // returns in extern-declaration context. func funcbody() { // change the declaration context from auto to previous context types.Popdcl() var e funcStackEnt funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1] Curfn, dclcontext = e.curfn, e.dclcontext } // structs, functions, and methods. // they don't belong here, but where do they belong? func checkembeddedtype(t *types.Type) { if t == nil { return } if t.Sym == nil && t.IsPtr() { t = t.Elem() if t.IsInterface() { yyerror("embedded type cannot be a pointer to interface") } } if t.IsPtr() || t.IsUnsafePtr() { yyerror("embedded type cannot be a pointer") } else if t.Etype == TFORW && !t.ForwardType().Embedlineno.IsKnown() { t.ForwardType().Embedlineno = lineno } } func structfield(n *Node) *types.Field { lno := lineno lineno = n.Pos if n.Op != ODCLFIELD { Fatalf("structfield: oops %v\n", n) } f := types.NewField() f.Pos = n.Pos f.Sym = n.Sym if n.Left != nil { n.Left = typecheck(n.Left, ctxType) n.Type = n.Left.Type n.Left = nil } f.Type = n.Type if f.Type == nil { f.SetBroke(true) } if n.Embedded() { checkembeddedtype(n.Type) f.Embedded = 1 } else { f.Embedded = 0 } switch u := n.Val().U.(type) { case string: f.Note = u default: yyerror("field tag must be a string") case nil: // no-op } lineno = lno return f } // checkdupfields emits errors for duplicately named fields or methods in // a list of struct or interface types. func checkdupfields(what string, fss ...[]*types.Field) { seen := make(map[*types.Sym]bool) for _, fs := range fss { for _, f := range fs { if f.Sym == nil || f.Sym.IsBlank() { continue } if seen[f.Sym] { yyerrorl(f.Pos, "duplicate %s %s", what, f.Sym.Name) continue } seen[f.Sym] = true } } } // convert a parsed id/type list into // a type for struct/interface/arglist func tostruct(l []*Node) *types.Type { t := types.New(TSTRUCT) fields := make([]*types.Field, len(l)) for i, n := range l { f := structfield(n) if f.Broke() { t.SetBroke(true) } fields[i] = f } t.SetFields(fields) checkdupfields("field", t.FieldSlice()) if !t.Broke() { checkwidth(t) } return t } func tofunargs(l []*Node, funarg types.Funarg) *types.Type { t := types.New(TSTRUCT) t.StructType().Funarg = funarg fields := make([]*types.Field, len(l)) for i, n := range l { f := structfield(n) f.SetIsDDD(n.IsDDD()) if n.Right != nil { n.Right.Type = f.Type f.Nname = asTypesNode(n.Right) } if f.Broke() { t.SetBroke(true) } fields[i] = f } t.SetFields(fields) return t } func tofunargsfield(fields []*types.Field, funarg types.Funarg) *types.Type { t := types.New(TSTRUCT) t.StructType().Funarg = funarg t.SetFields(fields) return t } func interfacefield(n *Node) *types.Field { lno := lineno lineno = n.Pos if n.Op != ODCLFIELD { Fatalf("interfacefield: oops %v\n", n) } if n.Val().Ctype() != CTxxx { yyerror("interface method cannot have annotation") } // MethodSpec = MethodName Signature | InterfaceTypeName . // // If Sym != nil, then Sym is MethodName and Left is Signature. // Otherwise, Left is InterfaceTypeName. if n.Left != nil { n.Left = typecheck(n.Left, ctxType) n.Type = n.Left.Type n.Left = nil } f := types.NewField() f.Pos = n.Pos f.Sym = n.Sym f.Type = n.Type if f.Type == nil { f.SetBroke(true) } lineno = lno return f } func tointerface(l []*Node) *types.Type { if len(l) == 0 { return types.Types[TINTER] } t := types.New(TINTER) var fields []*types.Field for _, n := range l { f := interfacefield(n) if f.Broke() { t.SetBroke(true) } fields = append(fields, f) } t.SetInterface(fields) return t } func fakeRecv() *Node { return anonfield(types.FakeRecvType()) } func fakeRecvField() *types.Field { f := types.NewField() f.Type = types.FakeRecvType() return f } // isifacemethod reports whether (field) m is // an interface method. Such methods have the // special receiver type types.FakeRecvType(). func isifacemethod(f *types.Type) bool { return f.Recv().Type == types.FakeRecvType() } // turn a parsed function declaration into a type func functype(this *Node, in, out []*Node) *types.Type { t := types.New(TFUNC) var rcvr []*Node if this != nil { rcvr = []*Node{this} } t.FuncType().Receiver = tofunargs(rcvr, types.FunargRcvr) t.FuncType().Params = tofunargs(in, types.FunargParams) t.FuncType().Results = tofunargs(out, types.FunargResults) checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice()) if t.Recvs().Broke() || t.Results().Broke() || t.Params().Broke() { t.SetBroke(true) } t.FuncType().Outnamed = t.NumResults() > 0 && origSym(t.Results().Field(0).Sym) != nil return t } func functypefield(this *types.Field, in, out []*types.Field) *types.Type { t := types.New(TFUNC) var rcvr []*types.Field if this != nil { rcvr = []*types.Field{this} } t.FuncType().Receiver = tofunargsfield(rcvr, types.FunargRcvr) t.FuncType().Params = tofunargsfield(in, types.FunargParams) t.FuncType().Results = tofunargsfield(out, types.FunargResults) t.FuncType().Outnamed = t.NumResults() > 0 && origSym(t.Results().Field(0).Sym) != nil return t } // origSym returns the original symbol written by the user. func origSym(s *types.Sym) *types.Sym { if s == nil { return nil } if len(s.Name) > 1 && s.Name[0] == '~' { switch s.Name[1] { case 'r': // originally an unnamed result return nil case 'b': // originally the blank identifier _ // TODO(mdempsky): Does s.Pkg matter here? return nblank.Sym } return s } if strings.HasPrefix(s.Name, ".anon") { // originally an unnamed or _ name (see subr.go: structargs) return nil } return s } // methodSym returns the method symbol representing a method name // associated with a specific receiver type. // // Method symbols can be used to distinguish the same method appearing // in different method sets. For example, T.M and (*T).M have distinct // method symbols. // // The returned symbol will be marked as a function. func methodSym(recv *types.Type, msym *types.Sym) *types.Sym { sym := methodSymSuffix(recv, msym, "") sym.SetFunc(true) return sym } // methodSymSuffix is like methodsym, but allows attaching a // distinguisher suffix. To avoid collisions, the suffix must not // start with a letter, number, or period. func methodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym { if msym.IsBlank() { Fatalf("blank method name") } rsym := recv.Sym if recv.IsPtr() { if rsym != nil { Fatalf("declared pointer receiver type: %v", recv) } rsym = recv.Elem().Sym } // Find the package the receiver type appeared in. For // anonymous receiver types (i.e., anonymous structs with // embedded fields), use the "go" pseudo-package instead. rpkg := gopkg if rsym != nil { rpkg = rsym.Pkg } var b bytes.Buffer if recv.IsPtr() { // The parentheses aren't really necessary, but // they're pretty traditional at this point. fmt.Fprintf(&b, "(%-S)", recv) } else { fmt.Fprintf(&b, "%-S", recv) } // A particular receiver type may have multiple non-exported // methods with the same name. To disambiguate them, include a // package qualifier for names that came from a different // package than the receiver type. if !types.IsExported(msym.Name) && msym.Pkg != rpkg { b.WriteString(".") b.WriteString(msym.Pkg.Prefix) } b.WriteString(".") b.WriteString(msym.Name) b.WriteString(suffix) return rpkg.LookupBytes(b.Bytes()) } // Add a method, declared as a function. // - msym is the method symbol // - t is function type (with receiver) // Returns a pointer to the existing or added Field; or nil if there's an error. func addmethod(msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field { if msym == nil { Fatalf("no method symbol") } // get parent type sym rf := t.Recv() // ptr to this structure if rf == nil { yyerror("missing receiver") return nil } mt := methtype(rf.Type) if mt == nil || mt.Sym == nil { pa := rf.Type t := pa if t != nil && t.IsPtr() { if t.Sym != nil { yyerror("invalid receiver type %v (%v is a pointer type)", pa, t) return nil } t = t.Elem() } switch { case t == nil || t.Broke(): // rely on typecheck having complained before case t.Sym == nil: yyerror("invalid receiver type %v (%v is not a defined type)", pa, t) case t.IsPtr(): yyerror("invalid receiver type %v (%v is a pointer type)", pa, t) case t.IsInterface(): yyerror("invalid receiver type %v (%v is an interface type)", pa, t) default: // Should have picked off all the reasons above, // but just in case, fall back to generic error. yyerror("invalid receiver type %v (%L / %L)", pa, pa, t) } return nil } if local && mt.Sym.Pkg != localpkg { yyerror("cannot define new methods on non-local type %v", mt) return nil } if msym.IsBlank() { return nil } if mt.IsStruct() { for _, f := range mt.Fields().Slice() { if f.Sym == msym { yyerror("type %v has both field and method named %v", mt, msym) f.SetBroke(true) return nil } } } for _, f := range mt.Methods().Slice() { if msym.Name != f.Sym.Name { continue } // types.Identical only checks that incoming and result parameters match, // so explicitly check that the receiver parameters match too. if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) { yyerror("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t) } return f } f := types.NewField() f.Pos = lineno f.Sym = msym f.Type = t f.SetNointerface(nointerface) mt.Methods().Append(f) return f } func funcsymname(s *types.Sym) string { return s.Name + "·f" } // funcsym returns s·f. func funcsym(s *types.Sym) *types.Sym { // funcsymsmu here serves to protect not just mutations of funcsyms (below), // but also the package lookup of the func sym name, // since this function gets called concurrently from the backend. // There are no other concurrent package lookups in the backend, // except for the types package, which is protected separately. // Reusing funcsymsmu to also cover this package lookup // avoids a general, broader, expensive package lookup mutex. // Note makefuncsym also does package look-up of func sym names, // but that it is only called serially, from the front end. funcsymsmu.Lock() sf, existed := s.Pkg.LookupOK(funcsymname(s)) // Don't export s·f when compiling for dynamic linking. // When dynamically linking, the necessary function // symbols will be created explicitly with makefuncsym. // See the makefuncsym comment for details. if !Ctxt.Flag_dynlink && !existed { funcsyms = append(funcsyms, s) } funcsymsmu.Unlock() return sf } // makefuncsym ensures that s·f is exported. // It is only used with -dynlink. // When not compiling for dynamic linking, // the funcsyms are created as needed by // the packages that use them. // Normally we emit the s·f stubs as DUPOK syms, // but DUPOK doesn't work across shared library boundaries. // So instead, when dynamic linking, we only create // the s·f stubs in s's package. func makefuncsym(s *types.Sym) { if !Ctxt.Flag_dynlink { Fatalf("makefuncsym dynlink") } if s.IsBlank() { return } if compiling_runtime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") { // runtime.getg(), getclosureptr(), getcallerpc(), and // getcallersp() are not real functions and so do not // get funcsyms. return } if _, existed := s.Pkg.LookupOK(funcsymname(s)); !existed { funcsyms = append(funcsyms, s) } } // setNodeNameFunc marks a node as a function. func setNodeNameFunc(n *Node) { if n.Op != ONAME || n.Class() != Pxxx { Fatalf("expected ONAME/Pxxx node, got %v", n) } n.SetClass(PFUNC) n.Sym.SetFunc(true) } func dclfunc(sym *types.Sym, tfn *Node) *Node { if tfn.Op != OTFUNC { Fatalf("expected OTFUNC node, got %v", tfn) } fn := nod(ODCLFUNC, nil, nil) fn.Func.Nname = newfuncnamel(lineno, sym) fn.Func.Nname.Name.Defn = fn fn.Func.Nname.Name.Param.Ntype = tfn setNodeNameFunc(fn.Func.Nname) funchdr(fn) fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, ctxType) return fn } type nowritebarrierrecChecker struct { // extraCalls contains extra function calls that may not be // visible during later analysis. It maps from the ODCLFUNC of // the caller to a list of callees. extraCalls map[*Node][]nowritebarrierrecCall // curfn is the current function during AST walks. curfn *Node } type nowritebarrierrecCall struct { target *Node // ODCLFUNC of caller or callee lineno src.XPos // line of call } type nowritebarrierrecCallSym struct { target *obj.LSym // LSym of callee lineno src.XPos // line of call } // newNowritebarrierrecChecker creates a nowritebarrierrecChecker. It // must be called before transformclosure and walk. func newNowritebarrierrecChecker() *nowritebarrierrecChecker { c := &nowritebarrierrecChecker{ extraCalls: make(map[*Node][]nowritebarrierrecCall), } // Find all systemstack calls and record their targets. In // general, flow analysis can't see into systemstack, but it's // important to handle it for this check, so we model it // directly. This has to happen before transformclosure since // it's a lot harder to work out the argument after. for _, n := range xtop { if n.Op != ODCLFUNC { continue } c.curfn = n inspect(n, c.findExtraCalls) } c.curfn = nil return c } func (c *nowritebarrierrecChecker) findExtraCalls(n *Node) bool { if n.Op != OCALLFUNC { return true } fn := n.Left if fn == nil || fn.Op != ONAME || fn.Class() != PFUNC || fn.Name.Defn == nil { return true } if !isRuntimePkg(fn.Sym.Pkg) || fn.Sym.Name != "systemstack" { return true } var callee *Node arg := n.List.First() switch arg.Op { case ONAME: callee = arg.Name.Defn case OCLOSURE: callee = arg.Func.Closure default: Fatalf("expected ONAME or OCLOSURE node, got %+v", arg) } if callee.Op != ODCLFUNC { Fatalf("expected ODCLFUNC node, got %+v", callee) } c.extraCalls[c.curfn] = append(c.extraCalls[c.curfn], nowritebarrierrecCall{callee, n.Pos}) return true } // recordCall records a call from ODCLFUNC node "from", to function // symbol "to" at position pos. // // This should be done as late as possible during compilation to // capture precise call graphs. The target of the call is an LSym // because that's all we know after we start SSA. // // This can be called concurrently for different from Nodes. func (c *nowritebarrierrecChecker) recordCall(from *Node, to *obj.LSym, pos src.XPos) { if from.Op != ODCLFUNC { Fatalf("expected ODCLFUNC, got %v", from) } // We record this information on the *Func so this is // concurrent-safe. fn := from.Func if fn.nwbrCalls == nil { fn.nwbrCalls = new([]nowritebarrierrecCallSym) } *fn.nwbrCalls = append(*fn.nwbrCalls, nowritebarrierrecCallSym{to, pos}) } func (c *nowritebarrierrecChecker) check() { // We walk the call graph as late as possible so we can // capture all calls created by lowering, but this means we // only get to see the obj.LSyms of calls. symToFunc lets us // get back to the ODCLFUNCs. symToFunc := make(map[*obj.LSym]*Node) // funcs records the back-edges of the BFS call graph walk. It // maps from the ODCLFUNC of each function that must not have // write barriers to the call that inhibits them. Functions // that are directly marked go:nowritebarrierrec are in this // map with a zero-valued nowritebarrierrecCall. This also // acts as the set of marks for the BFS of the call graph. funcs := make(map[*Node]nowritebarrierrecCall) // q is the queue of ODCLFUNC Nodes to visit in BFS order. var q nodeQueue for _, n := range xtop { if n.Op != ODCLFUNC { continue } symToFunc[n.Func.lsym] = n // Make nowritebarrierrec functions BFS roots. if n.Func.Pragma&Nowritebarrierrec != 0 { funcs[n] = nowritebarrierrecCall{} q.pushRight(n) } // Check go:nowritebarrier functions. if n.Func.Pragma&Nowritebarrier != 0 && n.Func.WBPos.IsKnown() { yyerrorl(n.Func.WBPos, "write barrier prohibited") } } // Perform a BFS of the call graph from all // go:nowritebarrierrec functions. enqueue := func(src, target *Node, pos src.XPos) { if target.Func.Pragma&Yeswritebarrierrec != 0 { // Don't flow into this function. return } if _, ok := funcs[target]; ok { // Already found a path to target. return } // Record the path. funcs[target] = nowritebarrierrecCall{target: src, lineno: pos} q.pushRight(target) } for !q.empty() { fn := q.popLeft() // Check fn. if fn.Func.WBPos.IsKnown() { var err bytes.Buffer call := funcs[fn] for call.target != nil { fmt.Fprintf(&err, "\n\t%v: called by %v", linestr(call.lineno), call.target.Func.Nname) call = funcs[call.target] } yyerrorl(fn.Func.WBPos, "write barrier prohibited by caller; %v%s", fn.Func.Nname, err.String()) continue } // Enqueue fn's calls. for _, callee := range c.extraCalls[fn] { enqueue(fn, callee.target, callee.lineno) } if fn.Func.nwbrCalls == nil { continue } for _, callee := range *fn.Func.nwbrCalls { target := symToFunc[callee.target] if target != nil { enqueue(fn, target, callee.lineno) } } } }