diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/decl.go')
-rw-r--r-- | src/cmd/compile/internal/noder/decl.go | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go new file mode 100644 index 0000000000..ce5bad88f3 --- /dev/null +++ b/src/cmd/compile/internal/noder/decl.go @@ -0,0 +1,222 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" +) + +// TODO(mdempsky): Skip blank declarations? Probably only safe +// for declarations without pragmas. + +func (g *irgen) decls(decls []syntax.Decl) []ir.Node { + var res ir.Nodes + for _, decl := range decls { + switch decl := decl.(type) { + case *syntax.ConstDecl: + g.constDecl(&res, decl) + case *syntax.FuncDecl: + g.funcDecl(&res, decl) + case *syntax.TypeDecl: + if ir.CurFunc == nil { + continue // already handled in irgen.generate + } + g.typeDecl(&res, decl) + case *syntax.VarDecl: + g.varDecl(&res, decl) + default: + g.unhandled("declaration", decl) + } + } + return res +} + +func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) { + // TODO(mdempsky): Merge with gcimports so we don't have to import + // packages twice. + + g.pragmaFlags(decl.Pragma, 0) + + ipkg := importfile(decl) + if ipkg == ir.Pkgs.Unsafe { + p.importedUnsafe = true + } +} + +func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) { + g.pragmaFlags(decl.Pragma, 0) + + for _, name := range decl.NameList { + name, obj := g.def(name) + name.SetVal(obj.(*types2.Const).Val()) + out.Append(ir.NewDecl(g.pos(decl), ir.ODCLCONST, name)) + } +} + +func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) { + fn := ir.NewFunc(g.pos(decl)) + fn.Nname, _ = g.def(decl.Name) + fn.Nname.Func = fn + fn.Nname.Defn = fn + + fn.Pragma = g.pragmaFlags(decl.Pragma, funcPragmas) + if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 { + base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined") + } + + if decl.Name.Value == "init" && decl.Recv == nil { + g.target.Inits = append(g.target.Inits, fn) + } + + g.funcBody(fn, decl.Recv, decl.Type, decl.Body) + + out.Append(fn) +} + +func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { + if decl.Alias { + if !types.AllowsGoVersion(types.LocalPkg, 1, 9) { + base.ErrorfAt(g.pos(decl), "type aliases only supported as of -lang=go1.9") + } + + name, _ := g.def(decl.Name) + g.pragmaFlags(decl.Pragma, 0) + + // TODO(mdempsky): This matches how typecheckdef marks aliases for + // export, but this won't generalize to exporting function-scoped + // type aliases. We should maybe just use n.Alias() instead. + if ir.CurFunc == nil { + name.Sym().Def = ir.TypeNode(name.Type()) + } + + out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name)) + return + } + + name, obj := g.def(decl.Name) + ntyp, otyp := name.Type(), obj.Type() + if ir.CurFunc != nil { + typecheck.TypeGen++ + ntyp.Vargen = typecheck.TypeGen + } + + pragmas := g.pragmaFlags(decl.Pragma, typePragmas) + name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed? + + if pragmas&ir.NotInHeap != 0 { + ntyp.SetNotInHeap(true) + } + + // We need to use g.typeExpr(decl.Type) here to ensure that for + // chained, defined-type declarations like + // + // type T U + // + // //go:notinheap + // type U struct { … } + // + // that we mark both T and U as NotInHeap. If we instead used just + // g.typ(otyp.Underlying()), then we'd instead set T's underlying + // type directly to the struct type (which is not marked NotInHeap) + // and fail to mark T as NotInHeap. + // + // Also, we rely here on Type.SetUnderlying allowing passing a + // defined type and handling forward references like from T to U + // above. Contrast with go/types's Named.SetUnderlying, which + // disallows this. + // + // [mdempsky: Subtleties like these are why I always vehemently + // object to new type pragmas.] + ntyp.SetUnderlying(g.typeExpr(decl.Type)) + + if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 { + methods := make([]*types.Field, otyp.NumMethods()) + for i := range methods { + m := otyp.Method(i) + meth := g.obj(m) + methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) + methods[i].Nname = meth + } + ntyp.Methods().Set(methods) + } + + out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name)) +} + +func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) { + pos := g.pos(decl) + names := make([]*ir.Name, len(decl.NameList)) + for i, name := range decl.NameList { + names[i], _ = g.def(name) + } + values := g.exprList(decl.Values) + + if decl.Pragma != nil { + pragma := decl.Pragma.(*pragmas) + if err := varEmbed(g.makeXPos, names[0], decl, pragma); err != nil { + base.ErrorfAt(g.pos(decl), "%s", err.Error()) + } + g.reportUnused(pragma) + } + + var as2 *ir.AssignListStmt + if len(values) != 0 && len(names) != len(values) { + as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values) + } + + for i, name := range names { + if ir.CurFunc != nil { + out.Append(ir.NewDecl(pos, ir.ODCL, name)) + } + if as2 != nil { + as2.Lhs[i] = name + name.Defn = as2 + } else { + as := ir.NewAssignStmt(pos, name, nil) + if len(values) != 0 { + as.Y = values[i] + name.Defn = as + } else if ir.CurFunc == nil { + name.Defn = as + } + out.Append(typecheck.Stmt(as)) + } + } + if as2 != nil { + out.Append(typecheck.Stmt(as2)) + } +} + +// pragmaFlags returns any specified pragma flags included in allowed, +// and reports errors about any other, unexpected pragmas. +func (g *irgen) pragmaFlags(pragma syntax.Pragma, allowed ir.PragmaFlag) ir.PragmaFlag { + if pragma == nil { + return 0 + } + p := pragma.(*pragmas) + present := p.Flag & allowed + p.Flag &^= allowed + g.reportUnused(p) + return present +} + +// reportUnused reports errors about any unused pragmas. +func (g *irgen) reportUnused(pragma *pragmas) { + for _, pos := range pragma.Pos { + if pos.Flag&pragma.Flag != 0 { + base.ErrorfAt(g.makeXPos(pos.Pos), "misplaced compiler directive") + } + } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + base.ErrorfAt(g.makeXPos(e.Pos), "misplaced go:embed directive") + } + } +} |