mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
User errors should be reported in noder and/or typecheck, we already know the -lang flag's value during noding, and checking it then works better for unified IR. The "multiple files for type" and "cannot apply to var of type" errors should also be moved to typecheck, but then they'd have to be duplicated for -G=3 mode (because it avoids typecheck). So those are left behind for now. Change-Id: I7caf16163c9faf975784acacdb8147514d2e698e Reviewed-on: https://go-review.googlesource.com/c/go/+/327609 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
1895 lines
49 KiB
Go
1895 lines
49 KiB
Go
// Copyright 2016 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 (
|
|
"errors"
|
|
"fmt"
|
|
"go/constant"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/dwarfgen"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/syntax"
|
|
"cmd/compile/internal/typecheck"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
func LoadPackage(filenames []string) {
|
|
base.Timer.Start("fe", "parse")
|
|
|
|
mode := syntax.CheckBranches
|
|
if base.Flag.G != 0 {
|
|
mode |= syntax.AllowGenerics
|
|
}
|
|
|
|
// Limit the number of simultaneously open files.
|
|
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
|
|
|
|
noders := make([]*noder, len(filenames))
|
|
for i, filename := range filenames {
|
|
p := noder{
|
|
err: make(chan syntax.Error),
|
|
trackScopes: base.Flag.Dwarf,
|
|
}
|
|
noders[i] = &p
|
|
|
|
filename := filename
|
|
go func() {
|
|
sem <- struct{}{}
|
|
defer func() { <-sem }()
|
|
defer close(p.err)
|
|
fbase := syntax.NewFileBase(filename)
|
|
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
p.error(syntax.Error{Msg: err.Error()})
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error
|
|
}()
|
|
}
|
|
|
|
var lines uint
|
|
for _, p := range noders {
|
|
for e := range p.err {
|
|
p.errorAt(e.Pos, "%s", e.Msg)
|
|
}
|
|
if p.file == nil {
|
|
base.ErrorExit()
|
|
}
|
|
lines += p.file.EOF.Line()
|
|
}
|
|
base.Timer.AddEvent(int64(lines), "lines")
|
|
|
|
if base.Flag.G != 0 {
|
|
// Use types2 to type-check and possibly generate IR.
|
|
check2(noders)
|
|
return
|
|
}
|
|
|
|
for _, p := range noders {
|
|
p.node()
|
|
p.file = nil // release memory
|
|
}
|
|
|
|
if base.SyntaxErrors() != 0 {
|
|
base.ErrorExit()
|
|
}
|
|
types.CheckDclstack()
|
|
|
|
for _, p := range noders {
|
|
p.processPragmas()
|
|
}
|
|
|
|
// Typecheck.
|
|
types.LocalPkg.Height = myheight
|
|
typecheck.DeclareUniverse()
|
|
typecheck.TypecheckAllowed = true
|
|
|
|
// Process top-level declarations in phases.
|
|
|
|
// Phase 1: const, type, and names and types of funcs.
|
|
// This will gather all the information about types
|
|
// and methods but doesn't depend on any of it.
|
|
//
|
|
// We also defer type alias declarations until phase 2
|
|
// to avoid cycles like #18640.
|
|
// TODO(gri) Remove this again once we have a fix for #25838.
|
|
//
|
|
// Phase 2: Variable assignments.
|
|
// To check interface assignments, depends on phase 1.
|
|
|
|
// Don't use range--typecheck can add closures to Target.Decls.
|
|
for phase, name := range []string{"top1", "top2"} {
|
|
base.Timer.Start("fe", "typecheck", name)
|
|
for i := 0; i < len(typecheck.Target.Decls); i++ {
|
|
n := typecheck.Target.Decls[i]
|
|
op := n.Op()
|
|
|
|
// Closure function declarations are typechecked as part of the
|
|
// closure expression.
|
|
if fn, ok := n.(*ir.Func); ok && fn.OClosure != nil {
|
|
continue
|
|
}
|
|
|
|
// We don't actually add ir.ODCL nodes to Target.Decls. Make sure of that.
|
|
if op == ir.ODCL {
|
|
base.FatalfAt(n.Pos(), "unexpected top declaration: %v", op)
|
|
}
|
|
|
|
// Identify declarations that should be deferred to the second
|
|
// iteration.
|
|
late := op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias()
|
|
|
|
if late == (phase == 1) {
|
|
typecheck.Target.Decls[i] = typecheck.Stmt(n)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase 3: Type check function bodies.
|
|
// Don't use range--typecheck can add closures to Target.Decls.
|
|
base.Timer.Start("fe", "typecheck", "func")
|
|
var fcount int64
|
|
for i := 0; i < len(typecheck.Target.Decls); i++ {
|
|
if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
|
|
if base.Flag.W > 1 {
|
|
s := fmt.Sprintf("\nbefore typecheck %v", fn)
|
|
ir.Dump(s, fn)
|
|
}
|
|
typecheck.FuncBody(fn)
|
|
if base.Flag.W > 1 {
|
|
s := fmt.Sprintf("\nafter typecheck %v", fn)
|
|
ir.Dump(s, fn)
|
|
}
|
|
fcount++
|
|
}
|
|
}
|
|
|
|
// Phase 4: Check external declarations.
|
|
// TODO(mdempsky): This should be handled when type checking their
|
|
// corresponding ODCL nodes.
|
|
base.Timer.Start("fe", "typecheck", "externdcls")
|
|
for i, n := range typecheck.Target.Externs {
|
|
if n.Op() == ir.ONAME {
|
|
typecheck.Target.Externs[i] = typecheck.Expr(typecheck.Target.Externs[i])
|
|
}
|
|
}
|
|
|
|
// Phase 5: With all user code type-checked, it's now safe to verify map keys.
|
|
// With all user code typechecked, it's now safe to verify unused dot imports.
|
|
typecheck.CheckMapKeys()
|
|
CheckDotImports()
|
|
base.ExitIfErrors()
|
|
}
|
|
|
|
func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) {
|
|
base.ErrorfAt(p.makeXPos(pos), format, args...)
|
|
}
|
|
|
|
// TODO(gri) Can we eliminate fileh in favor of absFilename?
|
|
func fileh(name string) string {
|
|
return objabi.AbsFile("", name, base.Flag.TrimPath)
|
|
}
|
|
|
|
func absFilename(name string) string {
|
|
return objabi.AbsFile(base.Ctxt.Pathname, name, base.Flag.TrimPath)
|
|
}
|
|
|
|
// noder transforms package syntax's AST into a Node tree.
|
|
type noder struct {
|
|
posMap
|
|
|
|
file *syntax.File
|
|
linknames []linkname
|
|
pragcgobuf [][]string
|
|
err chan syntax.Error
|
|
importedUnsafe bool
|
|
importedEmbed bool
|
|
trackScopes bool
|
|
|
|
funcState *funcState
|
|
}
|
|
|
|
// funcState tracks all per-function state to make handling nested
|
|
// functions easier.
|
|
type funcState struct {
|
|
// scopeVars is a stack tracking the number of variables declared in
|
|
// the current function at the moment each open scope was opened.
|
|
scopeVars []int
|
|
marker dwarfgen.ScopeMarker
|
|
|
|
lastCloseScopePos syntax.Pos
|
|
}
|
|
|
|
func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) {
|
|
outerFuncState := p.funcState
|
|
p.funcState = new(funcState)
|
|
typecheck.StartFuncBody(fn)
|
|
|
|
if block != nil {
|
|
body := p.stmts(block.List)
|
|
if body == nil {
|
|
body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)}
|
|
}
|
|
fn.Body = body
|
|
|
|
base.Pos = p.makeXPos(block.Rbrace)
|
|
fn.Endlineno = base.Pos
|
|
}
|
|
|
|
typecheck.FinishFuncBody()
|
|
p.funcState.marker.WriteTo(fn)
|
|
p.funcState = outerFuncState
|
|
}
|
|
|
|
func (p *noder) openScope(pos syntax.Pos) {
|
|
fs := p.funcState
|
|
types.Markdcl()
|
|
|
|
if p.trackScopes {
|
|
fs.scopeVars = append(fs.scopeVars, len(ir.CurFunc.Dcl))
|
|
fs.marker.Push(p.makeXPos(pos))
|
|
}
|
|
}
|
|
|
|
func (p *noder) closeScope(pos syntax.Pos) {
|
|
fs := p.funcState
|
|
fs.lastCloseScopePos = pos
|
|
types.Popdcl()
|
|
|
|
if p.trackScopes {
|
|
scopeVars := fs.scopeVars[len(fs.scopeVars)-1]
|
|
fs.scopeVars = fs.scopeVars[:len(fs.scopeVars)-1]
|
|
if scopeVars == len(ir.CurFunc.Dcl) {
|
|
// no variables were declared in this scope, so we can retract it.
|
|
fs.marker.Unpush()
|
|
} else {
|
|
fs.marker.Pop(p.makeXPos(pos))
|
|
}
|
|
}
|
|
}
|
|
|
|
// closeAnotherScope is like closeScope, but it reuses the same mark
|
|
// position as the last closeScope call. This is useful for "for" and
|
|
// "if" statements, as their implicit blocks always end at the same
|
|
// position as an explicit block.
|
|
func (p *noder) closeAnotherScope() {
|
|
p.closeScope(p.funcState.lastCloseScopePos)
|
|
}
|
|
|
|
// linkname records a //go:linkname directive.
|
|
type linkname struct {
|
|
pos syntax.Pos
|
|
local string
|
|
remote string
|
|
}
|
|
|
|
func (p *noder) node() {
|
|
p.importedUnsafe = false
|
|
p.importedEmbed = false
|
|
|
|
p.setlineno(p.file.PkgName)
|
|
mkpackage(p.file.PkgName.Value)
|
|
|
|
if pragma, ok := p.file.Pragma.(*pragmas); ok {
|
|
pragma.Flag &^= ir.GoBuildPragma
|
|
p.checkUnused(pragma)
|
|
}
|
|
|
|
typecheck.Target.Decls = append(typecheck.Target.Decls, p.decls(p.file.DeclList)...)
|
|
|
|
base.Pos = src.NoXPos
|
|
clearImports()
|
|
}
|
|
|
|
func (p *noder) processPragmas() {
|
|
for _, l := range p.linknames {
|
|
if !p.importedUnsafe {
|
|
p.errorAt(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
|
|
continue
|
|
}
|
|
n := ir.AsNode(typecheck.Lookup(l.local).Def)
|
|
if n == nil || n.Op() != ir.ONAME {
|
|
// TODO(mdempsky): Change to p.errorAt before Go 1.17 release.
|
|
// base.WarnfAt(p.makeXPos(l.pos), "//go:linkname must refer to declared function or variable (will be an error in Go 1.17)")
|
|
continue
|
|
}
|
|
if n.Sym().Linkname != "" {
|
|
p.errorAt(l.pos, "duplicate //go:linkname for %s", l.local)
|
|
continue
|
|
}
|
|
n.Sym().Linkname = l.remote
|
|
}
|
|
typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...)
|
|
}
|
|
|
|
func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) {
|
|
var cs constState
|
|
|
|
for _, decl := range decls {
|
|
p.setlineno(decl)
|
|
switch decl := decl.(type) {
|
|
case *syntax.ImportDecl:
|
|
p.importDecl(decl)
|
|
|
|
case *syntax.VarDecl:
|
|
l = append(l, p.varDecl(decl)...)
|
|
|
|
case *syntax.ConstDecl:
|
|
l = append(l, p.constDecl(decl, &cs)...)
|
|
|
|
case *syntax.TypeDecl:
|
|
l = append(l, p.typeDecl(decl))
|
|
|
|
case *syntax.FuncDecl:
|
|
l = append(l, p.funcDecl(decl))
|
|
|
|
default:
|
|
panic("unhandled Decl")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
|
if imp.Path == nil || imp.Path.Bad {
|
|
return // avoid follow-on errors if there was a syntax error
|
|
}
|
|
|
|
if pragma, ok := imp.Pragma.(*pragmas); ok {
|
|
p.checkUnused(pragma)
|
|
}
|
|
|
|
ipkg := importfile(imp)
|
|
if ipkg == nil {
|
|
if base.Errors() == 0 {
|
|
base.Fatalf("phase error in import")
|
|
}
|
|
return
|
|
}
|
|
|
|
if ipkg == ir.Pkgs.Unsafe {
|
|
p.importedUnsafe = true
|
|
}
|
|
if ipkg.Path == "embed" {
|
|
p.importedEmbed = true
|
|
}
|
|
|
|
var my *types.Sym
|
|
if imp.LocalPkgName != nil {
|
|
my = p.name(imp.LocalPkgName)
|
|
} else {
|
|
my = typecheck.Lookup(ipkg.Name)
|
|
}
|
|
|
|
pack := ir.NewPkgName(p.pos(imp), my, ipkg)
|
|
|
|
switch my.Name {
|
|
case ".":
|
|
importDot(pack)
|
|
return
|
|
case "init":
|
|
base.ErrorfAt(pack.Pos(), "cannot import package as init - init must be a func")
|
|
return
|
|
case "_":
|
|
return
|
|
}
|
|
if my.Def != nil {
|
|
typecheck.Redeclared(pack.Pos(), my, "as imported package name")
|
|
}
|
|
my.Def = pack
|
|
my.Lastlineno = pack.Pos()
|
|
my.Block = 1 // at top level
|
|
}
|
|
|
|
func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node {
|
|
names := p.declNames(ir.ONAME, decl.NameList)
|
|
typ := p.typeExprOrNil(decl.Type)
|
|
exprs := p.exprList(decl.Values)
|
|
|
|
if pragma, ok := decl.Pragma.(*pragmas); ok {
|
|
varEmbed(p.makeXPos, names[0], decl, pragma, p.importedEmbed)
|
|
p.checkUnused(pragma)
|
|
}
|
|
|
|
var init []ir.Node
|
|
p.setlineno(decl)
|
|
|
|
if len(names) > 1 && len(exprs) == 1 {
|
|
as2 := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, exprs)
|
|
for _, v := range names {
|
|
as2.Lhs.Append(v)
|
|
typecheck.Declare(v, typecheck.DeclContext)
|
|
v.Ntype = typ
|
|
v.Defn = as2
|
|
if ir.CurFunc != nil {
|
|
init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v))
|
|
}
|
|
}
|
|
|
|
return append(init, as2)
|
|
}
|
|
|
|
for i, v := range names {
|
|
var e ir.Node
|
|
if i < len(exprs) {
|
|
e = exprs[i]
|
|
}
|
|
|
|
typecheck.Declare(v, typecheck.DeclContext)
|
|
v.Ntype = typ
|
|
|
|
if ir.CurFunc != nil {
|
|
init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v))
|
|
}
|
|
as := ir.NewAssignStmt(base.Pos, v, e)
|
|
init = append(init, as)
|
|
if e != nil || ir.CurFunc == nil {
|
|
v.Defn = as
|
|
}
|
|
}
|
|
|
|
if len(exprs) != 0 && len(names) != len(exprs) {
|
|
base.Errorf("assignment mismatch: %d variables but %d values", len(names), len(exprs))
|
|
}
|
|
|
|
return init
|
|
}
|
|
|
|
// constState tracks state between constant specifiers within a
|
|
// declaration group. This state is kept separate from noder so nested
|
|
// constant declarations are handled correctly (e.g., issue 15550).
|
|
type constState struct {
|
|
group *syntax.Group
|
|
typ ir.Ntype
|
|
values syntax.Expr
|
|
iota int64
|
|
}
|
|
|
|
func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
|
|
if decl.Group == nil || decl.Group != cs.group {
|
|
*cs = constState{
|
|
group: decl.Group,
|
|
}
|
|
}
|
|
|
|
if pragma, ok := decl.Pragma.(*pragmas); ok {
|
|
p.checkUnused(pragma)
|
|
}
|
|
|
|
names := p.declNames(ir.OLITERAL, decl.NameList)
|
|
typ := p.typeExprOrNil(decl.Type)
|
|
|
|
if decl.Values != nil {
|
|
cs.typ, cs.values = typ, decl.Values
|
|
} else {
|
|
if typ != nil {
|
|
base.Errorf("const declaration cannot have type without expression")
|
|
}
|
|
typ = cs.typ
|
|
}
|
|
values := p.exprList(cs.values)
|
|
|
|
nn := make([]ir.Node, 0, len(names))
|
|
for i, n := range names {
|
|
if i >= len(values) {
|
|
base.Errorf("missing value in const declaration")
|
|
break
|
|
}
|
|
|
|
v := values[i]
|
|
if decl.Values == nil {
|
|
ir.Visit(v, func(v ir.Node) {
|
|
if ir.HasUniquePos(v) {
|
|
v.SetPos(n.Pos())
|
|
}
|
|
})
|
|
}
|
|
|
|
typecheck.Declare(n, typecheck.DeclContext)
|
|
|
|
n.Ntype = typ
|
|
n.Defn = v
|
|
n.SetIota(cs.iota)
|
|
|
|
nn = append(nn, ir.NewDecl(p.pos(decl), ir.ODCLCONST, n))
|
|
}
|
|
|
|
if len(values) > len(names) {
|
|
base.Errorf("extra expression in const declaration")
|
|
}
|
|
|
|
cs.iota++
|
|
|
|
return nn
|
|
}
|
|
|
|
func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node {
|
|
n := p.declName(ir.OTYPE, decl.Name)
|
|
typecheck.Declare(n, typecheck.DeclContext)
|
|
|
|
// decl.Type may be nil but in that case we got a syntax error during parsing
|
|
typ := p.typeExprOrNil(decl.Type)
|
|
|
|
n.Ntype = typ
|
|
n.SetAlias(decl.Alias)
|
|
if pragma, ok := decl.Pragma.(*pragmas); ok {
|
|
if !decl.Alias {
|
|
n.SetPragma(pragma.Flag & typePragmas)
|
|
pragma.Flag &^= typePragmas
|
|
}
|
|
p.checkUnused(pragma)
|
|
}
|
|
|
|
nod := ir.NewDecl(p.pos(decl), ir.ODCLTYPE, n)
|
|
if n.Alias() && !types.AllowsGoVersion(types.LocalPkg, 1, 9) {
|
|
base.ErrorfAt(nod.Pos(), "type aliases only supported as of -lang=go1.9")
|
|
}
|
|
return nod
|
|
}
|
|
|
|
func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name {
|
|
nodes := make([]*ir.Name, 0, len(names))
|
|
for _, name := range names {
|
|
nodes = append(nodes, p.declName(op, name))
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name {
|
|
return ir.NewDeclNameAt(p.pos(name), op, p.name(name))
|
|
}
|
|
|
|
func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node {
|
|
name := p.name(fun.Name)
|
|
t := p.signature(fun.Recv, fun.Type)
|
|
f := ir.NewFunc(p.pos(fun))
|
|
|
|
if fun.Recv == nil {
|
|
if name.Name == "init" {
|
|
name = renameinit()
|
|
if len(t.Params) > 0 || len(t.Results) > 0 {
|
|
base.ErrorfAt(f.Pos(), "func init must have no arguments and no return values")
|
|
}
|
|
typecheck.Target.Inits = append(typecheck.Target.Inits, f)
|
|
}
|
|
|
|
if types.LocalPkg.Name == "main" && name.Name == "main" {
|
|
if len(t.Params) > 0 || len(t.Results) > 0 {
|
|
base.ErrorfAt(f.Pos(), "func main must have no arguments and no return values")
|
|
}
|
|
}
|
|
} else {
|
|
f.Shortname = name
|
|
name = ir.BlankNode.Sym() // filled in by tcFunc
|
|
}
|
|
|
|
f.Nname = ir.NewNameAt(p.pos(fun.Name), name)
|
|
f.Nname.Func = f
|
|
f.Nname.Defn = f
|
|
f.Nname.Ntype = t
|
|
|
|
if pragma, ok := fun.Pragma.(*pragmas); ok {
|
|
f.Pragma = pragma.Flag & funcPragmas
|
|
if pragma.Flag&ir.Systemstack != 0 && pragma.Flag&ir.Nosplit != 0 {
|
|
base.ErrorfAt(f.Pos(), "go:nosplit and go:systemstack cannot be combined")
|
|
}
|
|
pragma.Flag &^= funcPragmas
|
|
p.checkUnused(pragma)
|
|
}
|
|
|
|
if fun.Recv == nil {
|
|
typecheck.Declare(f.Nname, ir.PFUNC)
|
|
}
|
|
|
|
p.funcBody(f, fun.Body)
|
|
|
|
if fun.Body != nil {
|
|
if f.Pragma&ir.Noescape != 0 {
|
|
base.ErrorfAt(f.Pos(), "can only use //go:noescape with external func implementations")
|
|
}
|
|
} else {
|
|
if base.Flag.Complete || strings.HasPrefix(ir.FuncName(f), "init.") {
|
|
// Linknamed functions are allowed to have no body. Hopefully
|
|
// the linkname target has a body. See issue 23311.
|
|
isLinknamed := false
|
|
for _, n := range p.linknames {
|
|
if ir.FuncName(f) == n.local {
|
|
isLinknamed = true
|
|
break
|
|
}
|
|
}
|
|
if !isLinknamed {
|
|
base.ErrorfAt(f.Pos(), "missing function body")
|
|
}
|
|
}
|
|
}
|
|
|
|
return f
|
|
}
|
|
|
|
func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *ir.FuncType {
|
|
var rcvr *ir.Field
|
|
if recv != nil {
|
|
rcvr = p.param(recv, false, false)
|
|
}
|
|
return ir.NewFuncType(p.pos(typ), rcvr,
|
|
p.params(typ.ParamList, true),
|
|
p.params(typ.ResultList, false))
|
|
}
|
|
|
|
func (p *noder) params(params []*syntax.Field, dddOk bool) []*ir.Field {
|
|
nodes := make([]*ir.Field, 0, len(params))
|
|
for i, param := range params {
|
|
p.setlineno(param)
|
|
nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
|
|
if i > 0 && params[i].Type == params[i-1].Type {
|
|
nodes[i].Ntype = nodes[i-1].Ntype
|
|
}
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) param(param *syntax.Field, dddOk, final bool) *ir.Field {
|
|
var name *types.Sym
|
|
if param.Name != nil {
|
|
name = p.name(param.Name)
|
|
}
|
|
|
|
typ := p.typeExpr(param.Type)
|
|
n := ir.NewField(p.pos(param), name, typ, nil)
|
|
|
|
// rewrite ...T parameter
|
|
if typ, ok := typ.(*ir.SliceType); ok && typ.DDD {
|
|
if !dddOk {
|
|
// We mark these as syntax errors to get automatic elimination
|
|
// of multiple such errors per line (see ErrorfAt in subr.go).
|
|
base.Errorf("syntax error: cannot use ... in receiver or result parameter list")
|
|
} else if !final {
|
|
if param.Name == nil {
|
|
base.Errorf("syntax error: cannot use ... with non-final parameter")
|
|
} else {
|
|
p.errorAt(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value)
|
|
}
|
|
}
|
|
typ.DDD = false
|
|
n.IsDDD = true
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
func (p *noder) exprList(expr syntax.Expr) []ir.Node {
|
|
switch expr := expr.(type) {
|
|
case nil:
|
|
return nil
|
|
case *syntax.ListExpr:
|
|
return p.exprs(expr.ElemList)
|
|
default:
|
|
return []ir.Node{p.expr(expr)}
|
|
}
|
|
}
|
|
|
|
func (p *noder) exprs(exprs []syntax.Expr) []ir.Node {
|
|
nodes := make([]ir.Node, 0, len(exprs))
|
|
for _, expr := range exprs {
|
|
nodes = append(nodes, p.expr(expr))
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) expr(expr syntax.Expr) ir.Node {
|
|
p.setlineno(expr)
|
|
switch expr := expr.(type) {
|
|
case nil, *syntax.BadExpr:
|
|
return nil
|
|
case *syntax.Name:
|
|
return p.mkname(expr)
|
|
case *syntax.BasicLit:
|
|
n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr))
|
|
if expr.Kind == syntax.RuneLit {
|
|
n.SetType(types.UntypedRune)
|
|
}
|
|
n.SetDiag(expr.Bad || n.Val().Kind() == constant.Unknown) // avoid follow-on errors if there was a syntax error
|
|
return n
|
|
case *syntax.CompositeLit:
|
|
n := ir.NewCompLitExpr(p.pos(expr), ir.OCOMPLIT, p.typeExpr(expr.Type), nil)
|
|
l := p.exprs(expr.ElemList)
|
|
for i, e := range l {
|
|
l[i] = p.wrapname(expr.ElemList[i], e)
|
|
}
|
|
n.List = l
|
|
base.Pos = p.makeXPos(expr.Rbrace)
|
|
return n
|
|
case *syntax.KeyValueExpr:
|
|
// use position of expr.Key rather than of expr (which has position of ':')
|
|
return ir.NewKeyExpr(p.pos(expr.Key), p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
|
|
case *syntax.FuncLit:
|
|
return p.funcLit(expr)
|
|
case *syntax.ParenExpr:
|
|
return ir.NewParenExpr(p.pos(expr), p.expr(expr.X))
|
|
case *syntax.SelectorExpr:
|
|
// parser.new_dotname
|
|
obj := p.expr(expr.X)
|
|
if obj.Op() == ir.OPACK {
|
|
pack := obj.(*ir.PkgName)
|
|
pack.Used = true
|
|
return importName(pack.Pkg.Lookup(expr.Sel.Value))
|
|
}
|
|
n := ir.NewSelectorExpr(base.Pos, ir.OXDOT, obj, p.name(expr.Sel))
|
|
n.SetPos(p.pos(expr)) // lineno may have been changed by p.expr(expr.X)
|
|
return n
|
|
case *syntax.IndexExpr:
|
|
return ir.NewIndexExpr(p.pos(expr), p.expr(expr.X), p.expr(expr.Index))
|
|
case *syntax.SliceExpr:
|
|
op := ir.OSLICE
|
|
if expr.Full {
|
|
op = ir.OSLICE3
|
|
}
|
|
x := p.expr(expr.X)
|
|
var index [3]ir.Node
|
|
for i, n := range &expr.Index {
|
|
if n != nil {
|
|
index[i] = p.expr(n)
|
|
}
|
|
}
|
|
return ir.NewSliceExpr(p.pos(expr), op, x, index[0], index[1], index[2])
|
|
case *syntax.AssertExpr:
|
|
return ir.NewTypeAssertExpr(p.pos(expr), p.expr(expr.X), p.typeExpr(expr.Type))
|
|
case *syntax.Operation:
|
|
if expr.Op == syntax.Add && expr.Y != nil {
|
|
return p.sum(expr)
|
|
}
|
|
x := p.expr(expr.X)
|
|
if expr.Y == nil {
|
|
pos, op := p.pos(expr), p.unOp(expr.Op)
|
|
switch op {
|
|
case ir.OADDR:
|
|
return typecheck.NodAddrAt(pos, x)
|
|
case ir.ODEREF:
|
|
return ir.NewStarExpr(pos, x)
|
|
}
|
|
return ir.NewUnaryExpr(pos, op, x)
|
|
}
|
|
|
|
pos, op, y := p.pos(expr), p.binOp(expr.Op), p.expr(expr.Y)
|
|
switch op {
|
|
case ir.OANDAND, ir.OOROR:
|
|
return ir.NewLogicalExpr(pos, op, x, y)
|
|
}
|
|
return ir.NewBinaryExpr(pos, op, x, y)
|
|
case *syntax.CallExpr:
|
|
n := ir.NewCallExpr(p.pos(expr), ir.OCALL, p.expr(expr.Fun), p.exprs(expr.ArgList))
|
|
n.IsDDD = expr.HasDots
|
|
return n
|
|
|
|
case *syntax.ArrayType:
|
|
var len ir.Node
|
|
if expr.Len != nil {
|
|
len = p.expr(expr.Len)
|
|
}
|
|
return ir.NewArrayType(p.pos(expr), len, p.typeExpr(expr.Elem))
|
|
case *syntax.SliceType:
|
|
return ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem))
|
|
case *syntax.DotsType:
|
|
t := ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem))
|
|
t.DDD = true
|
|
return t
|
|
case *syntax.StructType:
|
|
return p.structType(expr)
|
|
case *syntax.InterfaceType:
|
|
return p.interfaceType(expr)
|
|
case *syntax.FuncType:
|
|
return p.signature(nil, expr)
|
|
case *syntax.MapType:
|
|
return ir.NewMapType(p.pos(expr),
|
|
p.typeExpr(expr.Key), p.typeExpr(expr.Value))
|
|
case *syntax.ChanType:
|
|
return ir.NewChanType(p.pos(expr),
|
|
p.typeExpr(expr.Elem), p.chanDir(expr.Dir))
|
|
|
|
case *syntax.TypeSwitchGuard:
|
|
var tag *ir.Ident
|
|
if expr.Lhs != nil {
|
|
tag = ir.NewIdent(p.pos(expr.Lhs), p.name(expr.Lhs))
|
|
if ir.IsBlank(tag) {
|
|
base.Errorf("invalid variable name %v in type switch", tag)
|
|
}
|
|
}
|
|
return ir.NewTypeSwitchGuard(p.pos(expr), tag, p.expr(expr.X))
|
|
}
|
|
panic("unhandled Expr")
|
|
}
|
|
|
|
// sum efficiently handles very large summation expressions (such as
|
|
// in issue #16394). In particular, it avoids left recursion and
|
|
// collapses string literals.
|
|
func (p *noder) sum(x syntax.Expr) ir.Node {
|
|
// While we need to handle long sums with asymptotic
|
|
// efficiency, the vast majority of sums are very small: ~95%
|
|
// have only 2 or 3 operands, and ~99% of string literals are
|
|
// never concatenated.
|
|
|
|
adds := make([]*syntax.Operation, 0, 2)
|
|
for {
|
|
add, ok := x.(*syntax.Operation)
|
|
if !ok || add.Op != syntax.Add || add.Y == nil {
|
|
break
|
|
}
|
|
adds = append(adds, add)
|
|
x = add.X
|
|
}
|
|
|
|
// nstr is the current rightmost string literal in the
|
|
// summation (if any), and chunks holds its accumulated
|
|
// substrings.
|
|
//
|
|
// Consider the expression x + "a" + "b" + "c" + y. When we
|
|
// reach the string literal "a", we assign nstr to point to
|
|
// its corresponding Node and initialize chunks to {"a"}.
|
|
// Visiting the subsequent string literals "b" and "c", we
|
|
// simply append their values to chunks. Finally, when we
|
|
// reach the non-constant operand y, we'll join chunks to form
|
|
// "abc" and reassign the "a" string literal's value.
|
|
//
|
|
// N.B., we need to be careful about named string constants
|
|
// (indicated by Sym != nil) because 1) we can't modify their
|
|
// value, as doing so would affect other uses of the string
|
|
// constant, and 2) they may have types, which we need to
|
|
// handle correctly. For now, we avoid these problems by
|
|
// treating named string constants the same as non-constant
|
|
// operands.
|
|
var nstr ir.Node
|
|
chunks := make([]string, 0, 1)
|
|
|
|
n := p.expr(x)
|
|
if ir.IsConst(n, constant.String) && n.Sym() == nil {
|
|
nstr = n
|
|
chunks = append(chunks, ir.StringVal(nstr))
|
|
}
|
|
|
|
for i := len(adds) - 1; i >= 0; i-- {
|
|
add := adds[i]
|
|
|
|
r := p.expr(add.Y)
|
|
if ir.IsConst(r, constant.String) && r.Sym() == nil {
|
|
if nstr != nil {
|
|
// Collapse r into nstr instead of adding to n.
|
|
chunks = append(chunks, ir.StringVal(r))
|
|
continue
|
|
}
|
|
|
|
nstr = r
|
|
chunks = append(chunks, ir.StringVal(nstr))
|
|
} else {
|
|
if len(chunks) > 1 {
|
|
nstr.SetVal(constant.MakeString(strings.Join(chunks, "")))
|
|
}
|
|
nstr = nil
|
|
chunks = chunks[:0]
|
|
}
|
|
n = ir.NewBinaryExpr(p.pos(add), ir.OADD, n, r)
|
|
}
|
|
if len(chunks) > 1 {
|
|
nstr.SetVal(constant.MakeString(strings.Join(chunks, "")))
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype {
|
|
// TODO(mdempsky): Be stricter? typecheck should handle errors anyway.
|
|
n := p.expr(typ)
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
return n.(ir.Ntype)
|
|
}
|
|
|
|
func (p *noder) typeExprOrNil(typ syntax.Expr) ir.Ntype {
|
|
if typ != nil {
|
|
return p.typeExpr(typ)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *noder) chanDir(dir syntax.ChanDir) types.ChanDir {
|
|
switch dir {
|
|
case 0:
|
|
return types.Cboth
|
|
case syntax.SendOnly:
|
|
return types.Csend
|
|
case syntax.RecvOnly:
|
|
return types.Crecv
|
|
}
|
|
panic("unhandled ChanDir")
|
|
}
|
|
|
|
func (p *noder) structType(expr *syntax.StructType) ir.Node {
|
|
l := make([]*ir.Field, 0, len(expr.FieldList))
|
|
for i, field := range expr.FieldList {
|
|
p.setlineno(field)
|
|
var n *ir.Field
|
|
if field.Name == nil {
|
|
n = p.embedded(field.Type)
|
|
} else {
|
|
n = ir.NewField(p.pos(field), p.name(field.Name), p.typeExpr(field.Type), nil)
|
|
}
|
|
if i > 0 && expr.FieldList[i].Type == expr.FieldList[i-1].Type {
|
|
n.Ntype = l[i-1].Ntype
|
|
}
|
|
if i < len(expr.TagList) && expr.TagList[i] != nil {
|
|
n.Note = constant.StringVal(p.basicLit(expr.TagList[i]))
|
|
}
|
|
l = append(l, n)
|
|
}
|
|
|
|
p.setlineno(expr)
|
|
return ir.NewStructType(p.pos(expr), l)
|
|
}
|
|
|
|
func (p *noder) interfaceType(expr *syntax.InterfaceType) ir.Node {
|
|
l := make([]*ir.Field, 0, len(expr.MethodList))
|
|
for _, method := range expr.MethodList {
|
|
p.setlineno(method)
|
|
var n *ir.Field
|
|
if method.Name == nil {
|
|
n = ir.NewField(p.pos(method), nil, importName(p.packname(method.Type)).(ir.Ntype), nil)
|
|
} else {
|
|
mname := p.name(method.Name)
|
|
if mname.IsBlank() {
|
|
base.Errorf("methods must have a unique non-blank name")
|
|
continue
|
|
}
|
|
sig := p.typeExpr(method.Type).(*ir.FuncType)
|
|
sig.Recv = fakeRecv()
|
|
n = ir.NewField(p.pos(method), mname, sig, nil)
|
|
}
|
|
l = append(l, n)
|
|
}
|
|
|
|
return ir.NewInterfaceType(p.pos(expr), l)
|
|
}
|
|
|
|
func (p *noder) packname(expr syntax.Expr) *types.Sym {
|
|
switch expr := expr.(type) {
|
|
case *syntax.Name:
|
|
name := p.name(expr)
|
|
if n := oldname(name); n.Name() != nil && n.Name().PkgName != nil {
|
|
n.Name().PkgName.Used = true
|
|
}
|
|
return name
|
|
case *syntax.SelectorExpr:
|
|
name := p.name(expr.X.(*syntax.Name))
|
|
def := ir.AsNode(name.Def)
|
|
if def == nil {
|
|
base.Errorf("undefined: %v", name)
|
|
return name
|
|
}
|
|
var pkg *types.Pkg
|
|
if def.Op() != ir.OPACK {
|
|
base.Errorf("%v is not a package", name)
|
|
pkg = types.LocalPkg
|
|
} else {
|
|
def := def.(*ir.PkgName)
|
|
def.Used = true
|
|
pkg = def.Pkg
|
|
}
|
|
return pkg.Lookup(expr.Sel.Value)
|
|
}
|
|
panic(fmt.Sprintf("unexpected packname: %#v", expr))
|
|
}
|
|
|
|
func (p *noder) embedded(typ syntax.Expr) *ir.Field {
|
|
pos := p.pos(syntax.StartPos(typ))
|
|
|
|
op, isStar := typ.(*syntax.Operation)
|
|
if isStar {
|
|
if op.Op != syntax.Mul || op.Y != nil {
|
|
panic("unexpected Operation")
|
|
}
|
|
typ = op.X
|
|
}
|
|
|
|
sym := p.packname(typ)
|
|
n := ir.NewField(pos, typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil)
|
|
n.Embedded = true
|
|
|
|
if isStar {
|
|
n.Ntype = ir.NewStarExpr(pos, n.Ntype)
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (p *noder) stmts(stmts []syntax.Stmt) []ir.Node {
|
|
return p.stmtsFall(stmts, false)
|
|
}
|
|
|
|
func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []ir.Node {
|
|
var nodes []ir.Node
|
|
for i, stmt := range stmts {
|
|
s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
|
|
if s == nil {
|
|
} else if s.Op() == ir.OBLOCK && len(s.(*ir.BlockStmt).List) > 0 {
|
|
// Inline non-empty block.
|
|
// Empty blocks must be preserved for CheckReturn.
|
|
nodes = append(nodes, s.(*ir.BlockStmt).List...)
|
|
} else {
|
|
nodes = append(nodes, s)
|
|
}
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) stmt(stmt syntax.Stmt) ir.Node {
|
|
return p.stmtFall(stmt, false)
|
|
}
|
|
|
|
func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node {
|
|
p.setlineno(stmt)
|
|
switch stmt := stmt.(type) {
|
|
case nil, *syntax.EmptyStmt:
|
|
return nil
|
|
case *syntax.LabeledStmt:
|
|
return p.labeledStmt(stmt, fallOK)
|
|
case *syntax.BlockStmt:
|
|
l := p.blockStmt(stmt)
|
|
if len(l) == 0 {
|
|
// TODO(mdempsky): Line number?
|
|
return ir.NewBlockStmt(base.Pos, nil)
|
|
}
|
|
return ir.NewBlockStmt(src.NoXPos, l)
|
|
case *syntax.ExprStmt:
|
|
return p.wrapname(stmt, p.expr(stmt.X))
|
|
case *syntax.SendStmt:
|
|
return ir.NewSendStmt(p.pos(stmt), p.expr(stmt.Chan), p.expr(stmt.Value))
|
|
case *syntax.DeclStmt:
|
|
return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList))
|
|
case *syntax.AssignStmt:
|
|
if stmt.Rhs == nil {
|
|
pos := p.pos(stmt)
|
|
n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one))
|
|
n.IncDec = true
|
|
return n
|
|
}
|
|
|
|
if stmt.Op != 0 && stmt.Op != syntax.Def {
|
|
n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs))
|
|
return n
|
|
}
|
|
|
|
rhs := p.exprList(stmt.Rhs)
|
|
if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 {
|
|
n := ir.NewAssignListStmt(p.pos(stmt), ir.OAS2, nil, nil)
|
|
n.Def = stmt.Op == syntax.Def
|
|
n.Lhs = p.assignList(stmt.Lhs, n, n.Def)
|
|
n.Rhs = rhs
|
|
return n
|
|
}
|
|
|
|
n := ir.NewAssignStmt(p.pos(stmt), nil, nil)
|
|
n.Def = stmt.Op == syntax.Def
|
|
n.X = p.assignList(stmt.Lhs, n, n.Def)[0]
|
|
n.Y = rhs[0]
|
|
return n
|
|
|
|
case *syntax.BranchStmt:
|
|
var op ir.Op
|
|
switch stmt.Tok {
|
|
case syntax.Break:
|
|
op = ir.OBREAK
|
|
case syntax.Continue:
|
|
op = ir.OCONTINUE
|
|
case syntax.Fallthrough:
|
|
if !fallOK {
|
|
base.Errorf("fallthrough statement out of place")
|
|
}
|
|
op = ir.OFALL
|
|
case syntax.Goto:
|
|
op = ir.OGOTO
|
|
default:
|
|
panic("unhandled BranchStmt")
|
|
}
|
|
var sym *types.Sym
|
|
if stmt.Label != nil {
|
|
sym = p.name(stmt.Label)
|
|
}
|
|
return ir.NewBranchStmt(p.pos(stmt), op, sym)
|
|
case *syntax.CallStmt:
|
|
var op ir.Op
|
|
switch stmt.Tok {
|
|
case syntax.Defer:
|
|
op = ir.ODEFER
|
|
case syntax.Go:
|
|
op = ir.OGO
|
|
default:
|
|
panic("unhandled CallStmt")
|
|
}
|
|
return ir.NewGoDeferStmt(p.pos(stmt), op, p.expr(stmt.Call))
|
|
case *syntax.ReturnStmt:
|
|
n := ir.NewReturnStmt(p.pos(stmt), p.exprList(stmt.Results))
|
|
if len(n.Results) == 0 && ir.CurFunc != nil {
|
|
for _, ln := range ir.CurFunc.Dcl {
|
|
if ln.Class == ir.PPARAM {
|
|
continue
|
|
}
|
|
if ln.Class != ir.PPARAMOUT {
|
|
break
|
|
}
|
|
if ln.Sym().Def != ln {
|
|
base.Errorf("%s is shadowed during return", ln.Sym().Name)
|
|
}
|
|
}
|
|
}
|
|
return n
|
|
case *syntax.IfStmt:
|
|
return p.ifStmt(stmt)
|
|
case *syntax.ForStmt:
|
|
return p.forStmt(stmt)
|
|
case *syntax.SwitchStmt:
|
|
return p.switchStmt(stmt)
|
|
case *syntax.SelectStmt:
|
|
return p.selectStmt(stmt)
|
|
}
|
|
panic("unhandled Stmt")
|
|
}
|
|
|
|
func (p *noder) assignList(expr syntax.Expr, defn ir.InitNode, colas bool) []ir.Node {
|
|
if !colas {
|
|
return p.exprList(expr)
|
|
}
|
|
|
|
var exprs []syntax.Expr
|
|
if list, ok := expr.(*syntax.ListExpr); ok {
|
|
exprs = list.ElemList
|
|
} else {
|
|
exprs = []syntax.Expr{expr}
|
|
}
|
|
|
|
res := make([]ir.Node, len(exprs))
|
|
seen := make(map[*types.Sym]bool, len(exprs))
|
|
|
|
newOrErr := false
|
|
for i, expr := range exprs {
|
|
p.setlineno(expr)
|
|
res[i] = ir.BlankNode
|
|
|
|
name, ok := expr.(*syntax.Name)
|
|
if !ok {
|
|
p.errorAt(expr.Pos(), "non-name %v on left side of :=", p.expr(expr))
|
|
newOrErr = true
|
|
continue
|
|
}
|
|
|
|
sym := p.name(name)
|
|
if sym.IsBlank() {
|
|
continue
|
|
}
|
|
|
|
if seen[sym] {
|
|
p.errorAt(expr.Pos(), "%v repeated on left side of :=", sym)
|
|
newOrErr = true
|
|
continue
|
|
}
|
|
seen[sym] = true
|
|
|
|
if sym.Block == types.Block {
|
|
res[i] = oldname(sym)
|
|
continue
|
|
}
|
|
|
|
newOrErr = true
|
|
n := typecheck.NewName(sym)
|
|
typecheck.Declare(n, typecheck.DeclContext)
|
|
n.Defn = defn
|
|
defn.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n))
|
|
res[i] = n
|
|
}
|
|
|
|
if !newOrErr {
|
|
base.ErrorfAt(defn.Pos(), "no new variables on left side of :=")
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (p *noder) blockStmt(stmt *syntax.BlockStmt) []ir.Node {
|
|
p.openScope(stmt.Pos())
|
|
nodes := p.stmts(stmt.List)
|
|
p.closeScope(stmt.Rbrace)
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node {
|
|
p.openScope(stmt.Pos())
|
|
init := p.stmt(stmt.Init)
|
|
n := ir.NewIfStmt(p.pos(stmt), p.expr(stmt.Cond), p.blockStmt(stmt.Then), nil)
|
|
if init != nil {
|
|
*n.PtrInit() = []ir.Node{init}
|
|
}
|
|
if stmt.Else != nil {
|
|
e := p.stmt(stmt.Else)
|
|
if e.Op() == ir.OBLOCK {
|
|
e := e.(*ir.BlockStmt)
|
|
n.Else = e.List
|
|
} else {
|
|
n.Else = []ir.Node{e}
|
|
}
|
|
}
|
|
p.closeAnotherScope()
|
|
return n
|
|
}
|
|
|
|
func (p *noder) forStmt(stmt *syntax.ForStmt) ir.Node {
|
|
p.openScope(stmt.Pos())
|
|
if r, ok := stmt.Init.(*syntax.RangeClause); ok {
|
|
if stmt.Cond != nil || stmt.Post != nil {
|
|
panic("unexpected RangeClause")
|
|
}
|
|
|
|
n := ir.NewRangeStmt(p.pos(r), nil, nil, p.expr(r.X), nil)
|
|
if r.Lhs != nil {
|
|
n.Def = r.Def
|
|
lhs := p.assignList(r.Lhs, n, n.Def)
|
|
n.Key = lhs[0]
|
|
if len(lhs) > 1 {
|
|
n.Value = lhs[1]
|
|
}
|
|
}
|
|
n.Body = p.blockStmt(stmt.Body)
|
|
p.closeAnotherScope()
|
|
return n
|
|
}
|
|
|
|
n := ir.NewForStmt(p.pos(stmt), p.stmt(stmt.Init), p.expr(stmt.Cond), p.stmt(stmt.Post), p.blockStmt(stmt.Body))
|
|
p.closeAnotherScope()
|
|
return n
|
|
}
|
|
|
|
func (p *noder) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
|
|
p.openScope(stmt.Pos())
|
|
|
|
init := p.stmt(stmt.Init)
|
|
n := ir.NewSwitchStmt(p.pos(stmt), p.expr(stmt.Tag), nil)
|
|
if init != nil {
|
|
*n.PtrInit() = []ir.Node{init}
|
|
}
|
|
|
|
var tswitch *ir.TypeSwitchGuard
|
|
if l := n.Tag; l != nil && l.Op() == ir.OTYPESW {
|
|
tswitch = l.(*ir.TypeSwitchGuard)
|
|
}
|
|
n.Cases = p.caseClauses(stmt.Body, tswitch, stmt.Rbrace)
|
|
|
|
p.closeScope(stmt.Rbrace)
|
|
return n
|
|
}
|
|
|
|
func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *ir.TypeSwitchGuard, rbrace syntax.Pos) []*ir.CaseClause {
|
|
nodes := make([]*ir.CaseClause, 0, len(clauses))
|
|
for i, clause := range clauses {
|
|
p.setlineno(clause)
|
|
if i > 0 {
|
|
p.closeScope(clause.Pos())
|
|
}
|
|
p.openScope(clause.Pos())
|
|
|
|
n := ir.NewCaseStmt(p.pos(clause), p.exprList(clause.Cases), nil)
|
|
if tswitch != nil && tswitch.Tag != nil {
|
|
nn := typecheck.NewName(tswitch.Tag.Sym())
|
|
typecheck.Declare(nn, typecheck.DeclContext)
|
|
n.Var = nn
|
|
// keep track of the instances for reporting unused
|
|
nn.Defn = tswitch
|
|
}
|
|
|
|
// Trim trailing empty statements. We omit them from
|
|
// the Node AST anyway, and it's easier to identify
|
|
// out-of-place fallthrough statements without them.
|
|
body := clause.Body
|
|
for len(body) > 0 {
|
|
if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok {
|
|
break
|
|
}
|
|
body = body[:len(body)-1]
|
|
}
|
|
|
|
n.Body = p.stmtsFall(body, true)
|
|
if l := len(n.Body); l > 0 && n.Body[l-1].Op() == ir.OFALL {
|
|
if tswitch != nil {
|
|
base.Errorf("cannot fallthrough in type switch")
|
|
}
|
|
if i+1 == len(clauses) {
|
|
base.Errorf("cannot fallthrough final case in switch")
|
|
}
|
|
}
|
|
|
|
nodes = append(nodes, n)
|
|
}
|
|
if len(clauses) > 0 {
|
|
p.closeScope(rbrace)
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) selectStmt(stmt *syntax.SelectStmt) ir.Node {
|
|
return ir.NewSelectStmt(p.pos(stmt), p.commClauses(stmt.Body, stmt.Rbrace))
|
|
}
|
|
|
|
func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*ir.CommClause {
|
|
nodes := make([]*ir.CommClause, len(clauses))
|
|
for i, clause := range clauses {
|
|
p.setlineno(clause)
|
|
if i > 0 {
|
|
p.closeScope(clause.Pos())
|
|
}
|
|
p.openScope(clause.Pos())
|
|
|
|
nodes[i] = ir.NewCommStmt(p.pos(clause), p.stmt(clause.Comm), p.stmts(clause.Body))
|
|
}
|
|
if len(clauses) > 0 {
|
|
p.closeScope(rbrace)
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node {
|
|
sym := p.name(label.Label)
|
|
lhs := ir.NewLabelStmt(p.pos(label), sym)
|
|
|
|
var ls ir.Node
|
|
if label.Stmt != nil { // TODO(mdempsky): Should always be present.
|
|
ls = p.stmtFall(label.Stmt, fallOK)
|
|
// Attach label directly to control statement too.
|
|
if ls != nil {
|
|
switch ls.Op() {
|
|
case ir.OFOR:
|
|
ls := ls.(*ir.ForStmt)
|
|
ls.Label = sym
|
|
case ir.ORANGE:
|
|
ls := ls.(*ir.RangeStmt)
|
|
ls.Label = sym
|
|
case ir.OSWITCH:
|
|
ls := ls.(*ir.SwitchStmt)
|
|
ls.Label = sym
|
|
case ir.OSELECT:
|
|
ls := ls.(*ir.SelectStmt)
|
|
ls.Label = sym
|
|
}
|
|
}
|
|
}
|
|
|
|
l := []ir.Node{lhs}
|
|
if ls != nil {
|
|
if ls.Op() == ir.OBLOCK {
|
|
ls := ls.(*ir.BlockStmt)
|
|
l = append(l, ls.List...)
|
|
} else {
|
|
l = append(l, ls)
|
|
}
|
|
}
|
|
return ir.NewBlockStmt(src.NoXPos, l)
|
|
}
|
|
|
|
var unOps = [...]ir.Op{
|
|
syntax.Recv: ir.ORECV,
|
|
syntax.Mul: ir.ODEREF,
|
|
syntax.And: ir.OADDR,
|
|
|
|
syntax.Not: ir.ONOT,
|
|
syntax.Xor: ir.OBITNOT,
|
|
syntax.Add: ir.OPLUS,
|
|
syntax.Sub: ir.ONEG,
|
|
}
|
|
|
|
func (p *noder) unOp(op syntax.Operator) ir.Op {
|
|
if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 {
|
|
panic("invalid Operator")
|
|
}
|
|
return unOps[op]
|
|
}
|
|
|
|
var binOps = [...]ir.Op{
|
|
syntax.OrOr: ir.OOROR,
|
|
syntax.AndAnd: ir.OANDAND,
|
|
|
|
syntax.Eql: ir.OEQ,
|
|
syntax.Neq: ir.ONE,
|
|
syntax.Lss: ir.OLT,
|
|
syntax.Leq: ir.OLE,
|
|
syntax.Gtr: ir.OGT,
|
|
syntax.Geq: ir.OGE,
|
|
|
|
syntax.Add: ir.OADD,
|
|
syntax.Sub: ir.OSUB,
|
|
syntax.Or: ir.OOR,
|
|
syntax.Xor: ir.OXOR,
|
|
|
|
syntax.Mul: ir.OMUL,
|
|
syntax.Div: ir.ODIV,
|
|
syntax.Rem: ir.OMOD,
|
|
syntax.And: ir.OAND,
|
|
syntax.AndNot: ir.OANDNOT,
|
|
syntax.Shl: ir.OLSH,
|
|
syntax.Shr: ir.ORSH,
|
|
}
|
|
|
|
func (p *noder) binOp(op syntax.Operator) ir.Op {
|
|
if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 {
|
|
panic("invalid Operator")
|
|
}
|
|
return binOps[op]
|
|
}
|
|
|
|
// checkLangCompat reports an error if the representation of a numeric
|
|
// literal is not compatible with the current language version.
|
|
func checkLangCompat(lit *syntax.BasicLit) {
|
|
s := lit.Value
|
|
if len(s) <= 2 || types.AllowsGoVersion(types.LocalPkg, 1, 13) {
|
|
return
|
|
}
|
|
// len(s) > 2
|
|
if strings.Contains(s, "_") {
|
|
base.ErrorfVers("go1.13", "underscores in numeric literals")
|
|
return
|
|
}
|
|
if s[0] != '0' {
|
|
return
|
|
}
|
|
radix := s[1]
|
|
if radix == 'b' || radix == 'B' {
|
|
base.ErrorfVers("go1.13", "binary literals")
|
|
return
|
|
}
|
|
if radix == 'o' || radix == 'O' {
|
|
base.ErrorfVers("go1.13", "0o/0O-style octal literals")
|
|
return
|
|
}
|
|
if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') {
|
|
base.ErrorfVers("go1.13", "hexadecimal floating-point literals")
|
|
}
|
|
}
|
|
|
|
func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value {
|
|
// We don't use the errors of the conversion routines to determine
|
|
// if a literal string is valid because the conversion routines may
|
|
// accept a wider syntax than the language permits. Rely on lit.Bad
|
|
// instead.
|
|
if lit.Bad {
|
|
return constant.MakeUnknown()
|
|
}
|
|
|
|
switch lit.Kind {
|
|
case syntax.IntLit, syntax.FloatLit, syntax.ImagLit:
|
|
checkLangCompat(lit)
|
|
// The max. mantissa precision for untyped numeric values
|
|
// is 512 bits, or 4048 bits for each of the two integer
|
|
// parts of a fraction for floating-point numbers that are
|
|
// represented accurately in the go/constant package.
|
|
// Constant literals that are longer than this many bits
|
|
// are not meaningful; and excessively long constants may
|
|
// consume a lot of space and time for a useless conversion.
|
|
// Cap constant length with a generous upper limit that also
|
|
// allows for separators between all digits.
|
|
const limit = 10000
|
|
if len(lit.Value) > limit {
|
|
p.errorAt(lit.Pos(), "excessively long constant: %s... (%d chars)", lit.Value[:10], len(lit.Value))
|
|
return constant.MakeUnknown()
|
|
}
|
|
}
|
|
|
|
v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0)
|
|
if v.Kind() == constant.Unknown {
|
|
// TODO(mdempsky): Better error message?
|
|
p.errorAt(lit.Pos(), "malformed constant: %s", lit.Value)
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
var tokenForLitKind = [...]token.Token{
|
|
syntax.IntLit: token.INT,
|
|
syntax.RuneLit: token.CHAR,
|
|
syntax.FloatLit: token.FLOAT,
|
|
syntax.ImagLit: token.IMAG,
|
|
syntax.StringLit: token.STRING,
|
|
}
|
|
|
|
func (p *noder) name(name *syntax.Name) *types.Sym {
|
|
return typecheck.Lookup(name.Value)
|
|
}
|
|
|
|
func (p *noder) mkname(name *syntax.Name) ir.Node {
|
|
// TODO(mdempsky): Set line number?
|
|
return mkname(p.name(name))
|
|
}
|
|
|
|
func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
|
|
// These nodes do not carry line numbers.
|
|
// Introduce a wrapper node to give them the correct line.
|
|
switch x.Op() {
|
|
case ir.OTYPE, ir.OLITERAL:
|
|
if x.Sym() == nil {
|
|
break
|
|
}
|
|
fallthrough
|
|
case ir.ONAME, ir.ONONAME, ir.OPACK:
|
|
p := ir.NewParenExpr(p.pos(n), x)
|
|
p.SetImplicit(true)
|
|
return p
|
|
}
|
|
return x
|
|
}
|
|
|
|
func (p *noder) setlineno(n syntax.Node) {
|
|
if n != nil {
|
|
base.Pos = p.pos(n)
|
|
}
|
|
}
|
|
|
|
// error is called concurrently if files are parsed concurrently.
|
|
func (p *noder) error(err error) {
|
|
p.err <- err.(syntax.Error)
|
|
}
|
|
|
|
// pragmas that are allowed in the std lib, but don't have
|
|
// a syntax.Pragma value (see lex.go) associated with them.
|
|
var allowedStdPragmas = map[string]bool{
|
|
"go:cgo_export_static": true,
|
|
"go:cgo_export_dynamic": true,
|
|
"go:cgo_import_static": true,
|
|
"go:cgo_import_dynamic": true,
|
|
"go:cgo_ldflag": true,
|
|
"go:cgo_dynamic_linker": true,
|
|
"go:embed": true,
|
|
"go:generate": true,
|
|
}
|
|
|
|
// *pragmas is the value stored in a syntax.pragmas during parsing.
|
|
type pragmas struct {
|
|
Flag ir.PragmaFlag // collected bits
|
|
Pos []pragmaPos // position of each individual flag
|
|
Embeds []pragmaEmbed
|
|
}
|
|
|
|
type pragmaPos struct {
|
|
Flag ir.PragmaFlag
|
|
Pos syntax.Pos
|
|
}
|
|
|
|
type pragmaEmbed struct {
|
|
Pos syntax.Pos
|
|
Patterns []string
|
|
}
|
|
|
|
func (p *noder) checkUnused(pragma *pragmas) {
|
|
for _, pos := range pragma.Pos {
|
|
if pos.Flag&pragma.Flag != 0 {
|
|
p.errorAt(pos.Pos, "misplaced compiler directive")
|
|
}
|
|
}
|
|
if len(pragma.Embeds) > 0 {
|
|
for _, e := range pragma.Embeds {
|
|
p.errorAt(e.Pos, "misplaced go:embed directive")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *noder) checkUnusedDuringParse(pragma *pragmas) {
|
|
for _, pos := range pragma.Pos {
|
|
if pos.Flag&pragma.Flag != 0 {
|
|
p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"})
|
|
}
|
|
}
|
|
if len(pragma.Embeds) > 0 {
|
|
for _, e := range pragma.Embeds {
|
|
p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"})
|
|
}
|
|
}
|
|
}
|
|
|
|
// pragma is called concurrently if files are parsed concurrently.
|
|
func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma {
|
|
pragma, _ := old.(*pragmas)
|
|
if pragma == nil {
|
|
pragma = new(pragmas)
|
|
}
|
|
|
|
if text == "" {
|
|
// unused pragma; only called with old != nil.
|
|
p.checkUnusedDuringParse(pragma)
|
|
return nil
|
|
}
|
|
|
|
if strings.HasPrefix(text, "line ") {
|
|
// line directives are handled by syntax package
|
|
panic("unreachable")
|
|
}
|
|
|
|
if !blankLine {
|
|
// directive must be on line by itself
|
|
p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"})
|
|
return pragma
|
|
}
|
|
|
|
switch {
|
|
case strings.HasPrefix(text, "go:linkname "):
|
|
f := strings.Fields(text)
|
|
if !(2 <= len(f) && len(f) <= 3) {
|
|
p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"})
|
|
break
|
|
}
|
|
// The second argument is optional. If omitted, we use
|
|
// the default object symbol name for this and
|
|
// linkname only serves to mark this symbol as
|
|
// something that may be referenced via the object
|
|
// symbol name from another package.
|
|
var target string
|
|
if len(f) == 3 {
|
|
target = f[2]
|
|
} else if base.Ctxt.Pkgpath != "" {
|
|
// Use the default object symbol name if the
|
|
// user didn't provide one.
|
|
target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1]
|
|
} else {
|
|
p.error(syntax.Error{Pos: pos, Msg: "//go:linkname requires linkname argument or -p compiler flag"})
|
|
break
|
|
}
|
|
p.linknames = append(p.linknames, linkname{pos, f[1], target})
|
|
|
|
case text == "go:embed", strings.HasPrefix(text, "go:embed "):
|
|
args, err := parseGoEmbed(text[len("go:embed"):])
|
|
if err != nil {
|
|
p.error(syntax.Error{Pos: pos, Msg: err.Error()})
|
|
}
|
|
if len(args) == 0 {
|
|
p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."})
|
|
break
|
|
}
|
|
pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args})
|
|
|
|
case strings.HasPrefix(text, "go:cgo_import_dynamic "):
|
|
// This is permitted for general use because Solaris
|
|
// code relies on it in golang.org/x/sys/unix and others.
|
|
fields := pragmaFields(text)
|
|
if len(fields) >= 4 {
|
|
lib := strings.Trim(fields[3], `"`)
|
|
if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) {
|
|
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
|
|
}
|
|
p.pragcgo(pos, text)
|
|
pragma.Flag |= pragmaFlag("go:cgo_import_dynamic")
|
|
break
|
|
}
|
|
fallthrough
|
|
case strings.HasPrefix(text, "go:cgo_"):
|
|
// For security, we disallow //go:cgo_* directives other
|
|
// than cgo_import_dynamic outside cgo-generated files.
|
|
// Exception: they are allowed in the standard library, for runtime and syscall.
|
|
if !isCgoGeneratedFile(pos) && !base.Flag.Std {
|
|
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)})
|
|
}
|
|
p.pragcgo(pos, text)
|
|
fallthrough // because of //go:cgo_unsafe_args
|
|
default:
|
|
verb := text
|
|
if i := strings.Index(text, " "); i >= 0 {
|
|
verb = verb[:i]
|
|
}
|
|
flag := pragmaFlag(verb)
|
|
const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec
|
|
if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 {
|
|
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
|
|
}
|
|
if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std {
|
|
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)})
|
|
}
|
|
pragma.Flag |= flag
|
|
pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos})
|
|
}
|
|
|
|
return pragma
|
|
}
|
|
|
|
// isCgoGeneratedFile reports whether pos is in a file
|
|
// generated by cgo, which is to say a file with name
|
|
// beginning with "_cgo_". Such files are allowed to
|
|
// contain cgo directives, and for security reasons
|
|
// (primarily misuse of linker flags), other files are not.
|
|
// See golang.org/issue/23672.
|
|
func isCgoGeneratedFile(pos syntax.Pos) bool {
|
|
return strings.HasPrefix(filepath.Base(filepath.Clean(fileh(pos.Base().Filename()))), "_cgo_")
|
|
}
|
|
|
|
// safeArg reports whether arg is a "safe" command-line argument,
|
|
// meaning that when it appears in a command-line, it probably
|
|
// doesn't have some special meaning other than its own name.
|
|
// This is copied from SafeArg in cmd/go/internal/load/pkg.go.
|
|
func safeArg(name string) bool {
|
|
if name == "" {
|
|
return false
|
|
}
|
|
c := name[0]
|
|
return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
|
|
}
|
|
|
|
func mkname(sym *types.Sym) ir.Node {
|
|
n := oldname(sym)
|
|
if n.Name() != nil && n.Name().PkgName != nil {
|
|
n.Name().PkgName.Used = true
|
|
}
|
|
return n
|
|
}
|
|
|
|
// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
|
|
// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
|
|
// go/build/read.go also processes these strings and contains similar logic.
|
|
func parseGoEmbed(args string) ([]string, error) {
|
|
var list []string
|
|
for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
|
|
var path string
|
|
Switch:
|
|
switch args[0] {
|
|
default:
|
|
i := len(args)
|
|
for j, c := range args {
|
|
if unicode.IsSpace(c) {
|
|
i = j
|
|
break
|
|
}
|
|
}
|
|
path = args[:i]
|
|
args = args[i:]
|
|
|
|
case '`':
|
|
i := strings.Index(args[1:], "`")
|
|
if i < 0 {
|
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
|
|
}
|
|
path = args[1 : 1+i]
|
|
args = args[1+i+1:]
|
|
|
|
case '"':
|
|
i := 1
|
|
for ; i < len(args); i++ {
|
|
if args[i] == '\\' {
|
|
i++
|
|
continue
|
|
}
|
|
if args[i] == '"' {
|
|
q, err := strconv.Unquote(args[:i+1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
|
|
}
|
|
path = q
|
|
args = args[i+1:]
|
|
break Switch
|
|
}
|
|
}
|
|
if i >= len(args) {
|
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
|
|
}
|
|
}
|
|
|
|
if args != "" {
|
|
r, _ := utf8.DecodeRuneInString(args)
|
|
if !unicode.IsSpace(r) {
|
|
return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
|
|
}
|
|
}
|
|
list = append(list, path)
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func fakeRecv() *ir.Field {
|
|
return ir.NewField(base.Pos, nil, nil, types.FakeRecvType())
|
|
}
|
|
|
|
func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
|
|
fn := ir.NewClosureFunc(p.pos(expr), ir.CurFunc != nil)
|
|
fn.Nname.Ntype = p.typeExpr(expr.Type)
|
|
|
|
p.funcBody(fn, expr.Body)
|
|
|
|
ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
|
|
|
|
return fn.OClosure
|
|
}
|
|
|
|
// A function named init is a special case.
|
|
// It is called by the initialization before main is run.
|
|
// To make it unique within a package and also uncallable,
|
|
// the name, normally "pkg.init", is altered to "pkg.init.0".
|
|
var renameinitgen int
|
|
|
|
func renameinit() *types.Sym {
|
|
s := typecheck.LookupNum("init.", renameinitgen)
|
|
renameinitgen++
|
|
return s
|
|
}
|
|
|
|
// oldname returns the Node that declares symbol s in the current scope.
|
|
// If no such Node currently exists, an ONONAME Node is returned instead.
|
|
// Automatically creates a new closure variable if the referenced symbol was
|
|
// declared in a different (containing) function.
|
|
func oldname(s *types.Sym) ir.Node {
|
|
if s.Pkg != types.LocalPkg {
|
|
return ir.NewIdent(base.Pos, s)
|
|
}
|
|
|
|
n := ir.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 ir.NewIdent(base.Pos, s)
|
|
}
|
|
|
|
if n, ok := n.(*ir.Name); ok {
|
|
// 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.
|
|
return ir.CaptureName(base.Pos, ir.CurFunc, n)
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) {
|
|
pragmaEmbeds := pragma.Embeds
|
|
pragma.Embeds = nil
|
|
if len(pragmaEmbeds) == 0 {
|
|
return
|
|
}
|
|
|
|
if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil {
|
|
base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err)
|
|
return
|
|
}
|
|
|
|
var embeds []ir.Embed
|
|
for _, e := range pragmaEmbeds {
|
|
embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns})
|
|
}
|
|
typecheck.Target.Embeds = append(typecheck.Target.Embeds, name)
|
|
name.Embed = &embeds
|
|
}
|
|
|
|
func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error {
|
|
switch {
|
|
case !haveEmbed:
|
|
return errors.New("go:embed only allowed in Go files that import \"embed\"")
|
|
case len(decl.NameList) > 1:
|
|
return errors.New("go:embed cannot apply to multiple vars")
|
|
case decl.Values != nil:
|
|
return errors.New("go:embed cannot apply to var with initializer")
|
|
case decl.Type == nil:
|
|
// Should not happen, since Values == nil now.
|
|
return errors.New("go:embed cannot apply to var without type")
|
|
case withinFunc:
|
|
return errors.New("go:embed cannot apply to var inside func")
|
|
case !types.AllowsGoVersion(types.LocalPkg, 1, 16):
|
|
return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang)
|
|
|
|
default:
|
|
return nil
|
|
}
|
|
}
|