mirror of
https://github.com/golang/go.git
synced 2025-11-11 22:21:06 +00:00
cmd/compile: allow embed into any string or byte slice type
The current implementation requires saying "string" or "[]byte" and disallows aliases, defined types, and even "[]uint8". This was not 100% intended and mostly just fell out of when the checks were being done in the implementation (too early, before typechecking). After discussion on #43217 (forked into #43602), the consensus was to allow all string and byte slice types, same as we do for string conversions in the language itself. This CL does that. It's more code than you'd expect because the decision has to be delayed until after typechecking. But it also more closely aligns with the version that's already on dev.regabi. Fixes #43602. Change-Id: Iba919cfadfbd5d7116f2bf47e2512fb1d5c36731 Reviewed-on: https://go-review.googlesource.com/c/go/+/282715 Trust: Russ Cox <rsc@golang.org> Reviewed-by: Jay Conrod <jayconrod@google.com>
This commit is contained in:
parent
54198b04db
commit
ec9470162f
3 changed files with 46 additions and 61 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue