Source file 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  	"fmt"
    10  	"go/version"
    11  	"internal/goversion"
    12  	"strings"
    13  )
    14  
    15  // A goVersion is a Go language version string of the form "go1.%d"
    16  // where d is the minor version number. goVersion strings don't
    17  // contain release numbers ("go1.20.1" is not a valid goVersion).
    18  type goVersion string
    19  
    20  // asGoVersion returns v as a goVersion (e.g., "go1.20.1" becomes "go1.20").
    21  // If v is not a valid Go version, the result is the empty string.
    22  func asGoVersion(v string) goVersion {
    23  	return goVersion(version.Lang(v))
    24  }
    25  
    26  // isValid reports whether v is a valid Go version.
    27  func (v goVersion) isValid() bool {
    28  	return v != ""
    29  }
    30  
    31  // cmp returns -1, 0, or +1 depending on whether x < y, x == y, or x > y,
    32  // interpreted as Go versions.
    33  func (x goVersion) cmp(y goVersion) int {
    34  	return version.Compare(string(x), string(y))
    35  }
    36  
    37  var (
    38  	// Go versions that introduced language changes
    39  	go1_9  = asGoVersion("go1.9")
    40  	go1_13 = asGoVersion("go1.13")
    41  	go1_14 = asGoVersion("go1.14")
    42  	go1_17 = asGoVersion("go1.17")
    43  	go1_18 = asGoVersion("go1.18")
    44  	go1_20 = asGoVersion("go1.20")
    45  	go1_21 = asGoVersion("go1.21")
    46  	go1_22 = asGoVersion("go1.22")
    47  
    48  	// current (deployed) Go version
    49  	go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
    50  )
    51  
    52  // langCompat reports an error if the representation of a numeric
    53  // literal is not compatible with the current language version.
    54  func (check *Checker) langCompat(lit *syntax.BasicLit) {
    55  	s := lit.Value
    56  	if len(s) <= 2 || check.allowVersion(check.pkg, lit, go1_13) {
    57  		return
    58  	}
    59  	// len(s) > 2
    60  	if strings.Contains(s, "_") {
    61  		check.versionErrorf(lit, go1_13, "underscores in numeric literals")
    62  		return
    63  	}
    64  	if s[0] != '0' {
    65  		return
    66  	}
    67  	radix := s[1]
    68  	if radix == 'b' || radix == 'B' {
    69  		check.versionErrorf(lit, go1_13, "binary literals")
    70  		return
    71  	}
    72  	if radix == 'o' || radix == 'O' {
    73  		check.versionErrorf(lit, go1_13, "0o/0O-style octal literals")
    74  		return
    75  	}
    76  	if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') {
    77  		check.versionErrorf(lit, go1_13, "hexadecimal floating-point literals")
    78  	}
    79  }
    80  
    81  // allowVersion reports whether the given package is allowed to use version v.
    82  func (check *Checker) allowVersion(pkg *Package, at poser, v goVersion) bool {
    83  	// We assume that imported packages have all been checked,
    84  	// so we only have to check for the local package.
    85  	if pkg != check.pkg {
    86  		return true
    87  	}
    88  
    89  	// If no explicit file version is specified,
    90  	// fileVersion corresponds to the module version.
    91  	var fileVersion goVersion
    92  	if pos := at.Pos(); pos.IsKnown() {
    93  		// We need version.Lang below because file versions
    94  		// can be (unaltered) Config.GoVersion strings that
    95  		// may contain dot-release information.
    96  		fileVersion = asGoVersion(check.versions[base(pos)])
    97  	}
    98  	return !fileVersion.isValid() || fileVersion.cmp(v) >= 0
    99  }
   100  
   101  // verifyVersionf is like allowVersion but also accepts a format string and arguments
   102  // which are used to report a version error if allowVersion returns false. It uses the
   103  // current package.
   104  func (check *Checker) verifyVersionf(at poser, v goVersion, format string, args ...interface{}) bool {
   105  	if !check.allowVersion(check.pkg, at, v) {
   106  		check.versionErrorf(at, v, format, args...)
   107  		return false
   108  	}
   109  	return true
   110  }
   111  
   112  // base finds the underlying PosBase of the source file containing pos,
   113  // skipping over intermediate PosBase layers created by //line directives.
   114  // The positions must be known.
   115  func base(pos syntax.Pos) *syntax.PosBase {
   116  	assert(pos.IsKnown())
   117  	b := pos.Base()
   118  	for {
   119  		bb := b.Pos().Base()
   120  		if bb == nil || bb == b {
   121  			break
   122  		}
   123  		b = bb
   124  	}
   125  	return b
   126  }
   127  

View as plain text