Skip to content

Commit 2e9f081

Browse files
cmd/compile: add -lang flag to specify language version
The default language version is the current one. For testing purposes, added a check that type aliases require version go1.9. There is no consistent support for changes made before 1.12. Updates #28221 Change-Id: Ia1ef63fff911d5fd29ef79d5fa4e20cfd945feb7 Reviewed-on: https://go-review.googlesource.com/c/144340 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent d1836e6 commit 2e9f081

File tree

5 files changed

+137
-2
lines changed

5 files changed

+137
-2
lines changed

src/cmd/compile/doc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ Flags:
6464
instead of $GOROOT/pkg/$GOOS_$GOARCH.
6565
-l
6666
Disable inlining.
67+
-lang version
68+
Set language version to compile, as in -lang=go1.12.
69+
Default is current version.
6770
-largemodel
6871
Generate code that assumes a large memory model.
6972
-linkobj file
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package gc
6+
7+
import (
8+
"internal/testenv"
9+
"io/ioutil"
10+
"os"
11+
"os/exec"
12+
"path/filepath"
13+
"testing"
14+
)
15+
16+
const aliasSrc = `
17+
package x
18+
19+
type T = int
20+
`
21+
22+
func TestInvalidLang(t *testing.T) {
23+
t.Parallel()
24+
25+
testenv.MustHaveGoBuild(t)
26+
27+
dir, err := ioutil.TempDir("", "TestInvalidLang")
28+
if err != nil {
29+
t.Fatal(err)
30+
}
31+
defer os.RemoveAll(dir)
32+
33+
src := filepath.Join(dir, "alias.go")
34+
if err := ioutil.WriteFile(src, []byte(aliasSrc), 0644); err != nil {
35+
t.Fatal(err)
36+
}
37+
38+
outfile := filepath.Join(dir, "alias.o")
39+
40+
if testLang(t, "go9.99", src, outfile) == nil {
41+
t.Error("compilation with -lang=go9.99 succeeded unexpectedly")
42+
}
43+
44+
if testLang(t, "go1.8", src, outfile) == nil {
45+
t.Error("compilation with -lang=go1.8 succeeded unexpectedly")
46+
}
47+
48+
if err := testLang(t, "go1.9", src, outfile); err != nil {
49+
t.Errorf("compilation with -lang=go1.9 failed unexpectedly: %v", err)
50+
}
51+
}
52+
53+
func testLang(t *testing.T, lang, src, outfile string) error {
54+
run := []string{testenv.GoToolPath(t), "tool", "compile", "-lang", lang, "-o", outfile, src}
55+
t.Log(run)
56+
out, err := exec.Command(run[0], run[1:]...).CombinedOutput()
57+
t.Logf("%s", out)
58+
return err
59+
}

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import (
1919
"cmd/internal/sys"
2020
"flag"
2121
"fmt"
22+
"go/build"
2223
"io"
2324
"io/ioutil"
2425
"log"
2526
"os"
2627
"path"
28+
"regexp"
2729
"runtime"
2830
"strconv"
2931
"strings"
@@ -211,6 +213,7 @@ func Main(archInit func(*Arch)) {
211213
flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
212214
objabi.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
213215
objabi.Flagcount("l", "disable inlining", &Debug['l'])
216+
flag.StringVar(&flag_lang, "lang", defaultLang(), "release to compile for")
214217
flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
215218
objabi.Flagcount("live", "debug liveness analysis", &debuglive)
216219
objabi.Flagcount("m", "print optimization decisions", &Debug['m'])
@@ -277,6 +280,8 @@ func Main(archInit func(*Arch)) {
277280
Exit(2)
278281
}
279282

283+
checkLang()
284+
280285
thearch.LinkArch.Init(Ctxt)
281286

282287
if outfile == "" {
@@ -1304,3 +1309,66 @@ func recordFlags(flags ...string) {
13041309
Ctxt.Data = append(Ctxt.Data, s)
13051310
s.P = cmd.Bytes()[1:]
13061311
}
1312+
1313+
// flag_lang is the language version we are compiling for, set by the -lang flag.
1314+
var flag_lang string
1315+
1316+
// defaultLang returns the default value for the -lang flag.
1317+
func defaultLang() string {
1318+
tags := build.Default.ReleaseTags
1319+
return tags[len(tags)-1]
1320+
}
1321+
1322+
// goVersionRE is a regular expression that matches the valid
1323+
// arguments to the -lang flag.
1324+
var goVersionRE = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
1325+
1326+
// A lang is a language version broken into major and minor numbers.
1327+
type lang struct {
1328+
major, minor int
1329+
}
1330+
1331+
// langWant is the desired language version set by the -lang flag.
1332+
var langWant lang
1333+
1334+
// langSupported reports whether language version major.minor is supported.
1335+
func langSupported(major, minor int) bool {
1336+
return langWant.major > major || (langWant.major == major && langWant.minor >= minor)
1337+
}
1338+
1339+
// checkLang verifies that the -lang flag holds a valid value, and
1340+
// exits if not. It initializes data used by langSupported.
1341+
func checkLang() {
1342+
var err error
1343+
langWant, err = parseLang(flag_lang)
1344+
if err != nil {
1345+
log.Fatalf("invalid value %q for -lang: %v", flag_lang, err)
1346+
}
1347+
1348+
if def := defaultLang(); flag_lang != def {
1349+
defVers, err := parseLang(def)
1350+
if err != nil {
1351+
log.Fatalf("internal error parsing default lang %q: %v", def, err)
1352+
}
1353+
if langWant.major > defVers.major || (langWant.major == defVers.major && langWant.major > defVers.minor) {
1354+
log.Fatalf("invalid value %q for -lang: max known version is %q", flag_lang, def)
1355+
}
1356+
}
1357+
}
1358+
1359+
// parseLang parses a -lang option into a langVer.
1360+
func parseLang(s string) (lang, error) {
1361+
matches := goVersionRE.FindStringSubmatch(s)
1362+
if matches == nil {
1363+
return lang{}, fmt.Errorf(`should be something like "go1.12"`)
1364+
}
1365+
major, err := strconv.Atoi(matches[1])
1366+
if err != nil {
1367+
return lang{}, err
1368+
}
1369+
minor, err := strconv.Atoi(matches[2])
1370+
if err != nil {
1371+
return lang{}, err
1372+
}
1373+
return lang{major: major, minor: minor}, nil
1374+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,11 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
417417
param.Pragma = 0
418418
}
419419

420-
return p.nod(decl, ODCLTYPE, n, nil)
421-
420+
nod := p.nod(decl, ODCLTYPE, n, nil)
421+
if param.Alias && !langSupported(1, 9) {
422+
yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9")
423+
}
424+
return nod
422425
}
423426

424427
func (p *noder) declNames(names []*syntax.Name) []*Node {

src/go/build/build.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type Context struct {
4545
// which defaults to the list of Go releases the current release is compatible with.
4646
// In addition to the BuildTags and ReleaseTags, build constraints
4747
// consider the values of GOARCH and GOOS as satisfied tags.
48+
// The last element in ReleaseTags is assumed to be the current release.
4849
BuildTags []string
4950
ReleaseTags []string
5051

@@ -296,6 +297,7 @@ func defaultContext() Context {
296297
// say "+build go1.x", and code that should only be built before Go 1.x
297298
// (perhaps it is the stub to use in that case) should say "+build !go1.x".
298299
// NOTE: If you add to this list, also update the doc comment in doc.go.
300+
// NOTE: The last element in ReleaseTags should be the current release.
299301
const version = 11 // go1.11
300302
for i := 1; i <= version; i++ {
301303
c.ReleaseTags = append(c.ReleaseTags, "go1."+strconv.Itoa(i))

0 commit comments

Comments
 (0)