]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types: implement unsafe.Add and unsafe.Slice
authorMatthew Dempsky <mdempsky@google.com>
Wed, 21 Apr 2021 10:29:18 +0000 (03:29 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Fri, 23 Apr 2021 00:40:48 +0000 (00:40 +0000)
Updates #19367.
Updates #40481.

Change-Id: Id2b2d2e3e716f91f0dd9e5102689a1ba90a819e4
Reviewed-on: https://go-review.googlesource.com/c/go/+/312213
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/go/types/builtins.go
src/go/types/builtins_test.go
src/go/types/check.go
src/go/types/errorcodes.go
src/go/types/universe.go

index 3df82fffdae06e84d91b721e246d62a3c454f615..9c5a0b5842c15ad5aa97faf47e21e1e374986b3e 100644 (file)
@@ -586,6 +586,25 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                        check.recordBuiltinType(call.Fun, makeSig(x.typ))
                }
 
+       case _Add:
+               // unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
+               check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add")
+               if x.mode == invalid {
+                       return
+               }
+
+               var y operand
+               arg(&y, 1)
+               if !check.isValidIndex(&y, _InvalidUnsafeAdd, "length", true) {
+                       return
+               }
+
+               x.mode = value
+               x.typ = Typ[UnsafePointer]
+               if check.Types != nil {
+                       check.recordBuiltinType(call.Fun, makeSig(x.typ, x.typ, y.typ))
+               }
+
        case _Alignof:
                // unsafe.Alignof(x T) uintptr
                if asTypeParam(x.typ) != nil {
@@ -663,6 +682,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                x.typ = Typ[Uintptr]
                // result is constant - no need to record signature
 
+       case _Slice:
+               // unsafe.Slice(ptr *T, len IntegerType) []T
+               typ := asPointer(x.typ)
+               if typ == nil {
+                       check.invalidArg(x, _InvalidUnsafeSlice, "%s is not a pointer", x)
+                       return
+               }
+
+               var y operand
+               arg(&y, 1)
+               if !check.isValidIndex(&y, _InvalidUnsafeSlice, "length", false) {
+                       return
+               }
+
+               x.mode = value
+               x.typ = NewSlice(typ.base)
+               if check.Types != nil {
+                       check.recordBuiltinType(call.Fun, makeSig(x.typ, typ, y.typ))
+               }
+
        case _Assert:
                // assert(pred) causes a typechecker error if pred is false.
                // The result of assert is the value of pred if there is no error.
index e9ffd2850852c45673169299809e93f1f39be202..11de9a1ac1a45d17bba26ed2f41bdb7499015858 100644 (file)
@@ -107,6 +107,10 @@ var builtinCalls = []struct {
        {"recover", `recover()`, `func() interface{}`},
        {"recover", `_ = recover()`, `func() interface{}`},
 
+       {"Add", `var p unsafe.Pointer; _ = unsafe.Add(p, -1.0)`, `func(unsafe.Pointer, int) unsafe.Pointer`},
+       {"Add", `var p unsafe.Pointer; var n uintptr; _ = unsafe.Add(p, n)`, `func(unsafe.Pointer, uintptr) unsafe.Pointer`},
+       {"Add", `_ = unsafe.Add(nil, 0)`, `func(unsafe.Pointer, int) unsafe.Pointer`},
+
        {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`},                 // constant
        {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
 
@@ -116,6 +120,9 @@ var builtinCalls = []struct {
        {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`},                 // constant
        {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
 
+       {"Slice", `var p *int; _ = unsafe.Slice(p, 1)`, `func(*int, int) []int`},
+       {"Slice", `var p *byte; var n uintptr; _ = unsafe.Slice(p, n)`, `func(*byte, uintptr) []byte`},
+
        {"assert", `assert(true)`, `invalid type`},                                    // constant
        {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
 
index b28481d7bd683a116f4c8f9404e1692afad7192f..2c8f683ad511ab8520f785259b93b09650368668 100644 (file)
@@ -329,14 +329,14 @@ func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type,
 }
 
 func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
-       // f must be a (possibly parenthesized) identifier denoting a built-in
-       // (built-ins in package unsafe always produce a constant result and
-       // we don't record their signatures, so we don't see qualified idents
-       // here): record the signature for f and possible children.
+       // f must be a (possibly parenthesized, possibly qualified)
+       // identifier denoting a built-in (including unsafe's non-constant
+       // functions Add and Slice): record the signature for f and possible
+       // children.
        for {
                check.recordTypeAndValue(f, builtin, sig, nil)
                switch p := f.(type) {
-               case *ast.Ident:
+               case *ast.Ident, *ast.SelectorExpr:
                        return // we're done
                case *ast.ParenExpr:
                        f = p.X
index 257fc4eff4a23b73d5e40dce908a88bdcd4ce802..1106cd986fea41f47c54ab53713258693d26359e 100644 (file)
@@ -883,6 +883,45 @@ const (
        //  var _ = real(int(1))
        _InvalidReal
 
+       // _InvalidUnsafeAdd occurs when unsafe.Add is called with a
+       // length argument that is not of integer type.
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  var p unsafe.Pointer
+       //  var _ = unsafe.Add(p, float64(1))
+       _InvalidUnsafeAdd
+
+       // _InvalidUnsafeSlice occurs when unsafe.Slice is called with a
+       // pointer argument that is not of pointer type or a length argument
+       // that is not of integer type, negative, or out of bounds.
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  var x int
+       //  var _ = unsafe.Slice(x, 1)
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  var x int
+       //  var _ = unsafe.Slice(&x, float64(1))
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  var x int
+       //  var _ = unsafe.Slice(&x, -1)
+       //
+       // Example:
+       //  import "unsafe"
+       //
+       //  var x int
+       //  var _ = unsafe.Slice(&x, uint64(1) << 63)
+       _InvalidUnsafeSlice
+
        /* exprs > assertion */
 
        // _InvalidAssert occurs when a type assertion is applied to a
index 4ced018f8e84c753b9a565876ba4e39dce1bd3f3..7c211fa6f7e4add4026f29dc454f7d2f72331f4d 100644 (file)
@@ -137,9 +137,11 @@ const (
        _Recover
 
        // package unsafe
+       _Add
        _Alignof
        _Offsetof
        _Sizeof
+       _Slice
 
        // testing support
        _Assert
@@ -168,9 +170,11 @@ var predeclaredFuncs = [...]struct {
        _Real:    {"real", 1, false, expression},
        _Recover: {"recover", 0, false, statement},
 
+       _Add:      {"Add", 2, false, expression},
        _Alignof:  {"Alignof", 1, false, expression},
        _Offsetof: {"Offsetof", 1, false, expression},
        _Sizeof:   {"Sizeof", 1, false, expression},
+       _Slice:    {"Slice", 2, false, expression},
 
        _Assert: {"assert", 1, false, statement},
        _Trace:  {"trace", 0, true, statement},