Skip to content

Commit 804d786

Browse files
rscgopherbot
authored andcommitted
go/types, cmd/compile/internal/types2: use per-file Go version
For #57001, compilers and others tools will need to understand that a different Go version can be used in different files in a program, according to the //go:build lines in those files. Update go/types and cmd/compile/internal/types2 to track and use per-file Go versions. The two must be updated together because of the files in go/types that are generated from files in types2. The effect of the //go:build go1.N line depends on the Go version declared in the 'go 1.M' line in go.mod. If N > M, the file gets go1.N semantics when built with a Go 1.N or later toolchain (when built with an earlier toolchain the //go:build line will keep the file from being built at all). If N < M, then in general we want the file to get go1.N semantics as well, meaning later features are disabled. However, older Go 1.M did not apply this kind of downgrade, so for compatibility, N < M only has an effect when M >= 21, meaning when using semantics from Go 1.21 or later. For #59033. Change-Id: I93cf07e6c687d37bd37a9461dc60cc032bafd01d Reviewed-on: https://go-review.googlesource.com/c/go/+/476278 TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Russ Cox <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Run-TryBot: Russ Cox <[email protected]>
1 parent 8854be4 commit 804d786

33 files changed

+315
-97
lines changed

src/cmd/compile/internal/types2/api.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ func AssertableTo(V *Interface, T Type) bool {
451451
if T.Underlying() == Typ[Invalid] {
452452
return false
453453
}
454-
return (*Checker)(nil).newAssertableTo(V, T, nil)
454+
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
455455
}
456456

457457
// AssignableTo reports whether a value of type V is assignable to a variable
@@ -489,15 +489,15 @@ func Implements(V Type, T *Interface) bool {
489489
if V.Underlying() == Typ[Invalid] {
490490
return false
491491
}
492-
return (*Checker)(nil).implements(V, T, false, nil)
492+
return (*Checker)(nil).implements(nopos, V, T, false, nil)
493493
}
494494

495495
// Satisfies reports whether type V satisfies the constraint T.
496496
//
497497
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
498498
// generic type.
499499
func Satisfies(V Type, T *Interface) bool {
500-
return (*Checker)(nil).implements(V, T, true, nil)
500+
return (*Checker)(nil).implements(nopos, V, T, true, nil)
501501
}
502502

503503
// Identical reports whether x and y are identical types.

src/cmd/compile/internal/types2/builtins.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
234234

235235
case _Clear:
236236
// clear(m)
237-
if !check.allowVersion(check.pkg, 1, 21) {
237+
if !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
238238
check.versionErrorf(call.Fun, "go1.21", "clear")
239239
return
240240
}
@@ -626,7 +626,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
626626

627627
case _Add:
628628
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
629-
if !check.allowVersion(check.pkg, 1, 17) {
629+
if !check.allowVersion(check.pkg, call.Pos(), 1, 17) {
630630
check.versionErrorf(call.Fun, "go1.17", "unsafe.Add")
631631
return
632632
}
@@ -762,7 +762,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
762762

763763
case _Slice:
764764
// unsafe.Slice(ptr *T, len IntegerType) []T
765-
if !check.allowVersion(check.pkg, 1, 17) {
765+
if !check.allowVersion(check.pkg, call.Pos(), 1, 17) {
766766
check.versionErrorf(call.Fun, "go1.17", "unsafe.Slice")
767767
return
768768
}
@@ -787,7 +787,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
787787

788788
case _SliceData:
789789
// unsafe.SliceData(slice []T) *T
790-
if !check.allowVersion(check.pkg, 1, 20) {
790+
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
791791
check.versionErrorf(call.Fun, "go1.20", "unsafe.SliceData")
792792
return
793793
}
@@ -806,7 +806,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
806806

807807
case _String:
808808
// unsafe.String(ptr *byte, len IntegerType) string
809-
if !check.allowVersion(check.pkg, 1, 20) {
809+
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
810810
check.versionErrorf(call.Fun, "go1.20", "unsafe.String")
811811
return
812812
}
@@ -830,7 +830,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
830830

831831
case _StringData:
832832
// unsafe.StringData(str string) *byte
833-
if !check.allowVersion(check.pkg, 1, 20) {
833+
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
834834
check.versionErrorf(call.Fun, "go1.20", "unsafe.StringData")
835835
return
836836
}

src/cmd/compile/internal/types2/call.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst *syntax.IndexExpr) {
2424
assert(tsig != nil || inst != nil)
2525

26-
if !check.allowVersion(check.pkg, 1, 18) {
26+
if !check.allowVersion(check.pkg, pos, 1, 18) {
2727
check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
2828
}
2929

@@ -278,7 +278,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
278278
// is an error checking its arguments (for example, if an incorrect number
279279
// of arguments is supplied).
280280
if got == want && want > 0 {
281-
if !check.allowVersion(check.pkg, 1, 18) {
281+
if !check.allowVersion(check.pkg, x.Pos(), 1, 18) {
282282
check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
283283
}
284284

@@ -444,7 +444,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
444444

445445
// infer type arguments and instantiate signature if necessary
446446
if sig.TypeParams().Len() > 0 {
447-
if !check.allowVersion(check.pkg, 1, 18) {
447+
if !check.allowVersion(check.pkg, call.Pos(), 1, 18) {
448448
if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
449449
check.versionErrorf(iexpr.Pos(), "go1.18", "function instantiation")
450450
} else {

src/cmd/compile/internal/types2/check.go

+27
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ type Checker struct {
116116
// (initialized by Files, valid only for the duration of check.Files;
117117
// maps and lists are allocated on demand)
118118
files []*syntax.File // list of package files
119+
posVers map[*syntax.PosBase]version // Pos -> Go version mapping
119120
imports []*PkgName // list of imported packages
120121
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
121122
recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type
@@ -281,6 +282,32 @@ func (check *Checker) initFiles(files []*syntax.File) {
281282
// ignore this file
282283
}
283284
}
285+
286+
for _, file := range check.files {
287+
v, _ := parseGoVersion(file.GoVersion)
288+
if v.major > 0 {
289+
if v.equal(check.version) {
290+
continue
291+
}
292+
// Go 1.21 introduced the feature of setting the go.mod
293+
// go line to an early version of Go and allowing //go:build lines
294+
// to “upgrade” the Go version in a given file.
295+
// We can do that backwards compatibly.
296+
// Go 1.21 also introduced the feature of allowing //go:build lines
297+
// to “downgrade” the Go version in a given file.
298+
// That can't be done compatibly in general, since before the
299+
// build lines were ignored and code got the module's Go version.
300+
// To work around this, downgrades are only allowed when the
301+
// module's Go version is Go 1.21 or later.
302+
if v.before(check.version) && check.version.before(version{1, 21}) {
303+
continue
304+
}
305+
if check.posVers == nil {
306+
check.posVers = make(map[*syntax.PosBase]version)
307+
}
308+
check.posVers[base(file.Pos())] = v
309+
}
310+
}
284311
}
285312

286313
// A bailout panic is used for early termination.

src/cmd/compile/internal/types2/conversions.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
183183
switch a := Tu.(type) {
184184
case *Array:
185185
if Identical(s.Elem(), a.Elem()) {
186-
if check == nil || check.allowVersion(check.pkg, 1, 20) {
186+
if check == nil || check.allowVersion(check.pkg, x.Pos(), 1, 20) {
187187
return true
188188
}
189189
// check != nil
@@ -196,7 +196,7 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
196196
case *Pointer:
197197
if a, _ := under(a.Elem()).(*Array); a != nil {
198198
if Identical(s.Elem(), a.Elem()) {
199-
if check == nil || check.allowVersion(check.pkg, 1, 17) {
199+
if check == nil || check.allowVersion(check.pkg, x.Pos(), 1, 17) {
200200
return true
201201
}
202202
// check != nil

src/cmd/compile/internal/types2/decl.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
506506
check.validType(t)
507507
}
508508
// If typ is local, an error was already reported where typ is specified/defined.
509-
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
509+
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, tdecl.Pos(), 1, 18) {
510510
check.versionErrorf(tdecl.Type, "go1.18", "using type constraint %s", rhs)
511511
}
512512
}).describef(obj, "validType(%s)", obj.Name())
@@ -521,7 +521,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
521521

522522
// alias declaration
523523
if alias {
524-
if !check.allowVersion(check.pkg, 1, 9) {
524+
if !check.allowVersion(check.pkg, tdecl.Pos(), 1, 9) {
525525
check.versionErrorf(tdecl, "go1.9", "type aliases")
526526
}
527527

src/cmd/compile/internal/types2/expr.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
977977
// Check that RHS is otherwise at least of integer type.
978978
switch {
979979
case allInteger(y.typ):
980-
if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
980+
if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, x.Pos(), 1, 13) {
981981
check.versionErrorf(y, "go1.13", invalidOp+"signed shift count %s", y)
982982
x.mode = invalid
983983
return

src/cmd/compile/internal/types2/instantiate.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type,
176176
// the parameterized type.
177177
bound := check.subst(pos, tpar.bound, smap, nil, ctxt)
178178
var cause string
179-
if !check.implements(targs[i], bound, true, &cause) {
179+
if !check.implements(pos, targs[i], bound, true, &cause) {
180180
return i, errors.New(cause)
181181
}
182182
}
@@ -189,7 +189,7 @@ func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type,
189189
//
190190
// If the provided cause is non-nil, it may be set to an error string
191191
// explaining why V does not implement (or satisfy, for constraints) T.
192-
func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool {
192+
func (check *Checker) implements(pos syntax.Pos, V, T Type, constraint bool, cause *string) bool {
193193
Vu := under(V)
194194
Tu := under(T)
195195
if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
@@ -262,7 +262,7 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
262262
// so that ordinary, non-type parameter interfaces implement comparable.
263263
if constraint && comparable(V, true /* spec comparability */, nil, nil) {
264264
// V is comparable if we are at Go 1.20 or higher.
265-
if check == nil || check.allowVersion(check.pkg, 1, 20) {
265+
if check == nil || check.allowVersion(check.pkg, pos, 1, 20) {
266266
return true
267267
}
268268
if cause != nil {

src/cmd/compile/internal/types2/lookup.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package types2
88

99
import (
1010
"bytes"
11+
"cmd/compile/internal/syntax"
1112
"strings"
1213
)
1314

@@ -505,14 +506,14 @@ func (check *Checker) assertableTo(V, T Type, cause *string) bool {
505506
// in constraint position (we have not yet defined that behavior in the spec).
506507
// The underlying type of V must be an interface.
507508
// If the result is false and cause is not nil, *cause is set to the error cause.
508-
func (check *Checker) newAssertableTo(V, T Type, cause *string) bool {
509+
func (check *Checker) newAssertableTo(pos syntax.Pos, V, T Type, cause *string) bool {
509510
// no static check is required if T is an interface
510511
// spec: "If T is an interface type, x.(T) asserts that the
511512
// dynamic type of x implements the interface T."
512513
if IsInterface(T) {
513514
return true
514515
}
515-
return check.implements(T, V, false, cause)
516+
return check.implements(pos, T, V, false, cause)
516517
}
517518

518519
// deref dereferences typ if it is a *Pointer (but not a *Named type

src/cmd/compile/internal/types2/operand.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,15 @@ func (x *operand) assignableTo(check *Checker, T Type, cause *string) (bool, Cod
293293
// T is an interface type and x implements T and T is not a type parameter.
294294
// Also handle the case where T is a pointer to an interface.
295295
if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) {
296-
if !check.implements(V, T, false, cause) {
296+
if !check.implements(x.Pos(), V, T, false, cause) {
297297
return false, InvalidIfaceAssign
298298
}
299299
return true, 0
300300
}
301301

302302
// If V is an interface, check if a missing type assertion is the problem.
303303
if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
304-
if check.implements(T, V, false, nil) {
304+
if check.implements(x.Pos(), T, V, false, nil) {
305305
// T implements V, so give hint about type assertion.
306306
if cause != nil {
307307
*cause = "need type assertion"

src/cmd/compile/internal/types2/resolver.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func (check *Checker) collectObjects() {
406406
}
407407

408408
case *syntax.TypeDecl:
409-
if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) {
409+
if len(s.TParamList) != 0 && !check.allowVersion(pkg, s.Pos(), 1, 18) {
410410
check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
411411
}
412412
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
@@ -455,7 +455,7 @@ func (check *Checker) collectObjects() {
455455
}
456456
check.recordDef(s.Name, obj)
457457
}
458-
if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
458+
if len(s.TParamList) != 0 && !check.allowVersion(pkg, s.Pos(), 1, 18) && !hasTParamError {
459459
check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
460460
}
461461
info := &declInfo{file: fileScope, fdecl: s}

src/cmd/compile/internal/types2/typeset.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
244244
}
245245
// check != nil
246246
check.later(func() {
247-
if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) {
247+
if !check.allowVersion(m.pkg, pos, 1, 14) || !Identical(m.typ, other.Type()) {
248248
var err error_
249249
err.code = DuplicateDecl
250250
err.errorf(pos, "duplicate method %s", m.name)
@@ -278,7 +278,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
278278
assert(!isTypeParam(typ))
279279
tset := computeInterfaceTypeSet(check, pos, u)
280280
// If typ is local, an error was already reported where typ is specified/defined.
281-
if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, 1, 18) {
281+
if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, pos, 1, 18) {
282282
check.versionErrorf(pos, "go1.18", "embedding constraint interface %s", typ)
283283
continue
284284
}
@@ -288,7 +288,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
288288
}
289289
terms = tset.terms
290290
case *Union:
291-
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
291+
if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
292292
check.versionErrorf(pos, "go1.18", "embedding interface element %s", u)
293293
continue
294294
}
@@ -303,7 +303,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
303303
if u == Typ[Invalid] {
304304
continue
305305
}
306-
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
306+
if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
307307
check.versionErrorf(pos, "go1.18", "embedding non-interface type %s", typ)
308308
continue
309309
}

src/cmd/compile/internal/types2/typexpr.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
4242
}
4343
return
4444
case universeAny, universeComparable:
45-
if !check.allowVersion(check.pkg, 1, 18) {
45+
if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
4646
check.versionErrorf(e, "go1.18", "predeclared %s", e.Value)
4747
return // avoid follow-on errors
4848
}
@@ -272,7 +272,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
272272
}
273273

274274
case *syntax.IndexExpr:
275-
if !check.allowVersion(check.pkg, 1, 18) {
275+
if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
276276
check.versionErrorf(e.Pos(), "go1.18", "type instantiation")
277277
}
278278
return check.instantiatedType(e.X, unpackExpr(e.Index), def)

0 commit comments

Comments
 (0)