Skip to content

Commit

Permalink
go/types, types2: implement type checking of "clear" built-in
Browse files Browse the repository at this point in the history
Will become available with Go 1.21.

Recognizing the `clear` built-in early is not causing any problems:
if existing code defines a `clear`, that will be used as before. If
code doesn't define `clear` the error message will make it clear
that with 1.21 the function will be available. It's still possible
to define a local `clear` and get rid of the error; but more likely
the name choice should be avoided going forward, so this provides a
useful early "heads-up".

For #56351.

Change-Id: I3d0fb1eb3508fbc78d7514b6238eac89610158c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/448076
Run-TryBot: Robert Griesemer <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Robert Findley <[email protected]>
Reviewed-by: Robert Griesemer <[email protected]>
Auto-Submit: Robert Griesemer <[email protected]>
  • Loading branch information
griesemer authored and gopherbot committed Nov 17, 2022
1 parent cafb49a commit 5c834a2
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/cmd/compile/internal/types2/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,33 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
x.typ = Typ[Int]
x.val = val

case _Clear:
// clear(m)
if !check.allowVersion(check.pkg, 1, 21) {
check.versionErrorf(call.Fun, "go1.21", "clear")
return
}

if !underIs(x.typ, func(u Type) bool {
switch u := u.(type) {
case *Map, *Slice:
return true
case *Pointer:
if _, ok := under(u.base).(*Array); ok {
return true
}
}
check.errorf(x, InvalidClear, invalidArg+"cannot clear %s: argument must be (or constrained by) map, slice, or array pointer", x)
return false
}) {
return
}

x.mode = novalue
if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
}

case _Close:
// close(c)
if !underIs(x.typ, func(u Type) bool {
Expand Down
5 changes: 5 additions & 0 deletions src/cmd/compile/internal/types2/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ var builtinCalls = []struct {
{"len", `type S []byte; var s S; _ = len(s)`, `func(p.S) int`},
{"len", `var s P; _ = len(s)`, `func(P) int`},

{"clear", `var m map[float64]int; clear(m)`, `func(map[float64]int)`},
{"clear", `var s []byte; clear(s)`, `func([]byte)`},
{"clear", `var p *[10]int; clear(p)`, `func(*[10]int)`},
{"clear", `var s P; clear(s)`, `func(P)`},

{"close", `var c chan int; close(c)`, `func(chan int)`},
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},

Expand Down
2 changes: 2 additions & 0 deletions src/cmd/compile/internal/types2/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ const (
// universe scope
_Append builtinId = iota
_Cap
_Clear
_Close
_Complex
_Copy
Expand Down Expand Up @@ -182,6 +183,7 @@ var predeclaredFuncs = [...]struct {
}{
_Append: {"append", 1, true, expression},
_Cap: {"cap", 1, false, expression},
_Clear: {"clear", 1, false, statement},
_Close: {"close", 1, false, statement},
_Complex: {"complex", 2, false, expression},
_Copy: {"copy", 2, false, statement},
Expand Down
27 changes: 27 additions & 0 deletions src/go/types/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,33 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
x.typ = Typ[Int]
x.val = val

case _Clear:
// clear(m)
if !check.allowVersion(check.pkg, 1, 21) {
check.error(call.Fun, UnsupportedFeature, "clear requires go1.21 or later")
return
}

if !underIs(x.typ, func(u Type) bool {
switch u := u.(type) {
case *Map, *Slice:
return true
case *Pointer:
if _, ok := under(u.base).(*Array); ok {
return true
}
}
check.errorf(x, InvalidClear, invalidArg+"cannot clear %s: argument must be (or constrained by) map, slice, or array pointer", x)
return false
}) {
return
}

x.mode = novalue
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
}

case _Close:
// close(c)
if !underIs(x.typ, func(u Type) bool {
Expand Down
5 changes: 5 additions & 0 deletions src/go/types/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ var builtinCalls = []struct {
{"len", `type S []byte; var s S; _ = len(s)`, `func(p.S) int`},
{"len", `var s P; _ = len(s)`, `func(P) int`},

{"clear", `var m map[float64]int; clear(m)`, `func(map[float64]int)`},
{"clear", `var s []byte; clear(s)`, `func([]byte)`},
{"clear", `var p *[10]int; clear(p)`, `func(*[10]int)`},
{"clear", `var s P; clear(s)`, `func(P)`},

{"close", `var c chan int; close(c)`, `func(chan int)`},
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},

Expand Down
2 changes: 2 additions & 0 deletions src/go/types/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const (
// universe scope
_Append builtinId = iota
_Cap
_Clear
_Close
_Complex
_Copy
Expand Down Expand Up @@ -183,6 +184,7 @@ var predeclaredFuncs = [...]struct {
}{
_Append: {"append", 1, true, expression},
_Cap: {"cap", 1, false, expression},
_Clear: {"clear", 1, false, statement},
_Close: {"close", 1, false, statement},
_Complex: {"complex", 2, false, expression},
_Copy: {"copy", 2, false, statement},
Expand Down
9 changes: 9 additions & 0 deletions src/internal/types/errors/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -1430,4 +1430,13 @@ const (
// InvalidUnsafeStringData occurs if it is used in a package
// compiled for a language version before go1.20.
_ // not used anymore

// InvalidClear occurs when clear is called with an argument
// that is not of map, slice, or pointer-to-array type.
//
// Example:
// func _(x int) {
// clear(x)
// }
InvalidClear
)
11 changes: 11 additions & 0 deletions src/internal/types/testdata/check/builtins0.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@ func cap3() {
)
}

func clear1() {
var a [10]int
var m map[float64]string
var s []byte
clear(a /* ERROR cannot clear a */)
clear(&a)
clear(m)
clear(s)
clear([]int{})
}

func close1() {
var c chan int
var r <-chan int
Expand Down
14 changes: 14 additions & 0 deletions src/internal/types/testdata/check/builtins1.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ package builtins

import "unsafe"

// clear

func _[T any](x T) {
clear(x /* ERROR cannot clear x */)
}

func _[T ~map[int]string | ~[]byte | ~*[10]int](x T) {
clear(x)
}

func _[T ~map[int]string | ~[]byte | ~*[10]int | string](x T) {
clear(x /* ERROR cannot clear x */)
}

// close

type C0 interface{ int }
Expand Down
11 changes: 11 additions & 0 deletions src/internal/types/testdata/fixedbugs/issue56351.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// -lang=go1.20

// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package p

func _(s []int) {
clear /* ERROR clear requires go1\.21 or later */ (s)
}

0 comments on commit 5c834a2

Please sign in to comment.