diff --git a/src/cmd/compile/internal/gc/embed.go b/src/cmd/compile/internal/gc/embed.go index 6db246eece..1307780960 100644 --- a/src/cmd/compile/internal/gc/embed.go +++ b/src/cmd/compile/internal/gc/embed.go @@ -47,9 +47,7 @@ const ( embedFiles ) -var numLocalEmbed int - -func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) (newExprs []*Node) { +func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) { haveEmbed := false for _, decl := range p.file.DeclList { imp, ok := decl.(*syntax.ImportDecl) @@ -67,44 +65,52 @@ func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []Pragma pos := embeds[0].Pos if !haveEmbed { p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"") - return exprs + return } if embedCfg.Patterns == nil { p.yyerrorpos(pos, "invalid go:embed: build system did not supply embed configuration") - return exprs + return } if len(names) > 1 { p.yyerrorpos(pos, "go:embed cannot apply to multiple vars") - return exprs + return } if len(exprs) > 0 { p.yyerrorpos(pos, "go:embed cannot apply to var with initializer") - return exprs + return } if typ == nil { // Should not happen, since len(exprs) == 0 now. p.yyerrorpos(pos, "go:embed cannot apply to var without type") - return exprs + return + } + if dclcontext != PEXTERN { + p.yyerrorpos(pos, "go:embed cannot apply to var inside func") + return } - kind := embedKindApprox(typ) - if kind == embedUnknown { - p.yyerrorpos(pos, "go:embed cannot apply to var of type %v", typ) - return exprs + var list []irEmbed + for _, e := range embeds { + list = append(list, irEmbed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns}) } + v := names[0] + v.Name.Param.SetEmbedList(list) + embedlist = append(embedlist, v) +} +func embedFileList(v *Node, kind int) []string { // Build list of files to store. have := make(map[string]bool) var list []string - for _, e := range embeds { + for _, e := range v.Name.Param.EmbedList() { for _, pattern := range e.Patterns { files, ok := embedCfg.Patterns[pattern] if !ok { - p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern) + yyerrorl(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern) } for _, file := range files { if embedCfg.Files[file] == "" { - p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map file: %s", file) + yyerrorl(e.Pos, "invalid go:embed: build system did not map file: %s", file) continue } if !have[file] { @@ -126,41 +132,12 @@ func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []Pragma if kind == embedString || kind == embedBytes { if len(list) > 1 { - p.yyerrorpos(pos, "invalid go:embed: multiple files for type %v", typ) - return exprs + yyerrorl(v.Pos, "invalid go:embed: multiple files for type %v", v.Type) + return nil } } - v := names[0] - if dclcontext != PEXTERN { - p.yyerrorpos(pos, "go:embed cannot apply to var inside func") - return exprs - } - - v.Name.Param.SetEmbedFiles(list) - embedlist = append(embedlist, v) - return exprs -} - -// embedKindApprox determines the kind of embedding variable, approximately. -// The match is approximate because we haven't done scope resolution yet and -// can't tell whether "string" and "byte" really mean "string" and "byte". -// The result must be confirmed later, after type checking, using embedKind. -func embedKindApprox(typ *Node) int { - if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) { - return embedFiles - } - // These are not guaranteed to match only string and []byte - - // maybe the local package has redefined one of those words. - // But it's the best we can do now during the noder. - // The stricter check happens later, in initEmbed calling embedKind. - if typ.Sym != nil && typ.Sym.Name == "string" && typ.Sym.Pkg == localpkg { - return embedString - } - if typ.Op == OTARRAY && typ.Left == nil && typ.Right.Sym != nil && typ.Right.Sym.Name == "byte" && typ.Right.Sym.Pkg == localpkg { - return embedBytes - } - return embedUnknown + return list } // embedKind determines the kind of embedding variable. @@ -168,10 +145,10 @@ func embedKind(typ *types.Type) int { if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) { return embedFiles } - if typ == types.Types[TSTRING] { + if typ.Etype == types.TSTRING { return embedString } - if typ.Sym == nil && typ.IsSlice() && typ.Elem() == types.Bytetype { + if typ.Etype == types.TSLICE && typ.Elem().Etype == types.TUINT8 { return embedBytes } return embedUnknown @@ -209,11 +186,14 @@ func dumpembeds() { // initEmbed emits the init data for a //go:embed variable, // which is either a string, a []byte, or an embed.FS. func initEmbed(v *Node) { - files := v.Name.Param.EmbedFiles() - switch kind := embedKind(v.Type); kind { - case embedUnknown: + kind := embedKind(v.Type) + if kind == embedUnknown { yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type) + return + } + files := embedFileList(v, kind) + switch kind { case embedString, embedBytes: file := files[0] fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil) diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 67d24ef0bc..7494c3ef6b 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -397,7 +397,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node { p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"") } } else { - exprs = varEmbed(p, names, typ, exprs, pragma.Embeds) + varEmbed(p, names, typ, exprs, pragma.Embeds) } pragma.Embeds = nil } diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 43358333b8..7b4a315e05 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -499,7 +499,12 @@ type paramType struct { alias bool } -type embedFileList []string +type irEmbed struct { + Pos src.XPos + Patterns []string +} + +type embedList []irEmbed // Pragma returns the PragmaFlag for p, which must be for an OTYPE. func (p *Param) Pragma() PragmaFlag { @@ -547,28 +552,28 @@ func (p *Param) SetAlias(alias bool) { (*p.Extra).(*paramType).alias = alias } -// EmbedFiles returns the list of embedded files for p, +// EmbedList returns the list of embedded files for p, // which must be for an ONAME var. -func (p *Param) EmbedFiles() []string { +func (p *Param) EmbedList() []irEmbed { if p.Extra == nil { return nil } - return *(*p.Extra).(*embedFileList) + return *(*p.Extra).(*embedList) } -// SetEmbedFiles sets the list of embedded files for p, +// SetEmbedList sets the list of embedded files for p, // which must be for an ONAME var. -func (p *Param) SetEmbedFiles(list []string) { +func (p *Param) SetEmbedList(list []irEmbed) { if p.Extra == nil { if len(list) == 0 { return } - f := embedFileList(list) + f := embedList(list) p.Extra = new(interface{}) *p.Extra = &f return } - *(*p.Extra).(*embedFileList) = list + *(*p.Extra).(*embedList) = list } // Functions