]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/version.go
go/types, types2: use version data type instead of major,minor ints
[gostls13.git] / src / cmd / compile / internal / types2 / version.go
1 // Copyright 2021 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 types2
6
7 import (
8         "cmd/compile/internal/syntax"
9         "errors"
10         "fmt"
11         "strings"
12 )
13
14 // A version represents a released Go version.
15 type version struct {
16         major, minor int
17 }
18
19 func (v version) String() string {
20         return fmt.Sprintf("go%d.%d", v.major, v.minor)
21 }
22
23 func (v version) equal(u version) bool {
24         return v.major == u.major && v.minor == u.minor
25 }
26
27 func (v version) before(u version) bool {
28         return v.major < u.major || v.major == u.major && v.minor < u.minor
29 }
30
31 func (v version) after(u version) bool {
32         return v.major > u.major || v.major == u.major && v.minor > u.minor
33 }
34
35 // Go versions that introduced language changes.
36 var (
37         go0_0  = version{0, 0} // no version specified
38         go1_9  = version{1, 9}
39         go1_13 = version{1, 13}
40         go1_14 = version{1, 14}
41         go1_17 = version{1, 17}
42         go1_18 = version{1, 18}
43         go1_20 = version{1, 20}
44         go1_21 = version{1, 21}
45 )
46
47 var errVersionSyntax = errors.New("invalid Go version syntax")
48
49 // parseGoVersion parses a Go version string (such as "go1.12")
50 // and returns the version, or an error. If s is the empty
51 // string, the version is 0.0.
52 func parseGoVersion(s string) (v version, err error) {
53         if s == "" {
54                 return
55         }
56         if !strings.HasPrefix(s, "go") {
57                 return version{}, errVersionSyntax
58         }
59         s = s[len("go"):]
60         i := 0
61         for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
62                 if i >= 10 || i == 0 && s[i] == '0' {
63                         return version{}, errVersionSyntax
64                 }
65                 v.major = 10*v.major + int(s[i]) - '0'
66         }
67         if i > 0 && i == len(s) {
68                 return
69         }
70         if i == 0 || s[i] != '.' {
71                 return version{}, errVersionSyntax
72         }
73         s = s[i+1:]
74         if s == "0" {
75                 // We really should not accept "go1.0",
76                 // but we didn't reject it from the start
77                 // and there are now programs that use it.
78                 // So accept it.
79                 return
80         }
81         i = 0
82         for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
83                 if i >= 10 || i == 0 && s[i] == '0' {
84                         return version{}, errVersionSyntax
85                 }
86                 v.minor = 10*v.minor + int(s[i]) - '0'
87         }
88         if i > 0 && i == len(s) {
89                 return
90         }
91         return version{}, errVersionSyntax
92 }
93
94 // langCompat reports an error if the representation of a numeric
95 // literal is not compatible with the current language version.
96 func (check *Checker) langCompat(lit *syntax.BasicLit) {
97         s := lit.Value
98         if len(s) <= 2 || check.allowVersion(check.pkg, lit, go1_13) {
99                 return
100         }
101         // len(s) > 2
102         if strings.Contains(s, "_") {
103                 check.versionErrorf(lit, go1_13, "underscores in numeric literals")
104                 return
105         }
106         if s[0] != '0' {
107                 return
108         }
109         radix := s[1]
110         if radix == 'b' || radix == 'B' {
111                 check.versionErrorf(lit, go1_13, "binary literals")
112                 return
113         }
114         if radix == 'o' || radix == 'O' {
115                 check.versionErrorf(lit, go1_13, "0o/0O-style octal literals")
116                 return
117         }
118         if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') {
119                 check.versionErrorf(lit, go1_13, "hexadecimal floating-point literals")
120         }
121 }
122
123 // allowVersion reports whether the given package
124 // is allowed to use version major.minor.
125 func (check *Checker) allowVersion(pkg *Package, at poser, v version) bool {
126         // We assume that imported packages have all been checked,
127         // so we only have to check for the local package.
128         if pkg != check.pkg {
129                 return true
130         }
131
132         // If the source file declares its Go version, use that to decide.
133         if check.posVers != nil {
134                 if src, ok := check.posVers[base(at.Pos())]; ok && src.major >= 1 {
135                         return !src.before(v)
136                 }
137         }
138
139         // Otherwise fall back to the version in the checker.
140         return check.version.equal(go0_0) || !check.version.before(v)
141 }
142
143 // allowVersionf is like allowVersion but also accepts a format string and arguments
144 // which are used to report a version error if allowVersion returns false.
145 func (check *Checker) allowVersionf(pkg *Package, at poser, v version, format string, args ...interface{}) bool {
146         if !check.allowVersion(pkg, at, v) {
147                 check.versionErrorf(at, v, format, args...)
148                 return false
149         }
150         return true
151 }
152
153 // base finds the underlying PosBase of the source file containing pos,
154 // skipping over intermediate PosBase layers created by //line directives.
155 func base(pos syntax.Pos) *syntax.PosBase {
156         b := pos.Base()
157         for {
158                 bb := b.Pos().Base()
159                 if bb == nil || bb == b {
160                         break
161                 }
162                 b = bb
163         }
164         return b
165 }