]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: don't apply -lang=go1.X restrictions to imported packages
authorMatthew Dempsky <mdempsky@google.com>
Thu, 7 Nov 2019 23:54:59 +0000 (15:54 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Fri, 8 Nov 2019 21:27:51 +0000 (21:27 +0000)
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>
src/cmd/compile/internal/gc/align.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/noder.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/go/testdata/script/mod_go_version_mixed.txt [new file with mode: 0644]

index 44a06fd72781926f13b5c0360938231ad749382d..78be1b231e9863a3cf881c52c9191be24b76fbee 100644 (file)
@@ -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)
index 721ebeed6f76b117c19c316b729cd1f5fdf1ee0b..428a74f26c743f1cf66b620f2c6dbf673aebebed 100644 (file)
@@ -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
        }
index e871878e29f1ceb740f110c3b0d27d7fdd5baf02..b6713ba685989194e9cad64f1a5cc7e5b4a0670b 100644 (file)
@@ -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
@@ -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
index 7b851d3d086e894573cc5b4918b9217f547fd668..ae2e16760d197743b10b22b7bc32de5602cb8c71 100644 (file)
@@ -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
@@ -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)
+}
diff --git a/src/cmd/go/testdata/script/mod_go_version_mixed.txt b/src/cmd/go/testdata/script/mod_go_version_mixed.txt
new file mode 100644 (file)
index 0000000..d6216ae
--- /dev/null
@@ -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() }