Skip to content

Commit ec94701

Browse files
committed
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 <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 54198b0 commit ec94701

File tree

3 files changed

+46
-61
lines changed

3 files changed

+46
-61
lines changed

src/cmd/compile/internal/gc/embed.go

+32-52
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ const (
4747
embedFiles
4848
)
4949

50-
var numLocalEmbed int
51-
52-
func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) (newExprs []*Node) {
50+
func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) {
5351
haveEmbed := false
5452
for _, decl := range p.file.DeclList {
5553
imp, ok := decl.(*syntax.ImportDecl)
@@ -67,44 +65,52 @@ func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []Pragma
6765
pos := embeds[0].Pos
6866
if !haveEmbed {
6967
p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"")
70-
return exprs
68+
return
7169
}
7270
if embedCfg.Patterns == nil {
7371
p.yyerrorpos(pos, "invalid go:embed: build system did not supply embed configuration")
74-
return exprs
72+
return
7573
}
7674
if len(names) > 1 {
7775
p.yyerrorpos(pos, "go:embed cannot apply to multiple vars")
78-
return exprs
76+
return
7977
}
8078
if len(exprs) > 0 {
8179
p.yyerrorpos(pos, "go:embed cannot apply to var with initializer")
82-
return exprs
80+
return
8381
}
8482
if typ == nil {
8583
// Should not happen, since len(exprs) == 0 now.
8684
p.yyerrorpos(pos, "go:embed cannot apply to var without type")
87-
return exprs
85+
return
86+
}
87+
if dclcontext != PEXTERN {
88+
p.yyerrorpos(pos, "go:embed cannot apply to var inside func")
89+
return
8890
}
8991

90-
kind := embedKindApprox(typ)
91-
if kind == embedUnknown {
92-
p.yyerrorpos(pos, "go:embed cannot apply to var of type %v", typ)
93-
return exprs
92+
var list []irEmbed
93+
for _, e := range embeds {
94+
list = append(list, irEmbed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns})
9495
}
96+
v := names[0]
97+
v.Name.Param.SetEmbedList(list)
98+
embedlist = append(embedlist, v)
99+
}
95100

101+
func embedFileList(v *Node, kind int) []string {
96102
// Build list of files to store.
97103
have := make(map[string]bool)
98104
var list []string
99-
for _, e := range embeds {
105+
for _, e := range v.Name.Param.EmbedList() {
100106
for _, pattern := range e.Patterns {
101107
files, ok := embedCfg.Patterns[pattern]
102108
if !ok {
103-
p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
109+
yyerrorl(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
104110
}
105111
for _, file := range files {
106112
if embedCfg.Files[file] == "" {
107-
p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map file: %s", file)
113+
yyerrorl(e.Pos, "invalid go:embed: build system did not map file: %s", file)
108114
continue
109115
}
110116
if !have[file] {
@@ -126,52 +132,23 @@ func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []Pragma
126132

127133
if kind == embedString || kind == embedBytes {
128134
if len(list) > 1 {
129-
p.yyerrorpos(pos, "invalid go:embed: multiple files for type %v", typ)
130-
return exprs
135+
yyerrorl(v.Pos, "invalid go:embed: multiple files for type %v", v.Type)
136+
return nil
131137
}
132138
}
133139

134-
v := names[0]
135-
if dclcontext != PEXTERN {
136-
p.yyerrorpos(pos, "go:embed cannot apply to var inside func")
137-
return exprs
138-
}
139-
140-
v.Name.Param.SetEmbedFiles(list)
141-
embedlist = append(embedlist, v)
142-
return exprs
143-
}
144-
145-
// embedKindApprox determines the kind of embedding variable, approximately.
146-
// The match is approximate because we haven't done scope resolution yet and
147-
// can't tell whether "string" and "byte" really mean "string" and "byte".
148-
// The result must be confirmed later, after type checking, using embedKind.
149-
func embedKindApprox(typ *Node) int {
150-
if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
151-
return embedFiles
152-
}
153-
// These are not guaranteed to match only string and []byte -
154-
// maybe the local package has redefined one of those words.
155-
// But it's the best we can do now during the noder.
156-
// The stricter check happens later, in initEmbed calling embedKind.
157-
if typ.Sym != nil && typ.Sym.Name == "string" && typ.Sym.Pkg == localpkg {
158-
return embedString
159-
}
160-
if typ.Op == OTARRAY && typ.Left == nil && typ.Right.Sym != nil && typ.Right.Sym.Name == "byte" && typ.Right.Sym.Pkg == localpkg {
161-
return embedBytes
162-
}
163-
return embedUnknown
140+
return list
164141
}
165142

166143
// embedKind determines the kind of embedding variable.
167144
func embedKind(typ *types.Type) int {
168145
if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
169146
return embedFiles
170147
}
171-
if typ == types.Types[TSTRING] {
148+
if typ.Etype == types.TSTRING {
172149
return embedString
173150
}
174-
if typ.Sym == nil && typ.IsSlice() && typ.Elem() == types.Bytetype {
151+
if typ.Etype == types.TSLICE && typ.Elem().Etype == types.TUINT8 {
175152
return embedBytes
176153
}
177154
return embedUnknown
@@ -209,11 +186,14 @@ func dumpembeds() {
209186
// initEmbed emits the init data for a //go:embed variable,
210187
// which is either a string, a []byte, or an embed.FS.
211188
func initEmbed(v *Node) {
212-
files := v.Name.Param.EmbedFiles()
213-
switch kind := embedKind(v.Type); kind {
214-
case embedUnknown:
189+
kind := embedKind(v.Type)
190+
if kind == embedUnknown {
215191
yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type)
192+
return
193+
}
216194

195+
files := embedFileList(v, kind)
196+
switch kind {
217197
case embedString, embedBytes:
218198
file := files[0]
219199
fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil)

src/cmd/compile/internal/gc/noder.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
397397
p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"")
398398
}
399399
} else {
400-
exprs = varEmbed(p, names, typ, exprs, pragma.Embeds)
400+
varEmbed(p, names, typ, exprs, pragma.Embeds)
401401
}
402402
pragma.Embeds = nil
403403
}

src/cmd/compile/internal/gc/syntax.go

+13-8
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,12 @@ type paramType struct {
499499
alias bool
500500
}
501501

502-
type embedFileList []string
502+
type irEmbed struct {
503+
Pos src.XPos
504+
Patterns []string
505+
}
506+
507+
type embedList []irEmbed
503508

504509
// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
505510
func (p *Param) Pragma() PragmaFlag {
@@ -547,28 +552,28 @@ func (p *Param) SetAlias(alias bool) {
547552
(*p.Extra).(*paramType).alias = alias
548553
}
549554

550-
// EmbedFiles returns the list of embedded files for p,
555+
// EmbedList returns the list of embedded files for p,
551556
// which must be for an ONAME var.
552-
func (p *Param) EmbedFiles() []string {
557+
func (p *Param) EmbedList() []irEmbed {
553558
if p.Extra == nil {
554559
return nil
555560
}
556-
return *(*p.Extra).(*embedFileList)
561+
return *(*p.Extra).(*embedList)
557562
}
558563

559-
// SetEmbedFiles sets the list of embedded files for p,
564+
// SetEmbedList sets the list of embedded files for p,
560565
// which must be for an ONAME var.
561-
func (p *Param) SetEmbedFiles(list []string) {
566+
func (p *Param) SetEmbedList(list []irEmbed) {
562567
if p.Extra == nil {
563568
if len(list) == 0 {
564569
return
565570
}
566-
f := embedFileList(list)
571+
f := embedList(list)
567572
p.Extra = new(interface{})
568573
*p.Extra = &f
569574
return
570575
}
571-
*(*p.Extra).(*embedFileList) = list
576+
*(*p.Extra).(*embedList) = list
572577
}
573578

574579
// Functions

0 commit comments

Comments
 (0)