]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: allow embed into any string or byte slice type
authorRuss Cox <rsc@golang.org>
Fri, 8 Jan 2021 19:27:00 +0000 (14:27 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 15 Jan 2021 20:37:59 +0000 (20:37 +0000)
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>
src/cmd/compile/internal/gc/embed.go
src/cmd/compile/internal/gc/noder.go
src/cmd/compile/internal/gc/syntax.go

index 6db246eece54b1eee2368b0fd62a6b0fac1cf5a3..13077809603e0e00947d74abd9f1006766c8f107 100644 (file)
@@ -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)
index 67d24ef0bc70672be7c579b9ee211066b34d80c6..7494c3ef6b1b7b2a81017f0db08eaae630f8bb2a 100644 (file)
@@ -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
                }
index 43358333b82e7dc062fc6f4cf8c378f7702d5ccd..7b4a315e05d4df214795a0fd11229b1b2bfaef31 100644 (file)
@@ -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