Skip to content

Commit

Permalink
cmd/compile: don't apply -lang=go1.X restrictions to imported packages
Browse files Browse the repository at this point in the history
Previously langSupported applied -lang as though it's a global
restriction, but it's actually a per-package restriction. This CL
fixes langSupported to take a *types.Pkg parameter to reflect this and
updates its callers accordingly.

This is relevant for signed shifts (added in Go 1.12), because they
can be inlined into a Go 1.11 package; and for overlapping interfaces
(added in Go 1.13), because they can be exported as part of the
package's API.

Today we require all Go packages to be compiled with the same
toolchain, and all uses of langSupported are for controlling
backwards-compatible features. So we can simply assume that since the
imported packages type-checked successfully, they must have been
compiled with an appropriate -lang setting.

In the future if we ever want to use langSupported to control
backwards-incompatible language changes, we might need to record the
-lang flag used for compiling a package in its export data.

Fixes #35437.
Fixes #35442.

Change-Id: Ifdf6a62ee80cd5fb4366cbf12933152506d1b36e
Reviewed-on: https://go-review.googlesource.com/c/go/+/205977
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
  • Loading branch information
mdempsky committed Nov 8, 2019
1 parent 9ee6ba0 commit b7d097a
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/gc/align.go
Expand Up @@ -34,7 +34,7 @@ func expandiface(t *types.Type) {
switch prev := seen[m.Sym]; {
case prev == nil:
seen[m.Sym] = m
case langSupported(1, 14) && !explicit && types.Identical(m.Type, prev.Type):
case langSupported(1, 14, t.Pkg()) && !explicit && types.Identical(m.Type, prev.Type):
return
default:
yyerrorl(m.Pos, "duplicate method %s", m.Sym.Name)
Expand Down
14 changes: 12 additions & 2 deletions src/cmd/compile/internal/gc/main.go
Expand Up @@ -1477,8 +1477,18 @@ type lang struct {
// any language version is supported.
var langWant lang

// langSupported reports whether language version major.minor is supported.
func langSupported(major, minor int) bool {
// langSupported reports whether language version major.minor is
// supported in a particular package.
func langSupported(major, minor int, pkg *types.Pkg) bool {
if pkg == nil {
// TODO(mdempsky): Set Pkg for local types earlier.
pkg = localpkg
}
if pkg != localpkg {
// Assume imported packages passed type-checking.
return true
}

if langWant.major == 0 && langWant.minor == 0 {
return true
}
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/compile/internal/gc/noder.go
Expand Up @@ -446,7 +446,7 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
}

nod := p.nod(decl, ODCLTYPE, n, nil)
if param.Alias && !langSupported(1, 9) {
if param.Alias && !langSupported(1, 9, localpkg) {
yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9")
}
return nod
Expand Down Expand Up @@ -1321,7 +1321,7 @@ func (p *noder) binOp(op syntax.Operator) Op {
// literal is not compatible with the current language version.
func checkLangCompat(lit *syntax.BasicLit) {
s := lit.Value
if len(s) <= 2 || langSupported(1, 13) {
if len(s) <= 2 || langSupported(1, 13, localpkg) {
return
}
// len(s) > 2
Expand Down
19 changes: 18 additions & 1 deletion src/cmd/compile/internal/gc/typecheck.go
Expand Up @@ -608,7 +608,7 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
}
if t.IsSigned() && !langSupported(1, 13) {
if t.IsSigned() && !langSupported(1, 13, curpkg()) {
yyerrorv("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type)
n.Type = nil
return n
Expand Down Expand Up @@ -3951,3 +3951,20 @@ func getIotaValue() int64 {

return -1
}

// curpkg returns the current package, based on Curfn.
func curpkg() *types.Pkg {
fn := Curfn
if fn == nil {
// Initialization expressions for package-scope variables.
return localpkg
}

// TODO(mdempsky): Standardize on either ODCLFUNC or ONAME for
// Curfn, rather than mixing them.
if fn.Op == ODCLFUNC {
fn = fn.Func.Nname
}

return fnpkg(fn)
}
43 changes: 43 additions & 0 deletions src/cmd/go/testdata/script/mod_go_version_mixed.txt
@@ -0,0 +1,43 @@
# Test that dependencies can use Go language features newer than the
# Go version specified by the main module.

env GO111MODULE=on

go build

-- go.mod --
module m
go 1.12
require (
sub.1 v1.0.0
)
replace (
sub.1 => ./sub
)

-- x.go --
package x

import "sub.1"

func F() { sub.F(0, 0) }

var A sub.Alias
var D sub.Defined

-- sub/go.mod --
module m
go 1.14

-- sub/sub.go --
package sub

// signed shift counts added in Go 1.13
func F(l, r int) int { return l << r }

type m1 interface { M() }
type m2 interface { M() }

// overlapping interfaces added in Go 1.14
type Alias = interface { m1; m2; M() }
type Defined interface { m1; m2; M() }

0 comments on commit b7d097a

Please sign in to comment.