mirror of https://github.com/golang/go.git
go/types, types2: implement min/max builtins
For #59488. Change-Id: I4553ab11af9179a4786dede44877f88286c168dc Reviewed-on: https://go-review.googlesource.com/c/go/+/496038 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Robert Griesemer <gri@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
c7d6c6000a
commit
b5515eef56
|
|
@ -533,6 +533,63 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
|
||||
}
|
||||
|
||||
case _Max, _Min:
|
||||
// max(x, ...)
|
||||
// min(x, ...)
|
||||
if !check.verifyVersionf(check.pkg, call.Fun, go1_21, bin.name) {
|
||||
return
|
||||
}
|
||||
|
||||
op := token.LSS
|
||||
if id == _Max {
|
||||
op = token.GTR
|
||||
}
|
||||
|
||||
for i, a := range args {
|
||||
if a.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if !allOrdered(a.typ) {
|
||||
check.errorf(a, InvalidMinMaxOperand, invalidArg+"%s cannot be ordered", a)
|
||||
return
|
||||
}
|
||||
|
||||
// The first argument is already in x and there's nothing left to do.
|
||||
if i > 0 {
|
||||
check.matchTypes(x, a)
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if !Identical(x.typ, a.typ) {
|
||||
check.errorf(a, MismatchedTypes, invalidArg+"mismatched types %s (previous argument) and %s (type of %s)", x.typ, a.typ, a.expr)
|
||||
return
|
||||
}
|
||||
|
||||
if x.mode == constant_ && a.mode == constant_ {
|
||||
if constant.Compare(a.val, op, x.val) {
|
||||
*x = *a
|
||||
}
|
||||
} else {
|
||||
x.mode = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If nargs == 1, make sure x.mode is either a value or a constant.
|
||||
if x.mode != constant_ {
|
||||
x.mode = value
|
||||
}
|
||||
|
||||
if check.recordTypes() && x.mode != constant_ {
|
||||
types := make([]Type, nargs)
|
||||
for i := range types {
|
||||
types[i] = x.typ
|
||||
}
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
|
||||
}
|
||||
|
||||
case _New:
|
||||
// new(T)
|
||||
// (no argument evaluated yet)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,18 @@ var builtinCalls = []struct {
|
|||
// go.dev/issue/45667
|
||||
{"make", `const l uint = 1; _ = make([]int, l)`, `func([]int, uint) []int`},
|
||||
|
||||
{"max", ` _ = max(0 )`, `invalid type`}, // constant
|
||||
{"max", `var x int ; _ = max(x )`, `func(int) int`},
|
||||
{"max", `var x int ; _ = max(0, x )`, `func(int, int) int`},
|
||||
{"max", `var x string ; _ = max("a", x )`, `func(string, string) string`},
|
||||
{"max", `var x float32; _ = max(0, 1.0, x)`, `func(float32, float32, float32) float32`},
|
||||
|
||||
{"min", ` _ = min(0 )`, `invalid type`}, // constant
|
||||
{"min", `var x int ; _ = min(x )`, `func(int) int`},
|
||||
{"min", `var x int ; _ = min(0, x )`, `func(int, int) int`},
|
||||
{"min", `var x string ; _ = min("a", x )`, `func(string, string) string`},
|
||||
{"min", `var x float32; _ = min(0, 1.0, x)`, `func(float32, float32, float32) float32`},
|
||||
|
||||
{"new", `_ = new(int)`, `func(int) *int`},
|
||||
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ const (
|
|||
_Imag
|
||||
_Len
|
||||
_Make
|
||||
_Max
|
||||
_Min
|
||||
_New
|
||||
_Panic
|
||||
_Print
|
||||
|
|
@ -191,6 +193,9 @@ var predeclaredFuncs = [...]struct {
|
|||
_Imag: {"imag", 1, false, expression},
|
||||
_Len: {"len", 1, false, expression},
|
||||
_Make: {"make", 1, true, expression},
|
||||
// To disable max/min, remove the next two lines.
|
||||
_Max: {"max", 1, true, expression},
|
||||
_Min: {"min", 1, true, expression},
|
||||
_New: {"new", 1, false, expression},
|
||||
_Panic: {"panic", 1, false, statement},
|
||||
_Print: {"print", 0, true, statement},
|
||||
|
|
|
|||
|
|
@ -532,6 +532,63 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
|
||||
}
|
||||
|
||||
case _Max, _Min:
|
||||
// max(x, ...)
|
||||
// min(x, ...)
|
||||
if !check.verifyVersionf(check.pkg, call.Fun, go1_21, bin.name) {
|
||||
return
|
||||
}
|
||||
|
||||
op := token.LSS
|
||||
if id == _Max {
|
||||
op = token.GTR
|
||||
}
|
||||
|
||||
for i, a := range args {
|
||||
if a.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if !allOrdered(a.typ) {
|
||||
check.errorf(a, InvalidMinMaxOperand, invalidArg+"%s cannot be ordered", a)
|
||||
return
|
||||
}
|
||||
|
||||
// The first argument is already in x and there's nothing left to do.
|
||||
if i > 0 {
|
||||
check.matchTypes(x, a)
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if !Identical(x.typ, a.typ) {
|
||||
check.errorf(a, MismatchedTypes, invalidArg+"mismatched types %s (previous argument) and %s (type of %s)", x.typ, a.typ, a.expr)
|
||||
return
|
||||
}
|
||||
|
||||
if x.mode == constant_ && a.mode == constant_ {
|
||||
if constant.Compare(a.val, op, x.val) {
|
||||
*x = *a
|
||||
}
|
||||
} else {
|
||||
x.mode = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If nargs == 1, make sure x.mode is either a value or a constant.
|
||||
if x.mode != constant_ {
|
||||
x.mode = value
|
||||
}
|
||||
|
||||
if check.recordTypes() && x.mode != constant_ {
|
||||
types := make([]Type, nargs)
|
||||
for i := range types {
|
||||
types[i] = x.typ
|
||||
}
|
||||
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
|
||||
}
|
||||
|
||||
case _New:
|
||||
// new(T)
|
||||
// (no argument evaluated yet)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,18 @@ var builtinCalls = []struct {
|
|||
// go.dev/issue/45667
|
||||
{"make", `const l uint = 1; _ = make([]int, l)`, `func([]int, uint) []int`},
|
||||
|
||||
{"max", ` _ = max(0 )`, `invalid type`}, // constant
|
||||
{"max", `var x int ; _ = max(x )`, `func(int) int`},
|
||||
{"max", `var x int ; _ = max(0, x )`, `func(int, int) int`},
|
||||
{"max", `var x string ; _ = max("a", x )`, `func(string, string) string`},
|
||||
{"max", `var x float32; _ = max(0, 1.0, x)`, `func(float32, float32, float32) float32`},
|
||||
|
||||
{"min", ` _ = min(0 )`, `invalid type`}, // constant
|
||||
{"min", `var x int ; _ = min(x )`, `func(int) int`},
|
||||
{"min", `var x int ; _ = min(0, x )`, `func(int, int) int`},
|
||||
{"min", `var x string ; _ = min("a", x )`, `func(string, string) string`},
|
||||
{"min", `var x float32; _ = min(0, 1.0, x)`, `func(float32, float32, float32) float32`},
|
||||
|
||||
{"new", `_ = new(int)`, `func(int) *int`},
|
||||
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ const (
|
|||
_Imag
|
||||
_Len
|
||||
_Make
|
||||
_Max
|
||||
_Min
|
||||
_New
|
||||
_Panic
|
||||
_Print
|
||||
|
|
@ -193,6 +195,9 @@ var predeclaredFuncs = [...]struct {
|
|||
_Imag: {"imag", 1, false, expression},
|
||||
_Len: {"len", 1, false, expression},
|
||||
_Make: {"make", 1, true, expression},
|
||||
// To disable max/min, remove the next two lines.
|
||||
_Max: {"max", 1, true, expression},
|
||||
_Min: {"min", 1, true, expression},
|
||||
_New: {"new", 1, false, expression},
|
||||
_Panic: {"panic", 1, false, statement},
|
||||
_Print: {"print", 0, true, statement},
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ func _() {
|
|||
_ = x[InvalidUnsafeString-146]
|
||||
_ = x[InvalidClear-148]
|
||||
_ = x[TypeTooLarge-149]
|
||||
_ = x[InvalidMinMaxOperand-150]
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -162,7 +163,7 @@ const (
|
|||
_Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot"
|
||||
_Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl"
|
||||
_Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
|
||||
_Code_name_5 = "InvalidClearTypeTooLarge"
|
||||
_Code_name_5 = "InvalidClearTypeTooLargeInvalidMinMaxOperand"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -170,7 +171,7 @@ var (
|
|||
_Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738, 756}
|
||||
_Code_index_3 = [...]uint16{0, 16, 31, 44, 54, 66, 77, 91, 104, 115, 125, 140, 151, 162, 175, 191, 208, 232, 249, 264, 274, 283, 296, 312, 328, 339, 354}
|
||||
_Code_index_4 = [...]uint16{0, 14, 30, 44, 61, 81, 94, 110, 124, 141, 158, 175, 190, 204, 218, 229, 241, 254, 271, 284, 295, 308, 320, 329, 336, 348, 364, 382, 400, 415, 432, 451, 465, 485, 497, 521, 544, 562, 584, 603}
|
||||
_Code_index_5 = [...]uint8{0, 12, 24}
|
||||
_Code_index_5 = [...]uint8{0, 12, 24, 44}
|
||||
)
|
||||
|
||||
func (i Code) String() string {
|
||||
|
|
@ -189,7 +190,7 @@ func (i Code) String() string {
|
|||
case 108 <= i && i <= 146:
|
||||
i -= 108
|
||||
return _Code_name_4[_Code_index_4[i]:_Code_index_4[i+1]]
|
||||
case 148 <= i && i <= 149:
|
||||
case 148 <= i && i <= 150:
|
||||
i -= 148
|
||||
return _Code_name_5[_Code_index_5[i]:_Code_index_5[i+1]]
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1462,4 +1462,16 @@ const (
|
|||
// }
|
||||
// var _ = unsafe.Offsetof(s.x)
|
||||
TypeTooLarge
|
||||
|
||||
// InvalidMinMaxOperand occurs if min or max is called
|
||||
// with an operand that cannot be ordered because it
|
||||
// does not support the < operator.
|
||||
//
|
||||
// Example:
|
||||
// const _ = min(true)
|
||||
//
|
||||
// Example:
|
||||
// var s, t []byte
|
||||
// var _ = max(s, t)
|
||||
InvalidMinMaxOperand
|
||||
)
|
||||
|
|
|
|||
|
|
@ -498,6 +498,114 @@ func make2() {
|
|||
_ = make(f1 /* ERROR "not a type" */ ())
|
||||
}
|
||||
|
||||
func max1() {
|
||||
var b bool
|
||||
var c complex128
|
||||
var x int
|
||||
var s string
|
||||
type myint int
|
||||
var m myint
|
||||
_ = max() /* ERROR "not enough arguments" */
|
||||
_ = max(b /* ERROR "cannot be ordered" */ )
|
||||
_ = max(c /* ERROR "cannot be ordered" */ )
|
||||
_ = max(x)
|
||||
_ = max(s)
|
||||
_ = max(x, x)
|
||||
_ = max(x, x, x, x, x)
|
||||
var _ int = max /* ERROR "cannot use max(m) (value of type myint) as int value" */ (m)
|
||||
_ = max(x, m /* ERROR "invalid argument: mismatched types int (previous argument) and myint (type of m)" */ , x)
|
||||
|
||||
_ = max(1, x)
|
||||
_ = max(1.0, x)
|
||||
_ = max(1.2 /* ERROR "1.2 (untyped float constant) truncated to int" */ , x)
|
||||
_ = max(-10, 1.0, c /* ERROR "cannot be ordered" */ )
|
||||
|
||||
const (
|
||||
_ = max /* ERROR "max(x) (value of type int) is not constant" */ (x)
|
||||
_ = max(true /* ERROR "invalid argument: true (untyped bool constant) cannot be ordered" */ )
|
||||
_ = max(1)
|
||||
_ = max(1, 2.3, 'a')
|
||||
_ = max(1, "foo" /* ERROR "mismatched types" */ )
|
||||
_ = max(1, 0i /* ERROR "cannot be ordered" */ )
|
||||
_ = max(1, 2 /* ERROR "cannot be ordered" */ + 3i )
|
||||
)
|
||||
}
|
||||
|
||||
func max2() {
|
||||
_ = assert(max(0) == 0)
|
||||
_ = assert(max(0, 1) == 1)
|
||||
_ = assert(max(0, -10, 123456789) == 123456789)
|
||||
_ = assert(max(-12345678901234567890, 0) == 0)
|
||||
|
||||
_ = assert(max(1, 2.3) == 2.3)
|
||||
_ = assert(max(1, 2.3, 'a') == 'a')
|
||||
|
||||
_ = assert(max("", "a") == "a")
|
||||
_ = assert(max("abcde", "xyz", "foo", "bar") == "xyz")
|
||||
|
||||
const (
|
||||
_ int = max(1.0)
|
||||
_ float32 = max(1, 2)
|
||||
_ int = max /* ERROR "cannot use max(1, 2.3) (untyped float constant 2.3) as int value" */ (1, 2.3)
|
||||
_ int = max(1.2, 3) // ok!
|
||||
_ byte = max(1, 'a')
|
||||
)
|
||||
}
|
||||
|
||||
func min1() {
|
||||
var b bool
|
||||
var c complex128
|
||||
var x int
|
||||
var s string
|
||||
type myint int
|
||||
var m myint
|
||||
_ = min() /* ERROR "not enough arguments" */
|
||||
_ = min(b /* ERROR "cannot be ordered" */ )
|
||||
_ = min(c /* ERROR "cannot be ordered" */ )
|
||||
_ = min(x)
|
||||
_ = min(s)
|
||||
_ = min(x, x)
|
||||
_ = min(x, x, x, x, x)
|
||||
var _ int = min /* ERROR "cannot use min(m) (value of type myint) as int value" */ (m)
|
||||
_ = min(x, m /* ERROR "invalid argument: mismatched types int (previous argument) and myint (type of m)" */ , x)
|
||||
|
||||
_ = min(1, x)
|
||||
_ = min(1.0, x)
|
||||
_ = min(1.2 /* ERROR "1.2 (untyped float constant) truncated to int" */ , x)
|
||||
_ = min(-10, 1.0, c /* ERROR "cannot be ordered" */ )
|
||||
|
||||
const (
|
||||
_ = min /* ERROR "min(x) (value of type int) is not constant" */ (x)
|
||||
_ = min(true /* ERROR "invalid argument: true (untyped bool constant) cannot be ordered" */ )
|
||||
_ = min(1)
|
||||
_ = min(1, 2.3, 'a')
|
||||
_ = min(1, "foo" /* ERROR "mismatched types" */ )
|
||||
_ = min(1, 0i /* ERROR "cannot be ordered" */ )
|
||||
_ = min(1, 2 /* ERROR "cannot be ordered" */ + 3i )
|
||||
)
|
||||
}
|
||||
|
||||
func min2() {
|
||||
_ = assert(min(0) == 0)
|
||||
_ = assert(min(0, 1) == 0)
|
||||
_ = assert(min(0, -10, 123456789) == -10)
|
||||
_ = assert(min(-12345678901234567890, 0) == -12345678901234567890)
|
||||
|
||||
_ = assert(min(1, 2.3) == 1)
|
||||
_ = assert(min(1, 2.3, 'a') == 1)
|
||||
|
||||
_ = assert(min("", "a") == "")
|
||||
_ = assert(min("abcde", "xyz", "foo", "bar") == "abcde")
|
||||
|
||||
const (
|
||||
_ int = min(1.0)
|
||||
_ float32 = min(1, 2)
|
||||
_ int = min(1, 2.3) // ok!
|
||||
_ int = min /* ERROR "cannot use min(1.2, 3) (untyped float constant 1.2) as int value" */ (1.2, 3)
|
||||
_ byte = min(1, 'a')
|
||||
)
|
||||
}
|
||||
|
||||
func new1() {
|
||||
_ = new() // ERROR "not enough arguments"
|
||||
_ = new(1, 2) // ERROR "too many arguments"
|
||||
|
|
|
|||
|
|
@ -182,6 +182,48 @@ func _[
|
|||
_ = make(C3)
|
||||
}
|
||||
|
||||
// max
|
||||
|
||||
func _[
|
||||
P1 ~int|~float64,
|
||||
P2 ~int|~string|~uint,
|
||||
P3 ~int|bool,
|
||||
]() {
|
||||
var x1 P1
|
||||
_ = max(x1)
|
||||
_ = max(x1, x1)
|
||||
_ = max(1, x1, 2)
|
||||
const _ = max /* ERROR "max(1, x1, 2) (value of type P1 constrained by ~int | ~float64) is not constant" */ (1, x1, 2)
|
||||
|
||||
var x2 P2
|
||||
_ = max(x2)
|
||||
_ = max(x2, x2)
|
||||
_ = max(1, 2 /* ERROR "cannot convert 2 (untyped int constant) to type P2" */, x2) // error at 2 because max is 2
|
||||
|
||||
_ = max(x1, x2 /* ERROR "mismatched types P1 (previous argument) and P2 (type of x2)" */ )
|
||||
}
|
||||
|
||||
// min
|
||||
|
||||
func _[
|
||||
P1 ~int|~float64,
|
||||
P2 ~int|~string|~uint,
|
||||
P3 ~int|bool,
|
||||
]() {
|
||||
var x1 P1
|
||||
_ = min(x1)
|
||||
_ = min(x1, x1)
|
||||
_ = min(1, x1, 2)
|
||||
const _ = min /* ERROR "min(1, x1, 2) (value of type P1 constrained by ~int | ~float64) is not constant" */ (1, x1, 2)
|
||||
|
||||
var x2 P2
|
||||
_ = min(x2)
|
||||
_ = min(x2, x2)
|
||||
_ = min(1 /* ERROR "cannot convert 1 (untyped int constant) to type P2" */ , 2, x2) // error at 1 because min is 1
|
||||
|
||||
_ = min(x1, x2 /* ERROR "mismatched types P1 (previous argument) and P2 (type of x2)" */ )
|
||||
}
|
||||
|
||||
// unsafe.Alignof
|
||||
|
||||
func _[T comparable]() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue