[dev.boringcrypto] all: merge master into dev.boringcrypto

Change-Id: I18dbf4f9fa7e2334fccedd862a523126cf38164e
This commit is contained in:
Chressie Himpel 2022-02-03 19:10:54 +01:00
commit e14fee553a
1603 changed files with 47792 additions and 24800 deletions

View file

@ -1,6 +1,11 @@
---
name: Bugs
about: The go command, standard library, or anything else
title: "affected/package: "
---
<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->
### What version of Go are you using (`go version`)?
@ -26,7 +31,7 @@ $ go env
<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
A link on go.dev/play is best.
-->
@ -36,3 +41,5 @@ A link on play.golang.org is best.
### What did you see instead?

47
.github/ISSUE_TEMPLATE/01-pkgsite.md vendored Normal file
View file

@ -0,0 +1,47 @@
---
name: Pkg.go.dev bugs or feature requests
about: Issues or feature requests for the documentation site
title: "x/pkgsite: "
labels: pkgsite
---
<!--
Please answer these questions before submitting your issue. Thanks!
-->
### What is the URL of the page with the issue?
### What is your user agent?
<!--
You can find your user agent here:
https://www.google.com/search?q=what+is+my+user+agent
-->
### Screenshot
<!--
Please paste a screenshot of the page.
-->
### What did you do?
<!--
If possible, provide a recipe for reproducing the error.
-->
### What did you expect to see?
### What did you see instead?

View file

@ -0,0 +1,39 @@
---
name: Pkg.go.dev package removal request
about: Request a package be removed from the documentation site (pkg.go.dev)
title: "x/pkgsite: package removal request for [type path here]"
labels: pkgsite
---
<!--
Please answer these questions before submitting your issue. Thanks!
-->
### What is the path of the package that you would like to have removed?
<!---
We can remove packages with a shared path prefix.
For example, a request for "github.com/author" would remove all pkg.go.dev pages with that package path prefix.
--->
### Are you the owner of this package?
<!---
Only the package owners can request to have their packages removed from pkg.go.dev.
--->
### What is the reason that you could not retract this package instead?
<!---
If you would like to have your module removed from pkg.go.dev, we recommend that you retract them, so that they can be removed from the go command and proxy.golang.org as well.
Retracting a module version involves adding a retract directive to your go.mod file and publishing a new version. For example: https://github.com/jba/retract-demo/blob/main/go.mod#L5-L8
See https://pkg.go.dev/about#removing-a-package for additional tips on retractions.
--->

61
.github/ISSUE_TEMPLATE/03-gopls.md vendored Normal file
View file

@ -0,0 +1,61 @@
---
name: Gopls bugs or feature requests
about: Issues or feature requests for the Go language server (gopls)
title: "x/tools/gopls: "
labels: gopls Tools
---
<!--
Please answer these questions before submitting your issue. Thanks!
-->
### gopls version
<!--
Output of `gopls -v version` on the command line
-->
### go env
<!--
Output of `go env` on the command line in your workspace directory
-->
### What did you do?
<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on go.dev/play is better.
A failing unit test is the best.
-->
### What did you expect to see?
### What did you see instead?
### Editor and settings
<!--
Your editor and any settings you have configured (for example, your VSCode settings.json file)
-->
### Logs
<!--
If possible please include gopls logs. Instructions for capturing them can be found here:
https://github.com/golang/tools/blob/master/gopls/doc/troubleshooting.md#capture-logs
-->

13
.github/ISSUE_TEMPLATE/10-proposal.md vendored Normal file
View file

@ -0,0 +1,13 @@
---
name: Proposals
about: New external API or other notable changes
title: "proposal: affected/package: "
labels: Proposal
---
<!--
Our proposal process is documented here:
https://go.dev/s/proposal-process
-->

View file

@ -0,0 +1,52 @@
---
name: Language Change Proposals
about: Changes to the language
title: "proposal: Go 2: "
labels: Proposal Go2 LanguageChange
---
<!--
Our process for evaluating language changes can be found here:
https://go.googlesource.com/proposal/+/refs/heads/master#language-changes
-->
### Author background
- **Would you consider yourself a novice, intermediate, or experienced Go programmer?**
- **What other languages do you have experience with?**
### Related proposals
- **Has this idea, or one like it, been proposed before?**
- **If so, how does this proposal differ?**
- **Does this affect error handling?**
- **If so, how does this differ from previous error handling proposals?**
- **Is this about generics?**
- **If so, how does this relate to the accepted design and other generics proposals?**
### Proposal
- **What is the proposed change?**
- **Who does this proposal help, and why?**
- **Please describe as precisely as possible the change to the language.**
- **What would change in the language spec?**
- **Please also describe the change informally, as in a class teaching Go.**
- **Is this change backward compatible?**
- Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit.
Show example code before and after the change.
- **Before**
- **After**
- **Orthogonality: how does this change interact or overlap with existing features?**
- **Is the goal of this change a performance improvement?**
- **If so, what quantifiable improvement should we expect?**
- **How would we measure it?**
### Costs
- **Would this change make Go easier or harder to learn, and why?**
- **What is the cost of this proposal? (Every language change has a cost).**
- **How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?**
- **What is the compile time cost?**
- **What is the run time cost?**
- **Can you describe a possible implementation?**
- **Do you have a prototype? (This is not required.)**

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions
about: Please use one of the forums for questions or general discussions
url: https://go.dev/wiki/Questions

View file

@ -209,6 +209,7 @@ Benjamin Hsieh <tanookiben@users.noreply.github.com>
Benny Siegert <bsiegert@gmail.com>
Benoit Sigoure <tsunanet@gmail.com>
Berengar Lehr <berengar.lehr@gmx.de>
Bharath Kumar Uppala <uppala.bharath@gmail.com>
Bill Zissimopoulos <billziss@navimatics.com>
Billie Harold Cleek <bhcleek@gmail.com>
Bjorn Tillenius <bjorn@tillenius.me>
@ -262,6 +263,7 @@ Casey Callendrello <squeed@gmail.com>
Casey Marshall <casey.marshall@gmail.com>
Cezar Sá Espinola <cezarsa@gmail.com>
ChaiShushan <chaishushan@gmail.com>
Chaoqun Han <hanssccv@gmail.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charles L. Dorian <cldorian@gmail.com>
Charles Lee <zombie.fml@gmail.com>
@ -1479,6 +1481,7 @@ Zemanta d.o.o.
Zev Goldstein <zev.goldstein@gmail.com>
Zheng Dayu <davidzheng23@gmail.com>
Zhongtao Chen <chenzhongtao@126.com>
Zhou Guangyuan <zhouguangyuan.xian@gmail.com>
Zhou Peng <p@ctriple.cn>
Ziad Hatahet <hatahet@gmail.com>
Zizhao Zhang <btw515wolf2@gmail.com>

View file

@ -368,6 +368,7 @@ Benny Siegert <bsiegert@gmail.com>
Benoit Sigoure <tsunanet@gmail.com>
Berengar Lehr <Berengar.Lehr@gmx.de>
Berkant Ipek <41230766+0xbkt@users.noreply.github.com>
Bharath Kumar Uppala <uppala.bharath@gmail.com>
Bharath Thiruveedula <tbharath91@gmail.com>
Bhavin Gandhi <bhavin7392@gmail.com>
Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@gmail.com>
@ -475,6 +476,7 @@ ChaiShushan <chaishushan@gmail.com>
Changkun Ou <hi@changkun.us>
Channing Kimble-Brown <channing@golang.org>
Chao Xu <xuchao@google.com>
Chaoqun Han <hanssccv@gmail.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charles Kenney <charlesc.kenney@gmail.com>
Charles L. Dorian <cldorian@gmail.com>
@ -2746,6 +2748,7 @@ Zhengyu He <hzy@google.com>
Zhongpeng Lin <zplin@uber.com>
Zhongtao Chen <chenzhongtao@126.com>
Zhongwei Yao <zhongwei.yao@arm.com>
Zhou Guangyuan <zhouguangyuan.xian@gmail.com>
Zhou Peng <p@ctriple.cn>
Ziad Hatahet <hatahet@gmail.com>
Ziheng Liu <lzhfromustc@gmail.com>

239
api/go1.18.txt Normal file
View file

@ -0,0 +1,239 @@
pkg bufio, method (*Writer) AvailableBuffer() []uint8
pkg bufio, method (ReadWriter) AvailableBuffer() []uint8
pkg bytes, func Cut([]uint8, []uint8) ([]uint8, []uint8, bool)
pkg constraints, type Complex interface {}
pkg constraints, type Float interface {}
pkg constraints, type Integer interface {}
pkg constraints, type Ordered interface {}
pkg constraints, type Signed interface {}
pkg constraints, type Unsigned interface {}
pkg crypto/tls, method (*Conn) NetConn() net.Conn
pkg debug/buildinfo, func Read(io.ReaderAt) (*debug.BuildInfo, error)
pkg debug/buildinfo, func ReadFile(string) (*debug.BuildInfo, error)
pkg debug/buildinfo, type BuildInfo = debug.BuildInfo
pkg debug/elf, const R_PPC64_RELATIVE = 22
pkg debug/elf, const R_PPC64_RELATIVE R_PPC64
pkg debug/dwarf, type BasicType struct, DataBitOffset int64
pkg debug/dwarf, type StructField struct, DataBitOffset int64
pkg debug/plan9obj, var ErrNoSymbols error
pkg go/ast, method (*IndexListExpr) End() token.Pos
pkg go/ast, method (*IndexListExpr) Pos() token.Pos
pkg go/ast, type FuncType struct, TypeParams *FieldList
pkg go/ast, type IndexListExpr struct
pkg go/ast, type IndexListExpr struct, Indices []Expr
pkg go/ast, type IndexListExpr struct, Lbrack token.Pos
pkg go/ast, type IndexListExpr struct, Rbrack token.Pos
pkg go/ast, type IndexListExpr struct, X Expr
pkg go/ast, type TypeSpec struct, TypeParams *FieldList
pkg go/constant, method (Kind) String() string
pkg go/token, const TILDE = 88
pkg go/token, const TILDE Token
pkg go/types, func Instantiate(*Context, Type, []Type, bool) (Type, error)
pkg go/types, func NewContext() *Context
pkg go/types, func NewSignatureType(*Var, []*TypeParam, []*TypeParam, *Tuple, *Tuple, bool) *Signature
pkg go/types, func NewTerm(bool, Type) *Term
pkg go/types, func NewTypeParam(*TypeName, Type) *TypeParam
pkg go/types, func NewUnion([]*Term) *Union
pkg go/types, method (*ArgumentError) Error() string
pkg go/types, method (*ArgumentError) Unwrap() error
pkg go/types, method (*Interface) IsComparable() bool
pkg go/types, method (*Interface) IsImplicit() bool
pkg go/types, method (*Interface) IsMethodSet() bool
pkg go/types, method (*Interface) MarkImplicit()
pkg go/types, method (*Named) Origin() *Named
pkg go/types, method (*Named) SetTypeParams([]*TypeParam)
pkg go/types, method (*Named) TypeArgs() *TypeList
pkg go/types, method (*Named) TypeParams() *TypeParamList
pkg go/types, method (*Signature) RecvTypeParams() *TypeParamList
pkg go/types, method (*Signature) TypeParams() *TypeParamList
pkg go/types, method (*Term) String() string
pkg go/types, method (*Term) Tilde() bool
pkg go/types, method (*Term) Type() Type
pkg go/types, method (*TypeList) At(int) Type
pkg go/types, method (*TypeList) Len() int
pkg go/types, method (*TypeParam) Constraint() Type
pkg go/types, method (*TypeParam) Index() int
pkg go/types, method (*TypeParam) Obj() *TypeName
pkg go/types, method (*TypeParam) SetConstraint(Type)
pkg go/types, method (*TypeParam) String() string
pkg go/types, method (*TypeParam) Underlying() Type
pkg go/types, method (*TypeParamList) At(int) *TypeParam
pkg go/types, method (*TypeParamList) Len() int
pkg go/types, method (*Union) Len() int
pkg go/types, method (*Union) String() string
pkg go/types, method (*Union) Term(int) *Term
pkg go/types, method (*Union) Underlying() Type
pkg go/types, type ArgumentError struct
pkg go/types, type ArgumentError struct, Err error
pkg go/types, type ArgumentError struct, Index int
pkg go/types, type Config struct, Context *Context
pkg go/types, type Config struct, GoVersion string
pkg go/types, type Context struct
pkg go/types, type Info struct, Instances map[*ast.Ident]Instance
pkg go/types, type Instance struct
pkg go/types, type Instance struct, Type Type
pkg go/types, type Instance struct, TypeArgs *TypeList
pkg go/types, type Term struct
pkg go/types, type TypeList struct
pkg go/types, type TypeParam struct
pkg go/types, type TypeParamList struct
pkg go/types, type Union struct
pkg net, func TCPAddrFromAddrPort(netip.AddrPort) *TCPAddr
pkg net, func UDPAddrFromAddrPort(netip.AddrPort) *UDPAddr
pkg net, method (*Resolver) LookupNetIP(context.Context, string, string) ([]netip.Addr, error)
pkg net, method (*TCPAddr) AddrPort() netip.AddrPort
pkg net, method (*UDPAddr) AddrPort() netip.AddrPort
pkg net, method (*UDPConn) ReadFromUDPAddrPort([]uint8) (int, netip.AddrPort, error)
pkg net, method (*UDPConn) ReadMsgUDPAddrPort([]uint8, []uint8) (int, int, int, netip.AddrPort, error)
pkg net, method (*UDPConn) WriteMsgUDPAddrPort([]uint8, []uint8, netip.AddrPort) (int, int, error)
pkg net, method (*UDPConn) WriteToUDPAddrPort([]uint8, netip.AddrPort) (int, error)
pkg net/http, func MaxBytesHandler(Handler, int64) Handler
pkg net/http, method (*Cookie) Valid() error
pkg net/netip, func AddrFrom16([16]uint8) Addr
pkg net/netip, func AddrFrom4([4]uint8) Addr
pkg net/netip, func AddrFromSlice([]uint8) (Addr, bool)
pkg net/netip, func AddrPortFrom(Addr, uint16) AddrPort
pkg net/netip, func IPv4Unspecified() Addr
pkg net/netip, func IPv6LinkLocalAllNodes() Addr
pkg net/netip, func IPv6Unspecified() Addr
pkg net/netip, func MustParseAddr(string) Addr
pkg net/netip, func MustParseAddrPort(string) AddrPort
pkg net/netip, func MustParsePrefix(string) Prefix
pkg net/netip, func ParseAddr(string) (Addr, error)
pkg net/netip, func ParseAddrPort(string) (AddrPort, error)
pkg net/netip, func ParsePrefix(string) (Prefix, error)
pkg net/netip, func PrefixFrom(Addr, int) Prefix
pkg net/netip, method (*Addr) UnmarshalBinary([]uint8) error
pkg net/netip, method (*Addr) UnmarshalText([]uint8) error
pkg net/netip, method (*AddrPort) UnmarshalBinary([]uint8) error
pkg net/netip, method (*AddrPort) UnmarshalText([]uint8) error
pkg net/netip, method (*Prefix) UnmarshalBinary([]uint8) error
pkg net/netip, method (*Prefix) UnmarshalText([]uint8) error
pkg net/netip, method (Addr) AppendTo([]uint8) []uint8
pkg net/netip, method (Addr) As16() [16]uint8
pkg net/netip, method (Addr) As4() [4]uint8
pkg net/netip, method (Addr) AsSlice() []uint8
pkg net/netip, method (Addr) BitLen() int
pkg net/netip, method (Addr) Compare(Addr) int
pkg net/netip, method (Addr) Is4() bool
pkg net/netip, method (Addr) Is4In6() bool
pkg net/netip, method (Addr) Is6() bool
pkg net/netip, method (Addr) IsGlobalUnicast() bool
pkg net/netip, method (Addr) IsInterfaceLocalMulticast() bool
pkg net/netip, method (Addr) IsLinkLocalMulticast() bool
pkg net/netip, method (Addr) IsLinkLocalUnicast() bool
pkg net/netip, method (Addr) IsLoopback() bool
pkg net/netip, method (Addr) IsMulticast() bool
pkg net/netip, method (Addr) IsPrivate() bool
pkg net/netip, method (Addr) IsUnspecified() bool
pkg net/netip, method (Addr) IsValid() bool
pkg net/netip, method (Addr) Less(Addr) bool
pkg net/netip, method (Addr) MarshalBinary() ([]uint8, error)
pkg net/netip, method (Addr) MarshalText() ([]uint8, error)
pkg net/netip, method (Addr) Next() Addr
pkg net/netip, method (Addr) Prefix(int) (Prefix, error)
pkg net/netip, method (Addr) Prev() Addr
pkg net/netip, method (Addr) String() string
pkg net/netip, method (Addr) StringExpanded() string
pkg net/netip, method (Addr) Unmap() Addr
pkg net/netip, method (Addr) WithZone(string) Addr
pkg net/netip, method (Addr) Zone() string
pkg net/netip, method (AddrPort) Addr() Addr
pkg net/netip, method (AddrPort) AppendTo([]uint8) []uint8
pkg net/netip, method (AddrPort) IsValid() bool
pkg net/netip, method (AddrPort) MarshalBinary() ([]uint8, error)
pkg net/netip, method (AddrPort) MarshalText() ([]uint8, error)
pkg net/netip, method (AddrPort) Port() uint16
pkg net/netip, method (AddrPort) String() string
pkg net/netip, method (Prefix) Addr() Addr
pkg net/netip, method (Prefix) AppendTo([]uint8) []uint8
pkg net/netip, method (Prefix) Bits() int
pkg net/netip, method (Prefix) Contains(Addr) bool
pkg net/netip, method (Prefix) IsSingleIP() bool
pkg net/netip, method (Prefix) IsValid() bool
pkg net/netip, method (Prefix) MarshalBinary() ([]uint8, error)
pkg net/netip, method (Prefix) MarshalText() ([]uint8, error)
pkg net/netip, method (Prefix) Masked() Prefix
pkg net/netip, method (Prefix) Overlaps(Prefix) bool
pkg net/netip, method (Prefix) String() string
pkg net/netip, type Addr struct
pkg net/netip, type AddrPort struct
pkg net/netip, type Prefix struct
pkg reflect, const Pointer = 22
pkg reflect, const Pointer Kind
pkg reflect, func PointerTo(Type) Type
pkg reflect, method (*MapIter) Reset(Value)
pkg reflect, method (Value) CanComplex() bool
pkg reflect, method (Value) CanFloat() bool
pkg reflect, method (Value) CanInt() bool
pkg reflect, method (Value) CanUint() bool
pkg reflect, method (Value) FieldByIndexErr([]int) (Value, error)
pkg reflect, method (Value) SetIterKey(*MapIter)
pkg reflect, method (Value) SetIterValue(*MapIter)
pkg reflect, method (Value) UnsafePointer() unsafe.Pointer
pkg runtime/debug, method (*BuildInfo) MarshalText() ([]uint8, error)
pkg runtime/debug, method (*BuildInfo) UnmarshalText([]uint8) error
pkg runtime/debug, type BuildInfo struct, GoVersion string
pkg runtime/debug, type BuildInfo struct, Settings []BuildSetting
pkg runtime/debug, type BuildSetting struct
pkg runtime/debug, type BuildSetting struct, Key string
pkg runtime/debug, type BuildSetting struct, Value string
pkg strings, func Clone(string) string
pkg strings, func Cut(string, string) (string, string, bool)
pkg sync, method (*Mutex) TryLock() bool
pkg sync, method (*RWMutex) TryLock() bool
pkg sync, method (*RWMutex) TryRLock() bool
pkg syscall (freebsd-386), type SysProcAttr struct, Pdeathsig Signal
pkg syscall (freebsd-386-cgo), type SysProcAttr struct, Pdeathsig Signal
pkg syscall (freebsd-amd64), type SysProcAttr struct, Pdeathsig Signal
pkg syscall (freebsd-amd64-cgo), type SysProcAttr struct, Pdeathsig Signal
pkg syscall (freebsd-arm), type SysProcAttr struct, Pdeathsig Signal
pkg syscall (freebsd-arm-cgo), type SysProcAttr struct, Pdeathsig Signal
pkg syscall (windows-386), func SyscallN(uintptr, ...uintptr) (uintptr, uintptr, Errno)
pkg syscall (windows-amd64), func SyscallN(uintptr, ...uintptr) (uintptr, uintptr, Errno)
pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M
pkg testing, method (*F) Add(...interface{})
pkg testing, method (*F) Cleanup(func())
pkg testing, method (*F) Error(...interface{})
pkg testing, method (*F) Errorf(string, ...interface{})
pkg testing, method (*F) Fail()
pkg testing, method (*F) FailNow()
pkg testing, method (*F) Failed() bool
pkg testing, method (*F) Fatal(...interface{})
pkg testing, method (*F) Fatalf(string, ...interface{})
pkg testing, method (*F) Fuzz(interface{})
pkg testing, method (*F) Helper()
pkg testing, method (*F) Log(...interface{})
pkg testing, method (*F) Logf(string, ...interface{})
pkg testing, method (*F) Name() string
pkg testing, method (*F) Setenv(string, string)
pkg testing, method (*F) Skip(...interface{})
pkg testing, method (*F) SkipNow()
pkg testing, method (*F) Skipf(string, ...interface{})
pkg testing, method (*F) Skipped() bool
pkg testing, method (*F) TempDir() string
pkg testing, type F struct
pkg testing, type InternalFuzzTarget struct
pkg testing, type InternalFuzzTarget struct, Fn func(*F)
pkg testing, type InternalFuzzTarget struct, Name string
pkg text/template/parse, const NodeBreak = 21
pkg text/template/parse, const NodeBreak NodeType
pkg text/template/parse, const NodeContinue = 22
pkg text/template/parse, const NodeContinue NodeType
pkg text/template/parse, method (*BreakNode) Copy() Node
pkg text/template/parse, method (*BreakNode) String() string
pkg text/template/parse, method (*ContinueNode) Copy() Node
pkg text/template/parse, method (*ContinueNode) String() string
pkg text/template/parse, method (BreakNode) Position() Pos
pkg text/template/parse, method (BreakNode) Type() NodeType
pkg text/template/parse, method (ContinueNode) Position() Pos
pkg text/template/parse, method (ContinueNode) Type() NodeType
pkg text/template/parse, type BreakNode struct
pkg text/template/parse, type BreakNode struct, Line int
pkg text/template/parse, type BreakNode struct, embedded NodeType
pkg text/template/parse, type BreakNode struct, embedded Pos
pkg text/template/parse, type ContinueNode struct
pkg text/template/parse, type ContinueNode struct, Line int
pkg text/template/parse, type ContinueNode struct, embedded NodeType
pkg text/template/parse, type ContinueNode struct, embedded Pos
pkg unicode/utf8, func AppendRune([]uint8, int32) []uint8

View file

@ -1,47 +0,0 @@
pkg debug/buildinfo, func Read(io.ReaderAt) (*debug.BuildInfo, error)
pkg debug/buildinfo, func ReadFile(string) (*debug.BuildInfo, error)
pkg debug/buildinfo, type BuildInfo = debug.BuildInfo
pkg runtime/debug, method (*BuildInfo) MarshalText() ([]byte, error)
pkg runtime/debug, method (*BuildInfo) UnmarshalText() ([]byte, error)
pkg runtime/debug, type BuildInfo struct, GoVersion string
pkg runtime/debug, type BuildInfo struct, Settings []BuildSetting
pkg runtime/debug, type BuildSetting struct
pkg runtime/debug, type BuildSetting struct, Key string
pkg runtime/debug, type BuildSetting struct, Value string
pkg testing, func Fuzz(func(*F)) FuzzResult
pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M
pkg testing, func RunFuzzTargets(func(string, string) (bool, error), []InternalFuzzTarget) bool
pkg testing, func RunFuzzing(func(string, string) (bool, error), []InternalFuzzTarget) bool
pkg testing, method (*B) Setenv(string, string)
pkg testing, method (*F) Add(...interface{})
pkg testing, method (*F) Cleanup(func())
pkg testing, method (*F) Error(...interface{})
pkg testing, method (*F) Errorf(string, ...interface{})
pkg testing, method (*F) Fail()
pkg testing, method (*F) FailNow()
pkg testing, method (*F) Failed() bool
pkg testing, method (*F) Fatal(...interface{})
pkg testing, method (*F) Fatalf(string, ...interface{})
pkg testing, method (*F) Fuzz(interface{})
pkg testing, method (*F) Helper()
pkg testing, method (*F) Log(...interface{})
pkg testing, method (*F) Logf(string, ...interface{})
pkg testing, method (*F) Name() string
pkg testing, method (*F) Setenv(string, string)
pkg testing, method (*F) Skip(...interface{})
pkg testing, method (*F) SkipNow()
pkg testing, method (*F) Skipf(string, ...interface{})
pkg testing, method (*F) Skipped() bool
pkg testing, method (*F) TempDir() string
pkg testing, method (*T) Setenv(string, string)
pkg testing, method (FuzzResult) String() string
pkg testing, type F struct
pkg testing, type FuzzResult struct
pkg testing, type FuzzResult struct, Crasher entry
pkg testing, type FuzzResult struct, Error error
pkg testing, type FuzzResult struct, N int
pkg testing, type FuzzResult struct, T time.Duration
pkg testing, type InternalFuzzTarget struct
pkg testing, type InternalFuzzTarget struct, Fn func(*F)
pkg testing, type InternalFuzzTarget struct, Name string
pkg net/http, method (*Cookie) Valid() error

6858
doc/go1.17_spec.html Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,8 +8,8 @@
# Consult https://www.iana.org/time-zones for the latest versions.
# Versions to use.
CODE=2021a
DATA=2021a
CODE=2021e
DATA=2021e
set -e
rm -rf work

Binary file not shown.

View file

@ -61,6 +61,7 @@ func Test32579(t *testing.T) { test32579(t) }
func Test31891(t *testing.T) { test31891(t) }
func Test42018(t *testing.T) { test42018(t) }
func Test45451(t *testing.T) { test45451(t) }
func Test49633(t *testing.T) { test49633(t) }
func TestAlign(t *testing.T) { testAlign(t) }
func TestAtol(t *testing.T) { testAtol(t) }
func TestBlocking(t *testing.T) { testBlocking(t) }

View file

@ -915,6 +915,11 @@ void issue40494(enum Enum40494 e, union Union40494* up) {}
// Issue 45451, bad handling of go:notinheap types.
typedef struct issue45451Undefined issue45451;
// Issue 49633, example of cgo.Handle with void*.
extern void GoFunc49633(void*);
void cfunc49633(void *context) { GoFunc49633(context); }
*/
import "C"

View file

@ -15,6 +15,7 @@ import "C"
import (
"runtime"
"runtime/debug"
"sync/atomic"
"testing"
@ -46,6 +47,14 @@ func test9400(t *testing.T) {
big[i] = pattern
}
// Disable GC for the duration of the test.
// This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695.
defer debug.SetGCPercent(debug.SetGCPercent(-1))
// SetGCPercent waits until the mark phase is over, but the runtime
// also preempts at the start of the sweep phase, so make sure that's
// done too. See #49695.
runtime.GC()
// Temporarily rewind the stack and trigger SIGSETXID
issue9400.RewindAndSetgid()

View file

@ -113,6 +113,7 @@ typedef struct {
int i;
} Issue38408, *PIssue38408;
extern void cfunc49633(void*); // definition is in test.go
*/
import "C"
@ -554,3 +555,26 @@ func GoFunc37033(handle C.uintptr_t) {
// A typedef pointer can be used as the element type.
// No runtime test; just make sure it compiles.
var _ C.PIssue38408 = &C.Issue38408{i: 1}
// issue 49633, example use of cgo.Handle with void*
type data49633 struct {
msg string
}
//export GoFunc49633
func GoFunc49633(context unsafe.Pointer) {
h := *(*cgo.Handle)(context)
v := h.Value().(*data49633)
v.msg = "hello"
}
func test49633(t *testing.T) {
v := &data49633{}
h := cgo.NewHandle(v)
defer h.Delete()
C.cfunc49633(unsafe.Pointer(&h))
if v.msg != "hello" {
t.Errorf("msg = %q, want 'hello'", v.msg)
}
}

View file

@ -10,12 +10,15 @@ import (
"debug/elf"
"flag"
"fmt"
"io"
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"syscall"
"testing"
@ -245,6 +248,29 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
// checkIsExecutable verifies that exe exists and has execute permission.
//
// (https://golang.org/issue/49693 notes failures with "no such file or
// directory", so we want to double-check that the executable actually exists
// immediately after we build it in order to better understand that failure
// mode.)
func checkIsExecutable(t *testing.T, exe string) {
t.Helper()
fi, err := os.Stat(exe)
if err != nil {
t.Fatal(err)
}
if runtime.GOOS == "windows" {
// os.File doesn't check the "execute" permission on Windows files
// and as a result doesn't set that bit in a file's permissions.
// Assume that if the file exists it is “executable enough”.
return
}
if fi.Mode()&0111 == 0 {
t.Fatalf("%s is not executable: %0o", exe, fi.Mode()&fs.ModePerm)
}
}
// checkLineComments checks that the export header generated by
// -buildmode=c-archive doesn't have any absolute paths in the #line
// comments. We don't want those paths because they are unhelpful for
@ -263,6 +289,173 @@ func checkLineComments(t *testing.T, hdrname string) {
}
}
// checkArchive verifies that the created library looks OK.
// We just check a couple of things now, we can add more checks as needed.
func checkArchive(t *testing.T, arname string) {
t.Helper()
switch GOOS {
case "aix", "darwin", "ios", "windows":
// We don't have any checks for non-ELF libraries yet.
if _, err := os.Stat(arname); err != nil {
t.Errorf("archive %s does not exist: %v", arname, err)
}
default:
checkELFArchive(t, arname)
}
}
// checkELFArchive checks an ELF archive.
func checkELFArchive(t *testing.T, arname string) {
t.Helper()
f, err := os.Open(arname)
if err != nil {
t.Errorf("archive %s does not exist: %v", arname, err)
return
}
defer f.Close()
// TODO(iant): put these in a shared package? But where?
const (
magic = "!<arch>\n"
fmag = "`\n"
namelen = 16
datelen = 12
uidlen = 6
gidlen = 6
modelen = 8
sizelen = 10
fmaglen = 2
hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen
)
type arhdr struct {
name string
date string
uid string
gid string
mode string
size string
fmag string
}
var magbuf [len(magic)]byte
if _, err := io.ReadFull(f, magbuf[:]); err != nil {
t.Errorf("%s: archive too short", arname)
return
}
if string(magbuf[:]) != magic {
t.Errorf("%s: incorrect archive magic string %q", arname, magbuf)
}
off := int64(len(magic))
for {
if off&1 != 0 {
var b [1]byte
if _, err := f.Read(b[:]); err != nil {
if err == io.EOF {
break
}
t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err)
}
off++
}
var hdrbuf [hdrlen]byte
if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
if err == io.EOF {
break
}
t.Errorf("%s: error reading archive header at %d: %v", arname, off, err)
return
}
var hdr arhdr
hdrslice := hdrbuf[:]
set := func(len int, ps *string) {
*ps = string(bytes.TrimSpace(hdrslice[:len]))
hdrslice = hdrslice[len:]
}
set(namelen, &hdr.name)
set(datelen, &hdr.date)
set(uidlen, &hdr.uid)
set(gidlen, &hdr.gid)
set(modelen, &hdr.mode)
set(sizelen, &hdr.size)
hdr.fmag = string(hdrslice[:fmaglen])
hdrslice = hdrslice[fmaglen:]
if len(hdrslice) != 0 {
t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice))
}
if hdr.fmag != fmag {
t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off)
return
}
size, err := strconv.ParseInt(hdr.size, 10, 64)
if err != nil {
t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err)
return
}
off += hdrlen
switch hdr.name {
case "__.SYMDEF", "/", "/SYM64/":
// The archive symbol map.
case "//", "ARFILENAMES/":
// The extended name table.
default:
// This should be an ELF object.
checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size))
}
off += size
if _, err := f.Seek(off, os.SEEK_SET); err != nil {
t.Errorf("%s: failed to seek to %d: %v", arname, off, err)
}
}
}
// checkELFArchiveObject checks an object in an ELF archive.
func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) {
t.Helper()
ef, err := elf.NewFile(obj)
if err != nil {
t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err)
return
}
defer ef.Close()
// Verify section types.
for _, sec := range ef.Sections {
want := elf.SHT_NULL
switch sec.Name {
case ".text", ".data":
want = elf.SHT_PROGBITS
case ".bss":
want = elf.SHT_NOBITS
case ".symtab":
want = elf.SHT_SYMTAB
case ".strtab":
want = elf.SHT_STRTAB
case ".init_array":
want = elf.SHT_INIT_ARRAY
case ".fini_array":
want = elf.SHT_FINI_ARRAY
case ".preinit_array":
want = elf.SHT_PREINIT_ARRAY
}
if want != elf.SHT_NULL && sec.Type != want {
t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want)
}
}
}
func TestInstall(t *testing.T) {
if !testWork {
defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
@ -310,7 +503,7 @@ func TestEarlySignalHandler(t *testing.T) {
defer func() {
os.Remove("libgo2.a")
os.Remove("libgo2.h")
os.Remove("testp")
os.Remove("testp" + exeSuffix)
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
}()
}
@ -321,6 +514,7 @@ func TestEarlySignalHandler(t *testing.T) {
t.Fatal(err)
}
checkLineComments(t, "libgo2.h")
checkArchive(t, "libgo2.a")
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
if runtime.Compiler == "gccgo" {
@ -350,7 +544,7 @@ func TestSignalForwarding(t *testing.T) {
defer func() {
os.Remove("libgo2.a")
os.Remove("libgo2.h")
os.Remove("testp")
os.Remove("testp" + exeSuffix)
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
}()
}
@ -361,6 +555,7 @@ func TestSignalForwarding(t *testing.T) {
t.Fatal(err)
}
checkLineComments(t, "libgo2.h")
checkArchive(t, "libgo2.a")
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
if runtime.Compiler == "gccgo" {
@ -374,7 +569,7 @@ func TestSignalForwarding(t *testing.T) {
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
out, err := cmd.CombinedOutput()
t.Logf("%s", out)
t.Logf("%v\n%s", cmd.Args, out)
expectSignal(t, err, syscall.SIGSEGV)
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
@ -383,7 +578,9 @@ func TestSignalForwarding(t *testing.T) {
cmd = exec.Command(bin[0], append(bin[1:], "3")...)
out, err = cmd.CombinedOutput()
t.Logf("%s", out)
if len(out) > 0 {
t.Logf("%s", out)
}
expectSignal(t, err, syscall.SIGPIPE)
}
}
@ -400,7 +597,7 @@ func TestSignalForwardingExternal(t *testing.T) {
defer func() {
os.Remove("libgo2.a")
os.Remove("libgo2.h")
os.Remove("testp")
os.Remove("testp" + exeSuffix)
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
}()
}
@ -411,6 +608,7 @@ func TestSignalForwardingExternal(t *testing.T) {
t.Fatal(err)
}
checkLineComments(t, "libgo2.h")
checkArchive(t, "libgo2.a")
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
if runtime.Compiler == "gccgo" {
@ -517,7 +715,7 @@ func TestOsSignal(t *testing.T) {
defer func() {
os.Remove("libgo3.a")
os.Remove("libgo3.h")
os.Remove("testp")
os.Remove("testp" + exeSuffix)
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
}()
}
@ -528,6 +726,7 @@ func TestOsSignal(t *testing.T) {
t.Fatal(err)
}
checkLineComments(t, "libgo3.h")
checkArchive(t, "libgo3.a")
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
if runtime.Compiler == "gccgo" {
@ -554,7 +753,7 @@ func TestSigaltstack(t *testing.T) {
defer func() {
os.Remove("libgo4.a")
os.Remove("libgo4.h")
os.Remove("testp")
os.Remove("testp" + exeSuffix)
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
}()
}
@ -565,6 +764,7 @@ func TestSigaltstack(t *testing.T) {
t.Fatal(err)
}
checkLineComments(t, "libgo4.h")
checkArchive(t, "libgo4.a")
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
if runtime.Compiler == "gccgo" {
@ -747,25 +947,30 @@ func TestSIGPROF(t *testing.T) {
}
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
t.Fatal(err)
}
checkLineComments(t, "libgo6.h")
checkArchive(t, "libgo6.a")
ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
if runtime.Compiler == "gccgo" {
ccArgs = append(ccArgs, "-lgo")
}
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
t.Logf("%v\n%s", ccArgs, out)
if err != nil {
t.Fatal(err)
}
checkIsExecutable(t, "./testp6"+exeSuffix)
argv := cmdToRun("./testp6")
cmd = exec.Command(argv[0], argv[1:]...)
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err = cmd.CombinedOutput()
t.Logf("%v\n%s", argv, out)
if err != nil {
t.Fatal(err)
}
}
@ -788,13 +993,13 @@ func TestCompileWithoutShared(t *testing.T) {
}
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
t.Log(cmd.Args)
out, err := cmd.CombinedOutput()
t.Logf("%s", out)
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
t.Fatal(err)
}
checkLineComments(t, "libgo2.h")
checkArchive(t, "libgo2.a")
exe := "./testnoshared" + exeSuffix
@ -804,23 +1009,22 @@ func TestCompileWithoutShared(t *testing.T) {
if runtime.Compiler == "gccgo" {
ccArgs = append(ccArgs, "-lgo")
}
t.Log(ccArgs)
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
t.Logf("%v\n%s", ccArgs, out)
// If -no-pie unrecognized, try -nopie if this is possibly clang
if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
t.Log(ccArgs)
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
t.Logf("%v\n%s", ccArgs, out)
}
// Don't use either -no-pie or -nopie
if err != nil && bytes.Contains(out, []byte("unrecognized")) {
ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a")
t.Log(ccArgs)
ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a")
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
t.Logf("%v\n%s", ccArgs, out)
}
t.Logf("%s", out)
if err != nil {
t.Fatal(err)
}
@ -829,17 +1033,15 @@ func TestCompileWithoutShared(t *testing.T) {
}
binArgs := append(cmdToRun(exe), "1")
t.Log(binArgs)
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
t.Logf("%s", out)
t.Logf("%v\n%s", binArgs, out)
expectSignal(t, err, syscall.SIGSEGV)
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
binArgs := append(cmdToRun(exe), "3")
t.Log(binArgs)
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
t.Logf("%s", out)
t.Logf("%v\n%s", binArgs, out)
expectSignal(t, err, syscall.SIGPIPE)
}
}
@ -894,26 +1096,30 @@ func TestManyCalls(t *testing.T) {
}
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
t.Fatal(err)
}
checkLineComments(t, "libgo7.h")
checkArchive(t, "libgo7.a")
ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
if runtime.Compiler == "gccgo" {
ccArgs = append(ccArgs, "-lgo")
}
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
t.Logf("%v\n%s", ccArgs, out)
if err != nil {
t.Fatal(err)
}
checkIsExecutable(t, "./testp7"+exeSuffix)
argv := cmdToRun("./testp7")
cmd = exec.Command(argv[0], argv[1:]...)
var sb strings.Builder
cmd.Stdout = &sb
cmd.Stderr = &sb
sb := new(strings.Builder)
cmd.Stdout = sb
cmd.Stderr = sb
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
@ -926,8 +1132,9 @@ func TestManyCalls(t *testing.T) {
)
defer timer.Stop()
if err := cmd.Wait(); err != nil {
t.Log(sb.String())
err = cmd.Wait()
t.Logf("%v\n%s", cmd.Args, sb)
if err != nil {
t.Error(err)
}
}
@ -949,23 +1156,27 @@ func TestPreemption(t *testing.T) {
}
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
t.Fatal(err)
}
checkLineComments(t, "libgo8.h")
checkArchive(t, "libgo8.a")
ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
t.Logf("%s", out)
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
t.Logf("%v\n%s", ccArgs, out)
if err != nil {
t.Fatal(err)
}
checkIsExecutable(t, "./testp8"+exeSuffix)
argv := cmdToRun("./testp8")
cmd = exec.Command(argv[0], argv[1:]...)
var sb strings.Builder
cmd.Stdout = &sb
cmd.Stderr = &sb
sb := new(strings.Builder)
cmd.Stdout = sb
cmd.Stderr = sb
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
@ -978,8 +1189,9 @@ func TestPreemption(t *testing.T) {
)
defer timer.Stop()
if err := cmd.Wait(); err != nil {
t.Log(sb.String())
err = cmd.Wait()
t.Logf("%v\n%s", cmd.Args, sb)
if err != nil {
t.Error(err)
}
}

View file

@ -781,10 +781,10 @@ func copyFile(t *testing.T, dst, src string) {
func TestGo2C2Go(t *testing.T) {
switch GOOS {
case "darwin", "ios":
// Darwin shared libraries don't support the multiple
case "darwin", "ios", "windows":
// Non-ELF shared libraries don't support the multiple
// copies of the runtime package implied by this test.
t.Skip("linking c-shared into Go programs not supported on Darwin; issue 29061")
t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS)
case "android":
t.Skip("test fails on android; issue 29087")
}

View file

@ -265,10 +265,6 @@ func TestIssue25756(t *testing.T) {
// Test with main using -buildmode=pie with plugin for issue #43228
func TestIssue25756pie(t *testing.T) {
if os.Getenv("GO_BUILDER_NAME") == "darwin-arm64-11_0-toothrot" {
t.Skip("broken on darwin/arm64 builder in sharded mode; see issue 46239")
}
goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
run(t, "./issue25756pie.exe")
@ -293,3 +289,31 @@ func TestIssue44956(t *testing.T) {
goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
run(t, "./issue44956.exe")
}
func TestForkExec(t *testing.T) {
// Issue 38824: importing the plugin package causes it hang in forkExec on darwin.
t.Parallel()
goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
var cmd *exec.Cmd
done := make(chan int, 1)
go func() {
for i := 0; i < 100; i++ {
cmd = exec.Command("./forkexec.exe", "1")
err := cmd.Run()
if err != nil {
t.Errorf("running command failed: %v", err)
break
}
}
done <- 1
}()
select {
case <-done:
case <-time.After(5 * time.Minute):
cmd.Process.Kill()
t.Fatalf("subprocess hang")
}
}

View file

@ -0,0 +1,30 @@
// Copyright 2021 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 main
import (
"os"
"os/exec"
_ "plugin"
"sync"
)
func main() {
if os.Args[1] != "1" {
return
}
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// does not matter what we exec, just exec itself
cmd := exec.Command("./forkexec.exe", "0")
cmd.Run()
}()
}
wg.Wait()
}

View file

@ -33,11 +33,13 @@ func TestASAN(t *testing.T) {
cases := []struct {
src string
memoryAccessError string
errorLocation string
}{
{src: "asan1_fail.go", memoryAccessError: "heap-use-after-free"},
{src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow"},
{src: "asan3_fail.go", memoryAccessError: "use-after-poison"},
{src: "asan4_fail.go", memoryAccessError: "use-after-poison"},
{src: "asan1_fail.go", memoryAccessError: "heap-use-after-free", errorLocation: "asan1_fail.go:25"},
{src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow", errorLocation: "asan2_fail.go:31"},
{src: "asan3_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan3_fail.go:13"},
{src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"},
{src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"},
{src: "asan_useAfterReturn.go"},
}
for _, tc := range cases {
@ -54,8 +56,21 @@ func TestASAN(t *testing.T) {
cmd := hangProneCmd(outPath)
if tc.memoryAccessError != "" {
out, err := cmd.CombinedOutput()
if err != nil && strings.Contains(string(out), tc.memoryAccessError) {
outb, err := cmd.CombinedOutput()
out := string(outb)
if err != nil && strings.Contains(out, tc.memoryAccessError) {
// This string is output if the
// sanitizer library needs a
// symbolizer program and can't find it.
const noSymbolizer = "external symbolizer"
// Check if -asan option can correctly print where the error occured.
if tc.errorLocation != "" &&
!strings.Contains(out, tc.errorLocation) &&
!strings.Contains(out, noSymbolizer) &&
compilerSupportsLocation() {
t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.errorLocation, out)
}
return
}
t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)

View file

@ -218,6 +218,23 @@ func compilerVersion() (version, error) {
return compiler.version, compiler.err
}
// compilerSupportsLocation reports whether the compiler should be
// able to provide file/line information in backtraces.
func compilerSupportsLocation() bool {
compiler, err := compilerVersion()
if err != nil {
return false
}
switch compiler.name {
case "gcc":
return compiler.major >= 10
case "clang":
return true
default:
return false
}
}
type compilerCheck struct {
once sync.Once
err error
@ -269,6 +286,8 @@ func configure(sanitizer string) *config {
case "address":
c.goFlags = append(c.goFlags, "-asan")
// Set the debug mode to print the C stack trace.
c.cFlags = append(c.cFlags, "-g")
default:
panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))

View file

@ -22,7 +22,7 @@ func main() {
// C passes Go an invalid pointer.
a := C.test()
// Use after free
*a = 2
*a = 2 // BOOM
// We shouldn't get here; asan should stop us first.
fmt.Println(*a)
}

View file

@ -28,7 +28,7 @@ func main() {
a := C.f()
q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
// Access to C pointer out of bounds.
*q5 = 100
*q5 = 100 // BOOM
// We shouldn't get here; asan should stop us first.
fmt.Printf("q5: %d, %x\n", *q5, q5)
}

View file

@ -0,0 +1,21 @@
package main
import (
"fmt"
"runtime"
"unsafe"
)
func main() {
p := new([1024 * 1000]int)
p[0] = 10
r := bar(&p[1024*1000-1])
fmt.Printf("r value is %d", r)
}
func bar(a *int) int {
p := unsafe.Add(unsafe.Pointer(a), 2*unsafe.Sizeof(int(1)))
runtime.ASanWrite(p, 8) // BOOM
*((*int)(p)) = 10
return *((*int)(p))
}

View file

@ -20,6 +20,7 @@ import (
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"testing"
"time"
@ -55,7 +56,7 @@ func runWithEnv(t *testing.T, msg string, env []string, args ...string) {
// t.Fatalf if the command fails.
func goCmd(t *testing.T, args ...string) string {
newargs := []string{args[0]}
if *testX {
if *testX && args[0] != "env" {
newargs = append(newargs, "-x")
}
newargs = append(newargs, args[1:]...)
@ -461,7 +462,9 @@ func TestTrivialExecutable(t *testing.T) {
run(t, "trivial executable", "../../bin/trivial")
AssertIsLinkedTo(t, "../../bin/trivial", soname)
AssertHasRPath(t, "../../bin/trivial", gorootInstallDir)
checkSize(t, "../../bin/trivial", 100000) // it is 19K on linux/amd64, 100K should be enough
// It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment
// 4*64k should be enough, but this might need revision eventually.
checkSize(t, "../../bin/trivial", 256000)
}
// Build a trivial program in PIE mode that links against the shared runtime and check it runs.
@ -470,7 +473,9 @@ func TestTrivialExecutablePIE(t *testing.T) {
run(t, "trivial executable", "./trivial.pie")
AssertIsLinkedTo(t, "./trivial.pie", soname)
AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
checkSize(t, "./trivial.pie", 100000) // it is 19K on linux/amd64, 100K should be enough
// It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment
// 4*64k should be enough, but this might need revision eventually.
checkSize(t, "./trivial.pie", 256000)
}
// Check that the file size does not exceed a limit.
@ -694,7 +699,15 @@ func requireGccgo(t *testing.T) {
if err != nil {
t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output)
}
if string(output) < "5" {
dot := bytes.Index(output, []byte{'.'})
if dot > 0 {
output = output[:dot]
}
major, err := strconv.Atoi(string(output))
if err != nil {
t.Skipf("can't parse gccgo version number %s", output)
}
if major < 5 {
t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output)))
}

View file

@ -17,4 +17,4 @@ export IPHONEOS_DEPLOYMENT_TARGET=5.1
# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang.
CLANG=`xcrun --sdk $SDK --find clang`
exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=10.0 "$@"
exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=12.0 "$@"

View file

@ -15,6 +15,10 @@ import (
)
func TestRepeatBootstrap(t *testing.T) {
if testing.Short() {
t.Skipf("skipping test that rebuilds the entire toolchain")
}
goroot, err := os.MkdirTemp("", "reboot-goroot")
if err != nil {
t.Fatal(err)

View file

@ -538,7 +538,7 @@ type headerFileInfo struct {
func (fi headerFileInfo) Size() int64 { return fi.h.Size }
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
func (fi headerFileInfo) Sys() interface{} { return fi.h }
func (fi headerFileInfo) Sys() any { return fi.h }
// Name returns the base name of the file.
func (fi headerFileInfo) Name() string {

View file

@ -0,0 +1,80 @@
// Copyright 2021 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 tar
import (
"bytes"
"io"
"testing"
)
func FuzzReader(f *testing.F) {
b := bytes.NewBuffer(nil)
w := NewWriter(b)
inp := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
err := w.WriteHeader(&Header{
Name: "lorem.txt",
Mode: 0600,
Size: int64(len(inp)),
})
if err != nil {
f.Fatalf("failed to create writer: %s", err)
}
_, err = w.Write(inp)
if err != nil {
f.Fatalf("failed to write file to archive: %s", err)
}
if err := w.Close(); err != nil {
f.Fatalf("failed to write archive: %s", err)
}
f.Add(b.Bytes())
f.Fuzz(func(t *testing.T, b []byte) {
r := NewReader(bytes.NewReader(b))
type file struct {
header *Header
content []byte
}
files := []file{}
for {
hdr, err := r.Next()
if err == io.EOF {
break
}
if err != nil {
return
}
buf := bytes.NewBuffer(nil)
if _, err := io.Copy(buf, r); err != nil {
continue
}
files = append(files, file{header: hdr, content: buf.Bytes()})
}
// If we were unable to read anything out of the archive don't
// bother trying to roundtrip it.
if len(files) == 0 {
return
}
out := bytes.NewBuffer(nil)
w := NewWriter(out)
for _, f := range files {
if err := w.WriteHeader(f.header); err != nil {
t.Fatalf("unable to write previously parsed header: %s", err)
}
if _, err := w.Write(f.content); err != nil {
t.Fatalf("unable to write previously parsed content: %s", err)
}
}
if err := w.Close(); err != nil {
t.Fatalf("Unable to write archive: %s", err)
}
// TODO: We may want to check if the archive roundtrips. This would require
// taking into account addition of the two zero trailer blocks that Writer.Close
// appends.
})
}

View file

@ -1363,7 +1363,7 @@ func TestFileReader(t *testing.T) {
wantLCnt int64
wantPCnt int64
}
testFnc interface{} // testRead | testWriteTo | testRemaining
testFnc any // testRead | testWriteTo | testRemaining
)
type (
@ -1376,7 +1376,7 @@ func TestFileReader(t *testing.T) {
spd sparseDatas
size int64
}
fileMaker interface{} // makeReg | makeSparse
fileMaker any // makeReg | makeSparse
)
vectors := []struct {

View file

@ -23,7 +23,7 @@ import (
type testError struct{ error }
type fileOps []interface{} // []T where T is (string | int64)
type fileOps []any // []T where T is (string | int64)
// testFile is an io.ReadWriteSeeker where the IO operations performed
// on it must match the list of operations in ops.

View file

@ -67,7 +67,7 @@ func TestWriter(t *testing.T) {
testClose struct { // Close() == wantErr
wantErr error
}
testFnc interface{} // testHeader | testWrite | testReadFrom | testClose
testFnc any // testHeader | testWrite | testReadFrom | testClose
)
vectors := []struct {
@ -1031,7 +1031,7 @@ func TestFileWriter(t *testing.T) {
wantLCnt int64
wantPCnt int64
}
testFnc interface{} // testWrite | testReadFrom | testRemaining
testFnc any // testWrite | testReadFrom | testRemaining
)
type (
@ -1044,7 +1044,7 @@ func TestFileWriter(t *testing.T) {
sph sparseHoles
size int64
}
fileMaker interface{} // makeReg | makeSparse
fileMaker any // makeReg | makeSparse
)
vectors := []struct {

View file

@ -0,0 +1,81 @@
// Copyright 2021 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 zip
import (
"bytes"
"io"
"os"
"path/filepath"
"testing"
)
func FuzzReader(f *testing.F) {
testdata, err := os.ReadDir("testdata")
if err != nil {
f.Fatalf("failed to read testdata directory: %s", err)
}
for _, de := range testdata {
if de.IsDir() {
continue
}
b, err := os.ReadFile(filepath.Join("testdata", de.Name()))
if err != nil {
f.Fatalf("failed to read testdata: %s", err)
}
f.Add(b)
}
f.Fuzz(func(t *testing.T, b []byte) {
r, err := NewReader(bytes.NewReader(b), int64(len(b)))
if err != nil {
return
}
type file struct {
header *FileHeader
content []byte
}
files := []file{}
for _, f := range r.File {
fr, err := f.Open()
if err != nil {
continue
}
content, err := io.ReadAll(fr)
if err != nil {
continue
}
files = append(files, file{header: &f.FileHeader, content: content})
if _, err := r.Open(f.Name); err != nil {
continue
}
}
// If we were unable to read anything out of the archive don't
// bother trying to roundtrip it.
if len(files) == 0 {
return
}
w := NewWriter(io.Discard)
for _, f := range files {
ww, err := w.CreateHeader(f.header)
if err != nil {
t.Fatalf("unable to write previously parsed header: %s", err)
}
if _, err := ww.Write(f.content); err != nil {
t.Fatalf("unable to write previously parsed content: %s", err)
}
}
if err := w.Close(); err != nil {
t.Fatalf("Unable to write archive: %s", err)
}
// TODO: We may want to check if the archive roundtrips.
})
}

View file

@ -125,7 +125,6 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
if err != nil {
return err
}
f.readDataDescriptor()
z.File = append(z.File, f)
}
if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here
@ -186,10 +185,15 @@ func (f *File) Open() (io.ReadCloser, error) {
return nil, ErrAlgorithm
}
var rc io.ReadCloser = dcomp(r)
var desr io.Reader
if f.hasDataDescriptor() {
desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
}
rc = &checksumReader{
rc: rc,
hash: crc32.NewIEEE(),
f: f,
desr: desr,
}
return rc, nil
}
@ -205,49 +209,13 @@ func (f *File) OpenRaw() (io.Reader, error) {
return r, nil
}
func (f *File) readDataDescriptor() {
if !f.hasDataDescriptor() {
return
}
bodyOffset, err := f.findBodyOffset()
if err != nil {
f.descErr = err
return
}
// In section 4.3.9.2 of the spec: "However ZIP64 format MAY be used
// regardless of the size of a file. When extracting, if the zip64
// extended information extra field is present for the file the
// compressed and uncompressed sizes will be 8 byte values."
//
// Historically, this package has used the compressed and uncompressed
// sizes from the central directory to determine if the package is
// zip64.
//
// For this case we allow either the extra field or sizes to determine
// the data descriptor length.
zip64 := f.zip64 || f.isZip64()
n := int64(dataDescriptorLen)
if zip64 {
n = dataDescriptor64Len
}
size := int64(f.CompressedSize64)
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, n)
dd, err := readDataDescriptor(r, zip64)
if err != nil {
f.descErr = err
return
}
f.CRC32 = dd.crc32
}
type checksumReader struct {
rc io.ReadCloser
hash hash.Hash32
nread uint64 // number of bytes read so far
f *File
err error // sticky error
desr io.Reader // if non-nil, where to read the data descriptor
err error // sticky error
}
func (r *checksumReader) Stat() (fs.FileInfo, error) {
@ -268,12 +236,12 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
if r.nread != r.f.UncompressedSize64 {
return 0, io.ErrUnexpectedEOF
}
if r.f.hasDataDescriptor() {
if r.f.descErr != nil {
if r.f.descErr == io.EOF {
if r.desr != nil {
if err1 := readDataDescriptor(r.desr, r.f); err1 != nil {
if err1 == io.EOF {
err = io.ErrUnexpectedEOF
} else {
err = r.f.descErr
err = err1
}
} else if r.hash.Sum32() != r.f.CRC32 {
err = ErrChecksum
@ -485,10 +453,8 @@ parseExtras:
return nil
}
func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
// Create enough space for the largest possible size
var buf [dataDescriptor64Len]byte
func readDataDescriptor(r io.Reader, f *File) error {
var buf [dataDescriptorLen]byte
// The spec says: "Although not originally assigned a
// signature, the value 0x08074b50 has commonly been adopted
// as a signature value for the data descriptor record.
@ -497,9 +463,10 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
// descriptors and should account for either case when reading
// ZIP files to ensure compatibility."
//
// First read just those 4 bytes to see if the signature exists.
// dataDescriptorLen includes the size of the signature but
// first read just those 4 bytes to see if it exists.
if _, err := io.ReadFull(r, buf[:4]); err != nil {
return nil, err
return err
}
off := 0
maybeSig := readBuf(buf[:4])
@ -508,28 +475,21 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
// bytes.
off += 4
}
end := dataDescriptorLen - 4
if zip64 {
end = dataDescriptor64Len - 4
if _, err := io.ReadFull(r, buf[off:12]); err != nil {
return err
}
if _, err := io.ReadFull(r, buf[off:end]); err != nil {
return nil, err
}
b := readBuf(buf[:end])
out := &dataDescriptor{
crc32: b.uint32(),
b := readBuf(buf[:12])
if b.uint32() != f.CRC32 {
return ErrChecksum
}
if zip64 {
out.compressedSize = b.uint64()
out.uncompressedSize = b.uint64()
} else {
out.compressedSize = uint64(b.uint32())
out.uncompressedSize = uint64(b.uint32())
}
return out, nil
// The two sizes that follow here can be either 32 bits or 64 bits
// but the spec is not very clear on this and different
// interpretations has been made causing incompatibilities. We
// already have the sizes from the central directory so we can
// just ignore these.
return nil
}
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
@ -710,7 +670,7 @@ func (f *fileListEntry) Size() int64 { return 0 }
func (f *fileListEntry) Mode() fs.FileMode { return fs.ModeDir | 0555 }
func (f *fileListEntry) Type() fs.FileMode { return fs.ModeDir }
func (f *fileListEntry) IsDir() bool { return true }
func (f *fileListEntry) Sys() interface{} { return nil }
func (f *fileListEntry) Sys() any { return nil }
func (f *fileListEntry) ModTime() time.Time {
if f.file == nil {

View file

@ -1214,128 +1214,6 @@ func TestCVE202127919(t *testing.T) {
}
}
func TestReadDataDescriptor(t *testing.T) {
tests := []struct {
desc string
in []byte
zip64 bool
want *dataDescriptor
wantErr error
}{{
desc: "valid 32 bit with signature",
in: []byte{
0x50, 0x4b, 0x07, 0x08, // signature
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, 0x06, 0x07, // compressed size
0x08, 0x09, 0x0a, 0x0b, // uncompressed size
},
want: &dataDescriptor{
crc32: 0x03020100,
compressedSize: 0x07060504,
uncompressedSize: 0x0b0a0908,
},
}, {
desc: "valid 32 bit without signature",
in: []byte{
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, 0x06, 0x07, // compressed size
0x08, 0x09, 0x0a, 0x0b, // uncompressed size
},
want: &dataDescriptor{
crc32: 0x03020100,
compressedSize: 0x07060504,
uncompressedSize: 0x0b0a0908,
},
}, {
desc: "valid 64 bit with signature",
in: []byte{
0x50, 0x4b, 0x07, 0x08, // signature
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size
},
zip64: true,
want: &dataDescriptor{
crc32: 0x03020100,
compressedSize: 0x0b0a090807060504,
uncompressedSize: 0x131211100f0e0d0c,
},
}, {
desc: "valid 64 bit without signature",
in: []byte{
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size
},
zip64: true,
want: &dataDescriptor{
crc32: 0x03020100,
compressedSize: 0x0b0a090807060504,
uncompressedSize: 0x131211100f0e0d0c,
},
}, {
desc: "invalid 32 bit with signature",
in: []byte{
0x50, 0x4b, 0x07, 0x08, // signature
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, // unexpected end
},
wantErr: io.ErrUnexpectedEOF,
}, {
desc: "invalid 32 bit without signature",
in: []byte{
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, // unexpected end
},
wantErr: io.ErrUnexpectedEOF,
}, {
desc: "invalid 64 bit with signature",
in: []byte{
0x50, 0x4b, 0x07, 0x08, // signature
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end
},
zip64: true,
wantErr: io.ErrUnexpectedEOF,
}, {
desc: "invalid 64 bit without signature",
in: []byte{
0x00, 0x01, 0x02, 0x03, // crc32
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end
},
zip64: true,
wantErr: io.ErrUnexpectedEOF,
}}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
r := bytes.NewReader(test.in)
desc, err := readDataDescriptor(r, test.zip64)
if err != test.wantErr {
t.Fatalf("got err %v; want nil", err)
}
if test.want == nil {
return
}
if desc == nil {
t.Fatalf("got nil DataDescriptor; want non-nil")
}
if desc.crc32 != test.want.crc32 {
t.Errorf("got CRC32 %#x; want %#x", desc.crc32, test.want.crc32)
}
if desc.compressedSize != test.want.compressedSize {
t.Errorf("got CompressedSize %#x; want %#x", desc.compressedSize, test.want.compressedSize)
}
if desc.uncompressedSize != test.want.uncompressedSize {
t.Errorf("got UncompressedSize %#x; want %#x", desc.uncompressedSize, test.want.uncompressedSize)
}
})
}
}
func TestCVE202133196(t *testing.T) {
// Archive that indicates it has 1 << 128 -1 files,
// this would previously cause a panic due to attempting

View file

@ -163,7 +163,7 @@ func (fi headerFileInfo) ModTime() time.Time {
}
func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() }
func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() }
func (fi headerFileInfo) Sys() interface{} { return fi.fh }
func (fi headerFileInfo) Sys() any { return fi.fh }
func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil }
@ -390,11 +390,3 @@ func unixModeToFileMode(m uint32) fs.FileMode {
}
return mode
}
// dataDescriptor holds the data descriptor that optionally follows the file
// contents in the zip file.
type dataDescriptor struct {
crc32 uint32
compressedSize uint64
uncompressedSize uint64
}

View file

@ -244,6 +244,8 @@ func (b *Reader) Read(p []byte) (n int, err error) {
}
// copy as much as we can
// Note: if the slice panics here, it is probably because
// the underlying reader returned a bad count. See issue 49795.
n = copy(p, b.buf[b.r:b.w])
b.r += n
b.lastByte = int(b.buf[b.r-1])
@ -593,6 +595,8 @@ func NewWriterSize(w io.Writer, size int) *Writer {
}
// NewWriter returns a new Writer whose buffer has the default size.
// If the argument io.Writer is already a Writer with large enough buffer size,
// it returns the underlying Writer.
func NewWriter(w io.Writer) *Writer {
return NewWriterSize(w, defaultBufSize)
}

View file

@ -657,7 +657,7 @@ func TestWriterAppend(t *testing.T) {
}
// While not recommended, it is valid to append to a shifted buffer.
// This forces Write to copy the the input.
// This forces Write to copy the input.
if rn.Intn(8) == 0 && cap(b) > 0 {
b = b[1:1:cap(b)]
}

View file

@ -91,6 +91,16 @@ type byte = uint8
// used, by convention, to distinguish character values from integer values.
type rune = int32
// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}
// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, interfaces,
// arrays of comparable types, structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable comparable
// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
@ -229,7 +239,7 @@ func close(c chan<- Type)
// that point, the program is terminated with a non-zero exit code. This
// termination sequence is called panicking and can be controlled by the
// built-in function recover.
func panic(v interface{})
func panic(v any)
// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
@ -240,7 +250,7 @@ func panic(v interface{})
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover() interface{}
func recover() any
// The print built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.

View file

@ -76,7 +76,7 @@ func TestReaderAt(t *testing.T) {
off int64
n int
want string
wanterr interface{}
wanterr any
}{
{0, 10, "0123456789", nil},
{1, 10, "123456789", io.EOF},

View file

@ -459,8 +459,11 @@ type listImports struct {
var listCache sync.Map // map[string]listImports, keyed by contextName
// listSem is a semaphore restricting concurrent invocations of 'go list'.
var listSem = make(chan semToken, runtime.GOMAXPROCS(0))
// listSem is a semaphore restricting concurrent invocations of 'go list'. 'go
// list' has its own internal concurrency, so we use a hard-coded constant (to
// allow the I/O-intensive phases of 'go list' to overlap) instead of scaling
// all the way up to GOMAXPROCS.
var listSem = make(chan semToken, 2)
type semToken struct{}
@ -1071,7 +1074,7 @@ func (w *Walker) emitMethod(m *types.Selection) {
w.emitf("method (%s%s) %s%s", w.typeString(recv), tps, m.Obj().Name(), w.signatureString(sig))
}
func (w *Walker) emitf(format string, args ...interface{}) {
func (w *Walker) emitf(format string, args ...any) {
f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...)
if strings.Contains(f, "\n") {
panic("feature contains newlines: " + f)

View file

@ -1,3 +1,4 @@
//go:build !amd64
// +build !amd64
package p

View file

@ -197,7 +197,7 @@ var m map[string]int
var chanVar chan int
var ifaceVar interface{} = 5
var ifaceVar any = 5
var assertVar = ifaceVar.(int)

View file

@ -4,12 +4,12 @@
package p4
type Pair[T1 interface { M() }, T2 ~int] struct {
type Pair[T1 interface{ M() }, T2 ~int] struct {
f1 T1
f2 T2
}
func NewPair[T1 interface { M() }, T2 ~int](v1 T1, v2 T2) Pair[T1, T2] {
func NewPair[T1 interface{ M() }, T2 ~int](v1 T1, v2 T2) Pair[T1, T2] {
return Pair[T1, T2]{f1: v1, f2: v2}
}

View file

@ -40,12 +40,12 @@ which is used for error reporting and the creation of debugging information.
* `cmd/compile/internal/gc` (create compiler AST, type checking, AST transformations)
The gc package includes an AST definition carried over from when it was written
in C. All of its code is written in terms of it, so the first thing that the gc
The gc package includes its own AST definition carried over from when it was written
in C. All of its code is written in terms of this AST, so the first thing that the gc
package must do is convert the syntax package's syntax tree to the compiler's
AST representation. This extra step may be refactored away in the future.
The AST is then type-checked. The first steps are name resolution and type
The gc AST is then type-checked. The first steps are name resolution and type
inference, which determine which object belongs to which identifier, and what
type each expression has. Type-checking includes certain extra checks, such as
"declared and not used" as well as determining whether or not a function
@ -79,8 +79,7 @@ historical reasons, but the long-term plan is to move all of them here.
Then, a series of machine-independent passes and rules are applied. These do not
concern any single computer architecture, and thus run on all `GOARCH` variants.
Some examples of these generic passes include dead code elimination, removal of
These passes include dead code elimination, removal of
unneeded nil checks, and removal of unused branches. The generic rewrite rules
mainly concern expressions, such as replacing some expressions with constant
values, and optimizing multiplications and float operations.

View file

@ -410,7 +410,11 @@ Special-purpose registers are as follows:
| R13 | Scratch | Scratch | Scratch |
| R14 | Current goroutine | Same | Same |
| R15 | GOT reference temporary if dynlink | Same | Same |
| X15 | Zero value | Same | Scratch |
| X15 | Zero value (*) | Same | Scratch |
(*) Except on Plan 9, where X15 is a scratch register because SSE
registers cannot be used in note handlers (so the compiler avoids
using them except when absolutely necessary).
*Rationale*: These register meanings are compatible with Gos
stack-based calling convention except for R14 and X15, which will have

View file

@ -715,19 +715,20 @@ func setup() {
synthOnce.Do(func() {
fname := types.BuiltinPkg.Lookup
nxp := src.NoXPos
unsp := types.Types[types.TUNSAFEPTR]
ui := types.Types[types.TUINTPTR]
bp := types.NewPtr(types.Types[types.TUINT8])
it := types.Types[types.TINT]
synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("ptr"), unsp),
types.NewField(nxp, fname("len"), ui),
types.NewField(nxp, fname("cap"), ui),
types.NewField(nxp, fname("ptr"), bp),
types.NewField(nxp, fname("len"), it),
types.NewField(nxp, fname("cap"), it),
})
types.CalcStructSize(synthSlice)
synthString = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("data"), unsp),
types.NewField(nxp, fname("len"), ui),
types.NewField(nxp, fname("data"), bp),
types.NewField(nxp, fname("len"), it),
})
types.CalcStructSize(synthString)
unsp := types.Types[types.TUNSAFEPTR]
synthIface = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("f1"), unsp),
types.NewField(nxp, fname("f2"), unsp),

View file

@ -76,8 +76,18 @@ func TestGoAMD64v1(t *testing.T) {
if err != nil {
t.Fatalf("couldn't execute test: %s", err)
}
if string(out) != "PASS\n" {
t.Fatalf("test reported error: %s", string(out))
// Expect to see output of the form "PASS\n", unless the test binary
// was compiled for coverage (in which case there will be an extra line).
success := false
lines := strings.Split(string(out), "\n")
if len(lines) == 2 {
success = lines[0] == "PASS" && lines[1] == ""
} else if len(lines) == 3 {
success = lines[0] == "PASS" &&
strings.HasPrefix(lines[1], "coverage") && lines[2] == ""
}
if !success {
t.Fatalf("test reported error: %s lines=%+v", string(out), lines)
}
}

View file

@ -217,10 +217,10 @@ func FatalfAt(pos src.XPos, format string, args ...interface{}) {
fmt.Printf("\n")
// If this is a released compiler version, ask for a bug report.
if strings.HasPrefix(buildcfg.Version, "go") {
if Debug.Panic == 0 && strings.HasPrefix(buildcfg.Version, "go") {
fmt.Printf("\n")
fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
fmt.Printf("https://golang.org/issue/new\n")
fmt.Printf("https://go.dev/issue/new\n")
} else {
// Not a release; dump a stack trace, too.
fmt.Println()

View file

@ -150,6 +150,19 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
dcl := apDecls
if fnsym.WasInlined() {
dcl = preInliningDcls(fnsym)
} else {
// The backend's stackframe pass prunes away entries from the
// fn's Dcl list, including PARAMOUT nodes that correspond to
// output params passed in registers. Add back in these
// entries here so that we can process them properly during
// DWARF-gen. See issue 48573 for more details.
debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
for _, n := range debugInfo.RegOutputParams {
if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() {
panic("invalid ir.Name on debugInfo.RegOutputParams list")
}
dcl = append(dcl, n)
}
}
// If optimization is enabled, the list above will typically be

View file

@ -293,6 +293,14 @@ func (b *batch) finish(fns []*ir.Func) {
// TODO(mdempsky): Update tests to expect this.
goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
if n.Op() == ir.OCONVIDATA && n.(*ir.ConvExpr).NonEscaping {
// The allocation for the data word of an interface is known to not escape.
// See issue 50182.
// (But we do still need to process that allocation, as pointers inside
// the data word may escape.)
loc.escapes = false
}
if loc.escapes {
if n.Op() == ir.ONAME {
if base.Flag.CompilingRuntime {

View file

@ -31,7 +31,7 @@ func dumpasmhdr() {
if t == constant.Float || t == constant.Complex {
break
}
fmt.Fprintf(b, "#define const_%s %v\n", n.Sym().Name, n.Val())
fmt.Fprintf(b, "#define const_%s %v\n", n.Sym().Name, n.Val().ExactString())
case ir.OTYPE:
t := n.Type()

View file

@ -35,18 +35,18 @@ import (
"sort"
)
func hidePanic() {
if base.Debug.Panic == 0 && base.Errors() > 0 {
// If we've already complained about things
// in the program, don't bother complaining
// about a panic too; let the user clean up
// the code and try again.
if err := recover(); err != nil {
if err == "-h" {
panic(err)
}
base.ErrorExit()
// handlePanic ensures that we print out an "internal compiler error" for any panic
// or runtime exception during front-end compiler processing (unless there have
// already been some compiler errors). It may also be invoked from the explicit panic in
// hcrash(), in which case, we pass the panic on through.
func handlePanic() {
if err := recover(); err != nil {
if err == "-h" {
// Force real panic now with -h option (hcrash) - the error
// information will have already been printed.
panic(err)
}
base.Fatalf("panic: %v", err)
}
}
@ -56,7 +56,7 @@ func hidePanic() {
func Main(archInit func(*ssagen.ArchInfo)) {
base.Timer.Start("fe", "init")
defer hidePanic()
defer handlePanic()
archInit(&ssagen.Arch)
@ -245,11 +245,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
base.Timer.Start("fe", "inlining")
if base.Flag.LowerL != 0 {
inline.InlinePackage()
// If any new fully-instantiated types were referenced during
// inlining, we need to create needed instantiations.
if len(typecheck.GetInstTypeList()) > 0 {
noder.BuildInstantiations(false)
}
}
noder.MakeWrappers(typecheck.Target) // must happen after inlining

View file

@ -8,6 +8,7 @@ import (
"bytes"
"cmd/compile/internal/types2"
"fmt"
"internal/goexperiment"
"internal/testenv"
"os"
"os/exec"
@ -107,25 +108,29 @@ func TestImportTestdata(t *testing.T) {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
}
tmpdir := mktmpdir(t)
defer os.RemoveAll(tmpdir)
testfiles := map[string][]string{
"exports.go": {"go/ast", "go/token"},
}
if !goexperiment.Unified {
testfiles["generics.go"] = nil
}
compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
for testfile, wantImports := range testfiles {
tmpdir := mktmpdir(t)
defer os.RemoveAll(tmpdir)
if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
// The package's Imports list must include all packages
// explicitly imported by exports.go, plus all packages
// referenced indirectly via exported objects in exports.go.
// With the textual export format, the list may also include
// additional packages that are not strictly required for
// import processing alone (they are exported to err "on
// the safe side").
// TODO(gri) update the want list to be precise, now that
// the textual export data is gone.
got := fmt.Sprint(pkg.Imports())
for _, want := range []string{"go/ast", "go/token"} {
if !strings.Contains(got, want) {
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
if pkg := testPath(t, path, tmpdir); pkg != nil {
// The package's Imports list must include all packages
// explicitly imported by testfile, plus all packages
// referenced indirectly via exported objects in testfile.
got := fmt.Sprint(pkg.Imports())
for _, want := range wantImports {
if !strings.Contains(got, want) {
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
}
}
}
}
@ -253,7 +258,7 @@ var importedObjectTests = []struct {
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
// interfaces
{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},

View file

@ -9,6 +9,7 @@ package importer
import (
"cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types2"
"encoding/binary"
"fmt"
@ -45,7 +46,7 @@ func (r *intReader) uint64() uint64 {
const (
iexportVersionGo1_11 = 0
iexportVersionPosCol = 1
iexportVersionGenerics = 1 // probably change to 2 before release
iexportVersionGenerics = 2
iexportVersionGo1_18 = 2
iexportVersionCurrent = 2
@ -126,7 +127,7 @@ func ImportData(imports map[string]*types2.Package, data, path string) (pkg *typ
typCache: make(map[uint64]types2.Type),
// Separate map for typeparams, keyed by their package and unique
// name (name with subscript).
tparamIndex: make(map[ident]types2.Type),
tparamIndex: make(map[ident]*types2.TypeParam),
}
for i, pt := range predeclared {
@ -202,7 +203,7 @@ type iimporter struct {
declData string
pkgIndex map[*types2.Package]map[string]uint64
typCache map[uint64]types2.Type
tparamIndex map[ident]types2.Type
tparamIndex map[ident]*types2.TypeParam
interfaceList []*types2.Interface
}
@ -259,7 +260,7 @@ func (p *iimporter) posBaseAt(off uint64) *syntax.PosBase {
}
func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
if t, ok := p.typCache[off]; ok && canReuse(base, t) {
return t
}
@ -274,12 +275,30 @@ func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
r.declReader = *strings.NewReader(p.declData[off-predeclReserved:])
t := r.doType(base)
if base == nil || !isInterface(t) {
if canReuse(base, t) {
p.typCache[off] = t
}
return t
}
// canReuse reports whether the type rhs on the RHS of the declaration for def
// may be re-used.
//
// Specifically, if def is non-nil and rhs is an interface type with methods, it
// may not be re-used because we have a convention of setting the receiver type
// for interface methods to def.
func canReuse(def *types2.Named, rhs types2.Type) bool {
if def == nil {
return true
}
iface, _ := rhs.(*types2.Interface)
if iface == nil {
return true
}
// Don't use iface.Empty() here as iface may not be complete.
return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
}
type importReader struct {
p *iimporter
declReader strings.Reader
@ -358,12 +377,12 @@ func (r *importReader) obj(name string) {
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected type param type")
}
// Remove the "path" from the type param name that makes it unique
ix := strings.LastIndex(name, ".")
if ix < 0 {
errorf("missing path for type param")
name0 := typecheck.TparamName(name)
if name0 == "" {
errorf("malformed type parameter export name %s: missing prefix", name)
}
tn := types2.NewTypeName(pos, r.currPkg, name[ix+1:], nil)
tn := types2.NewTypeName(pos, r.currPkg, name0, nil)
t := types2.NewTypeParam(tn, nil)
// To handle recursive references to the typeparam within its
// bound, save the partial type in tparamIndex before reading the bounds.
@ -706,8 +725,7 @@ func (r *importReader) tparamList() []*types2.TypeParam {
}
xs := make([]*types2.TypeParam, n)
for i := range xs {
typ := r.typ()
xs[i] = types2.AsTypeParam(typ)
xs[i] = r.typ().(*types2.TypeParam)
}
return xs
}

View file

@ -118,10 +118,14 @@ var predeclared = []types2.Type{
types2.Typ[types2.Invalid], // only appears in packages with errors
// used internally by gc; never used by this package or in .a files
// not to be confused with the universe any
anyType{},
// comparable
types2.Universe.Lookup("comparable").Type(),
// any
types2.Universe.Lookup("any").Type(),
}
type anyType struct{}

View file

@ -15,14 +15,17 @@ const init1 = 0
func init() {}
const (
C0 int = 0
C1 = 3.14159265
C2 = 2.718281828i
C3 = -123.456e-789
C4 = +123.456e+789
C5 = 1234i
C6 = "foo\n"
C7 = `bar\n`
C0 int = 0
C1 = 3.14159265
C2 = 2.718281828i
C3 = -123.456e-789
C4 = +123.456e+789
C5 = 1234i
C6 = "foo\n"
C7 = `bar\n`
C8 = 42
C9 int = 42
C10 float64 = 42
)
type (

View file

@ -0,0 +1,29 @@
// Copyright 2021 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.
// This file is used to generate an object file which
// serves as test file for gcimporter_test.go.
package generics
type Any any
var x any
type T[A, B any] struct {
Left A
Right B
}
var X T[int, string] = T[int, string]{1, "hi"}
func ToInt[P interface{ ~int }](p P) int { return int(p) }
var IntID = ToInt[int]
type G[C comparable] int
func ImplicitFunc[T ~int]() {}
type ImplicitType[T ~int] int

View file

@ -1108,11 +1108,15 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
// closure does the necessary substitions for a ClosureExpr n and returns the new
// closure node.
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
// Prior to the subst edit, set a flag in the inlsubst to
// indicated that we don't want to update the source positions in
// the new closure. If we do this, it will appear that the closure
// itself has things inlined into it, which is not the case. See
// issue #46234 for more details.
// Prior to the subst edit, set a flag in the inlsubst to indicate
// that we don't want to update the source positions in the new
// closure function. If we do this, it will appear that the
// closure itself has things inlined into it, which is not the
// case. See issue #46234 for more details. At the same time, we
// do want to update the position in the new ClosureExpr (which is
// part of the function we're working on). See #49171 for an
// example of what happens if we miss that update.
newClosurePos := subst.updatedPos(n.Pos())
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
subst.noPosUpdate = true
@ -1175,6 +1179,7 @@ func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
// Actually create the named function for the closure, now that
// the closure is inlined in a specific function.
newclo := newfn.OClosure
newclo.SetPos(newClosurePos)
newclo.SetInit(subst.list(n.Init()))
return typecheck.Expr(newclo)
}

View file

@ -250,7 +250,8 @@ func (n *ConstExpr) Val() constant.Value { return n.val }
// It may end up being a value or a type.
type ConvExpr struct {
miniExpr
X Node
X Node
NonEscaping bool // The allocation needed for the conversion to interface is known not to escape
}
func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr {

View file

@ -471,7 +471,7 @@ const (
UintptrEscapes // pointers converted to uintptr escape
// Runtime-only func pragmas.
// See ../../../../runtime/README.md for detailed descriptions.
// See ../../../../runtime/HACKING.md for detailed descriptions.
Systemstack // func must run on system stack
Nowritebarrier // emit compiler error instead of write barrier
Nowritebarrierrec // error on write barrier in this or recursive callees
@ -584,7 +584,7 @@ func OuterValue(n Node) Node {
for {
switch nn := n; nn.Op() {
case OXDOT:
base.FatalfAt(n.Pos(), "OXDOT in walk: %v", n)
base.FatalfAt(n.Pos(), "OXDOT in OuterValue: %v", n)
case ODOT:
nn := nn.(*SelectorExpr)
n = nn.X

View file

@ -320,7 +320,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPS64MOVVreg {
a = a.Args[0]
}
if a.Op == ssa.OpLoadReg {
if a.Op == ssa.OpLoadReg && mips.REG_R0 <= a.Reg() && a.Reg() <= mips.REG_R31 {
// LoadReg from a narrower type does an extension, except loading
// to a floating point register. So only eliminate the extension
// if it is loaded to an integer register.
t := a.Type
switch {
case v.Op == ssa.OpMIPS64MOVBreg && t.Size() == 1 && t.IsSigned(),

View file

@ -86,6 +86,7 @@ func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
}
func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
assert(g.curDecl == "")
// Set g.curDecl to the function name, as context for the type params declared
// during types2-to-types1 translation if this is a generic function.
g.curDecl = decl.Name.Value
@ -94,7 +95,7 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
if recv != nil {
t2 := deref2(recv.Type())
// This is a method, so set g.curDecl to recvTypeName.methName instead.
g.curDecl = types2.AsNamed(t2).Obj().Name() + "." + g.curDecl
g.curDecl = t2.(*types2.Named).Obj().Name() + "." + g.curDecl
}
fn := ir.NewFunc(g.pos(decl))
@ -132,11 +133,20 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
g.target.Inits = append(g.target.Inits, fn)
}
haveEmbed := g.haveEmbed
saveHaveEmbed := g.haveEmbed
saveCurDecl := g.curDecl
g.curDecl = ""
g.later(func() {
defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
defer func(b bool, s string) {
// Revert haveEmbed and curDecl back to what they were before
// the "later" function.
g.haveEmbed = b
g.curDecl = s
}(g.haveEmbed, g.curDecl)
g.haveEmbed = haveEmbed
// Set haveEmbed and curDecl to what they were for this funcDecl.
g.haveEmbed = saveHaveEmbed
g.curDecl = saveCurDecl
if fn.Type().HasTParam() {
g.topFuncIsGeneric = true
}
@ -158,8 +168,12 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
}
func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
// Set the position for any error messages we might print (e.g. too large types).
base.Pos = g.pos(decl)
assert(ir.CurFunc != nil || g.curDecl == "")
// Set g.curDecl to the type name, as context for the type params declared
// during types2-to-types1 translation if this is a generic type.
saveCurDecl := g.curDecl
g.curDecl = decl.Name.Value
if decl.Alias {
name, _ := g.def(decl.Name)
@ -167,6 +181,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
assert(name.Alias()) // should be set by irgen.obj
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
g.curDecl = ""
return
}
@ -219,6 +234,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
}
types.ResumeCheckSize()
g.curDecl = saveCurDecl
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
methods := make([]*types.Field, otyp.NumMethods())
for i := range methods {
@ -229,6 +245,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
meth := g.obj(m)
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
methods[i].Nname = meth
g.curDecl = ""
}
ntyp.Methods().Set(methods)
}
@ -238,6 +255,8 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
pos := g.pos(decl)
// Set the position for any error messages we might print (e.g. too large types).
base.Pos = pos
names := make([]*ir.Name, len(decl.NameList))
for i, name := range decl.NameList {
names[i], _ = g.def(name)
@ -276,22 +295,26 @@ func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
} else if ir.CurFunc == nil {
name.Defn = as
}
lhs := []ir.Node{as.X}
rhs := []ir.Node{}
if as.Y != nil {
rhs = []ir.Node{as.Y}
}
transformAssign(as, lhs, rhs)
as.X = lhs[0]
if as.Y != nil {
as.Y = rhs[0]
if !g.delayTransform() {
lhs := []ir.Node{as.X}
rhs := []ir.Node{}
if as.Y != nil {
rhs = []ir.Node{as.Y}
}
transformAssign(as, lhs, rhs)
as.X = lhs[0]
if as.Y != nil {
as.Y = rhs[0]
}
}
as.SetTypecheck(1)
out.Append(as)
}
}
if as2 != nil {
transformAssign(as2, as2.Lhs, as2.Rhs)
if !g.delayTransform() {
transformAssign(as2, as2.Lhs, as2.Rhs)
}
as2.SetTypecheck(1)
out.Append(as2)
}

View file

@ -266,7 +266,7 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
if wantPtr {
recvType2Base = types2.AsPointer(recvType2).Elem()
}
if types2.AsNamed(recvType2Base).TypeParams().Len() > 0 {
if recvType2Base.(*types2.Named).TypeParams().Len() > 0 {
// recvType2 is the original generic type that is
// instantiated for this method call.
// selinfo.Recv() is the instantiated type
@ -332,13 +332,13 @@ func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node {
}
func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
if ptr, ok := types2.StructuralType(typ).(*types2.Pointer); ok {
n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
n.SetOp(ir.OPTRLIT)
return typed(g.typ(typ), n)
}
_, isStruct := types2.Structure(typ).(*types2.Struct)
_, isStruct := types2.StructuralType(typ).(*types2.Struct)
exprs := make([]ir.Node, len(lit.ElemList))
for i, elem := range lit.ElemList {

View file

@ -96,6 +96,17 @@ func check2(noders []*noder) {
}
}
// Information about sub-dictionary entries in a dictionary
type subDictInfo struct {
// Call or XDOT node that requires a dictionary.
callNode ir.Node
// Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic
// method or function call, since this node will get dropped when the generic
// method/function call is transformed to a call on the instantiated shape
// function. Nil for other kinds of calls or XDOTs.
savedXNode ir.Node
}
// dictInfo is the dictionary format for an instantiation of a generic function with
// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
// the actual dictionary entries in order, and the remaining fields are other info
@ -108,7 +119,7 @@ type dictInfo struct {
// Nodes in the instantiation that requires a subdictionary. Includes
// method and function calls (OCALL), function values (OFUNCINST), method
// values/expressions (OXDOT).
subDictCalls []ir.Node
subDictCalls []subDictInfo
// Nodes in the instantiation that are a conversion from a typeparam/derived
// type to a specific interface.
itabConvs []ir.Node
@ -317,7 +328,7 @@ Outer:
// Create any needed instantiations of generic functions and transform
// existing and new functions to use those instantiations.
BuildInstantiations(true)
BuildInstantiations()
// Remove all generic functions from g.target.Decl, since they have been
// used for stenciling, but don't compile. Generic functions will already

View file

@ -2029,7 +2029,7 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
// Quirk: If deadcode elimination turned a non-empty function into
// an empty one, we need to set the position for the empty block
// left behind to the the inlined position for src.NoXPos, so that
// left behind to the inlined position for src.NoXPos, so that
// an empty string gets added into the DWARF file name listing at
// the appropriate index.
if quirksMode() && len(body) == 1 {

View file

@ -250,7 +250,7 @@ func (r *reader2) doTyp() (res types2.Type) {
case typePointer:
return types2.NewPointer(r.typ())
case typeSignature:
return r.signature(nil)
return r.signature(nil, nil, nil)
case typeSlice:
return types2.NewSlice(r.typ())
case typeStruct:
@ -298,7 +298,7 @@ func (r *reader2) interfaceType() *types2.Interface {
for i := range methods {
pos := r.pos()
pkg, name := r.selector()
mtyp := r.signature(nil)
mtyp := r.signature(nil, nil, nil)
methods[i] = types2.NewFunc(pos, pkg, name, mtyp)
}
@ -309,14 +309,14 @@ func (r *reader2) interfaceType() *types2.Interface {
return types2.NewInterfaceType(methods, embeddeds)
}
func (r *reader2) signature(recv *types2.Var) *types2.Signature {
func (r *reader2) signature(recv *types2.Var, rtparams, tparams []*types2.TypeParam) *types2.Signature {
r.sync(syncSignature)
params := r.params()
results := r.params()
variadic := r.bool()
return types2.NewSignatureType(recv, nil, nil, params, results, variadic)
return types2.NewSignatureType(recv, rtparams, tparams, params, results, variadic)
}
func (r *reader2) params() *types2.Tuple {
@ -393,8 +393,7 @@ func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) {
case objFunc:
pos := r.pos()
tparams := r.typeParamNames()
sig := r.signature(nil)
sig.SetTypeParams(tparams)
sig := r.signature(nil, nil, tparams)
return types2.NewFunc(pos, objPkg, objName, sig)
case objType:
@ -490,9 +489,8 @@ func (r *reader2) method() *types2.Func {
pos := r.pos()
pkg, name := r.selector()
rparams := r.typeParamNames()
sig := r.signature(r.param())
sig.SetRecvTypeParams(rparams)
rtparams := r.typeParamNames()
sig := r.signature(r.param(), rtparams, nil)
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
return types2.NewFunc(pos, pkg, name, sig)

View file

@ -9,7 +9,6 @@ package noder
import (
"cmd/compile/internal/base"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
@ -22,7 +21,7 @@ import (
)
// Enable extra consistency checks.
const doubleCheck = true
const doubleCheck = false
func assert(p bool) {
base.Assert(p)
@ -40,34 +39,29 @@ func infoPrint(format string, a ...interface{}) {
var geninst genInst
func BuildInstantiations(preinliningMainScan bool) {
if geninst.instInfoMap == nil {
geninst.instInfoMap = make(map[*types.Sym]*instInfo)
}
geninst.buildInstantiations(preinliningMainScan)
func BuildInstantiations() {
geninst.instInfoMap = make(map[*types.Sym]*instInfo)
geninst.buildInstantiations()
geninst.instInfoMap = nil
}
// buildInstantiations scans functions for generic function calls and methods, and
// creates the required instantiations. It also creates instantiated methods for all
// fully-instantiated generic types that have been encountered already or new ones
// that are encountered during the instantiation process. If preinliningMainScan is
// true, it scans all declarations in typecheck.Target.Decls first, before scanning
// any new instantiations created. If preinliningMainScan is false, we do not scan
// any existing decls - we only scan method instantiations for any new
// fully-instantiated types that we saw during inlining.
func (g *genInst) buildInstantiations(preinliningMainScan bool) {
// that are encountered during the instantiation process. It scans all declarations
// in typecheck.Target.Decls first, before scanning any new instantiations created.
func (g *genInst) buildInstantiations() {
// Instantiate the methods of instantiated generic types that we have seen so far.
g.instantiateMethods()
if preinliningMainScan {
n := len(typecheck.Target.Decls)
for i := 0; i < n; i++ {
g.scanForGenCalls(typecheck.Target.Decls[i])
}
// Scan all currentdecls for call to generic functions/methods.
n := len(typecheck.Target.Decls)
for i := 0; i < n; i++ {
g.scanForGenCalls(typecheck.Target.Decls[i])
}
// Scan all new instantiations created due to g.instantiateMethods() and the
// scan of current decls (if done). This loop purposely runs until no new
// scan of current decls. This loop purposely runs until no new
// instantiations are created.
for i := 0; i < len(g.newInsts); i++ {
g.scanForGenCalls(g.newInsts[i])
@ -82,10 +76,6 @@ func (g *genInst) buildInstantiations(preinliningMainScan bool) {
for _, fun := range g.newInsts {
info := g.instInfoMap[fun.Sym()]
g.dictPass(info)
if !preinliningMainScan {
// Prepare for the round of inlining below.
inline.CanInline(fun.(*ir.Func))
}
if doubleCheck {
ir.Visit(info.fun, func(n ir.Node) {
if n.Op() != ir.OCONVIFACE {
@ -103,14 +93,6 @@ func (g *genInst) buildInstantiations(preinliningMainScan bool) {
ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
}
}
if !preinliningMainScan {
// Extra round of inlining for the new instantiations (only if
// preinliningMainScan is false, which means we have already done the
// main round of inlining)
for _, fun := range g.newInsts {
inline.InlineCalls(fun.(*ir.Func))
}
}
assert(l == len(g.newInsts))
g.newInsts = nil
}
@ -500,7 +482,7 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// explicitly traverse any embedded fields in the receiver
// argument in order to call the method instantiation.
arg0 := formalParams[0].Nname.(ir.Node)
arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(x.Pos(), ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
if valueMethod && arg0.Type().IsPtr() {
// For handling the (*T).M case: if we have a pointer
// receiver after following all the embedded fields,
@ -515,6 +497,7 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// Build call itself.
var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic()
if len(formalResults) > 0 {
innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
}
@ -601,7 +584,7 @@ func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.N
if declInfo != nil {
entry := -1
for i, de := range declInfo.dictInfo.subDictCalls {
if n == de {
if n == de.callNode {
entry = declInfo.dictInfo.startSubDict + i
break
}
@ -615,7 +598,7 @@ func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.N
}
}
if !usingSubdict {
dict = g.getDictionaryValue(nameNode, targs, isMeth)
dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth)
}
return dict, usingSubdict
}
@ -651,17 +634,38 @@ func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMe
checkFetchBody(nameNode)
}
var tparams []*types.Type
if isMeth {
// Get the type params from the method receiver (after skipping
// over any pointer)
recvType := nameNode.Type().Recv().Type
recvType = deref(recvType)
tparams = recvType.RParams()
} else {
fields := nameNode.Type().TParams().Fields().Slice()
tparams = make([]*types.Type, len(fields))
for i, f := range fields {
tparams[i] = f.Type
}
}
// Convert any non-shape type arguments to their shape, so we can reduce the
// number of instantiations we have to generate. You can actually have a mix
// of shape and non-shape arguments, because of inferred or explicitly
// specified concrete type args.
s1 := make([]*types.Type, len(shapes))
for i, t := range shapes {
var tparam *types.Type
if tparams[i].Kind() == types.TTYPEPARAM {
// Shapes are grouped differently for structural types, so we
// pass the type param to Shapify(), so we can distinguish.
tparam = tparams[i]
}
if !t.IsShape() {
s1[i] = typecheck.Shapify(t, i)
s1[i] = typecheck.Shapify(t, i, tparam)
} else {
// Already a shape, but make sure it has the correct index.
s1[i] = typecheck.Shapify(shapes[i].Underlying(), i)
s1[i] = typecheck.Shapify(shapes[i].Underlying(), i, tparam)
}
}
shapes = s1
@ -676,8 +680,23 @@ func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMe
}
info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)
// genericSubst fills in info.dictParam and info.tparamToBound.
st := g.genericSubst(sym, nameNode, shapes, isMeth, info)
if sym.Def != nil {
// This instantiation must have been imported from another
// package (because it was needed for inlining), so we should
// not re-generate it and have conflicting definitions for the
// symbol (issue #50121). It will have already gone through the
// dictionary transformations of dictPass, so we don't actually
// need the info.dictParam and info.shapeToBound info filled in
// below. We just set the imported instantiation as info.fun.
assert(sym.Pkg != types.LocalPkg)
info.fun = sym.Def.(*ir.Name).Func
assert(info.fun != nil)
g.instInfoMap[sym] = info
return info
}
// genericSubst fills in info.dictParam and info.shapeToBound.
st := g.genericSubst(sym, nameNode, tparams, shapes, isMeth, info)
info.fun = st
g.instInfoMap[sym] = info
@ -714,22 +733,8 @@ type subster struct {
// args shapes. For a method with a generic receiver, it returns an instantiated
// function type where the receiver becomes the first parameter. For either a generic
// method or function, a dictionary parameter is the added as the very first
// parameter. genericSubst fills in info.dictParam and info.tparamToBound.
func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
var tparams []*types.Type
if isMethod {
// Get the type params from the method receiver (after skipping
// over any pointer)
recvType := nameNode.Type().Recv().Type
recvType = deref(recvType)
tparams = recvType.RParams()
} else {
fields := nameNode.Type().TParams().Fields().Slice()
tparams = make([]*types.Type, len(fields))
for i, f := range fields {
tparams[i] = f.Type
}
}
// parameter. genericSubst fills in info.dictParam and info.shapeToBound.
func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
gf := nameNode.Func
// Pos of the instantiated function is same as the generic function
newf := ir.NewFunc(gf.Pos())
@ -801,11 +806,12 @@ func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*t
// Make sure name/type of newf is set before substituting the body.
newf.Body = subst.list(gf.Body)
// Add code to check that the dictionary is correct.
// TODO: must be adjusted to deal with shapes, but will go away soon when we move
// to many->1 shape to concrete mapping.
// newf.Body.Prepend(subst.checkDictionary(dictionaryName, shapes)...)
if len(newf.Body) == 0 {
// Ensure the body is nonempty, for issue 49524.
// TODO: have some other way to detect the difference between
// a function declared with no body, vs. one with an empty body?
newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil))
}
if len(subst.defnMap) > 0 {
base.Fatalf("defnMap is not empty")
@ -859,49 +865,6 @@ func (subst *subster) localvar(name *ir.Name) *ir.Name {
return m
}
// checkDictionary returns code that does runtime consistency checks
// between the dictionary and the types it should contain.
func (subst *subster) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.Node) {
if false {
return // checking turned off
}
// TODO: when moving to GCshape, this test will become harder. Call into
// runtime to check the expected shape is correct?
pos := name.Pos()
// Convert dictionary to *[N]uintptr
d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], name)
d.SetTypecheck(1)
d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(targs))).PtrTo(), d)
d.SetTypecheck(1)
types.CheckSize(d.Type().Elem())
// Check that each type entry in the dictionary is correct.
for i, t := range targs {
if t.HasShape() {
// Check the concrete type, not the shape type.
base.Fatalf("shape type in dictionary %s %+v\n", name.Sym().Name, t)
}
want := reflectdata.TypePtr(t)
typed(types.Types[types.TUINTPTR], want)
deref := ir.NewStarExpr(pos, d)
typed(d.Type().Elem(), deref)
idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), name) // TODO: what to set orig to?
typed(types.Types[types.TUINTPTR], idx)
got := ir.NewIndexExpr(pos, deref, idx)
typed(types.Types[types.TUINTPTR], got)
cond := ir.NewBinaryExpr(pos, ir.ONE, want, got)
typed(types.Types[types.TBOOL], cond)
panicArg := ir.NewNilExpr(pos)
typed(types.NewInterface(types.LocalPkg, nil, false), panicArg)
then := ir.NewUnaryExpr(pos, ir.OPANIC, panicArg)
then.SetTypecheck(1)
x := ir.NewIfStmt(pos, cond, []ir.Node{then}, nil)
x.SetTypecheck(1)
code = append(code, x)
}
return
}
// getDictionaryEntry gets the i'th entry in the dictionary dict.
func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
// Convert dictionary to *[N]uintptr
@ -946,6 +909,10 @@ func (subst *subster) node(n ir.Node) ir.Node {
// Use closure to capture all state needed by the ir.EditChildren argument.
var edit func(ir.Node) ir.Node
edit = func(x ir.Node) ir.Node {
// Analogous to ir.SetPos() at beginning of typecheck.typecheck() -
// allows using base.Pos during the transform functions, just like
// the tc*() functions.
ir.SetPos(x)
switch x.Op() {
case ir.OTYPE:
return ir.TypeNode(subst.ts.Typ(x.Type()))
@ -1069,13 +1036,13 @@ func (subst *subster) node(n ir.Node) ir.Node {
}
case ir.OXDOT:
// Finish the transformation of an OXDOT, unless this was a
// bound call (a direct call on a type param). A bound call
// will be transformed during the dictPass. Otherwise, m
// will be transformed to an OMETHVALUE node. It will be
// transformed to an ODOTMETH or ODOTINTER node if we find in
// the OCALL case below that the method value is actually
// called.
// Finish the transformation of an OXDOT, unless this is
// bound call or field access on a type param. A bound call
// or field access on a type param will be transformed during
// the dictPass. Otherwise, m will be transformed to an
// OMETHVALUE node. It will be transformed to an ODOTMETH or
// ODOTINTER node if we find in the OCALL case below that the
// method value is actually called.
mse := m.(*ir.SelectorExpr)
if src := mse.X.Type(); !src.IsShape() {
transformDot(mse, false)
@ -1134,8 +1101,12 @@ func (subst *subster) node(n ir.Node) ir.Node {
transformEarlyCall(call)
case ir.OXDOT:
// This is the case of a bound call on a typeparam,
// which will be handled in the dictPass.
// This is the case of a bound call or a field access
// on a typeparam, which will be handled in the
// dictPass. As with OFUNCINST, we must transform the
// arguments of the call now, so any needed CONVIFACE
// nodes are exposed.
transformEarlyCall(call)
case ir.ODOTTYPE, ir.ODOTTYPE2:
// These are DOTTYPEs that could get transformed into
@ -1245,39 +1216,53 @@ func (g *genInst) dictPass(info *instInfo) {
ir.CurFunc = info.fun
case ir.OXDOT:
// This is the case of a dot access on a type param. This is
// typically a bound call on the type param, but could be a
// field access, if the constraint has a single structural type.
mse := m.(*ir.SelectorExpr)
src := mse.X.Type()
assert(src.IsShape())
// The only dot on a shape type value are methods.
if mse.X.Op() == ir.OTYPE {
// Method expression T.M
m = g.buildClosure2(info, m)
// No need for transformDot - buildClosure2 has already
// transformed to OCALLINTER/ODOTINTER.
} else {
// Implement x.M as a conversion-to-bound-interface
// 1) convert x to the bound interface
// 2) call M on that interface
dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
if src.IsInterface() {
// If type arg is an interface (unusual case),
// we do a type assert to the type bound.
mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
} else {
mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst)
// If we can't find the selected method in the
// AllMethods of the bound, then this must be an access
// to a field of a structural type. If so, we skip the
// dictionary lookups - transformDot() will convert to
// the desired direct field access.
if isBoundMethod(info.dictInfo, mse) {
dst := info.dictInfo.shapeToBound[mse.X.Type()]
// Implement x.M as a conversion-to-bound-interface
// 1) convert x to the bound interface
// 2) call M on that interface
if src.IsInterface() {
// If type arg is an interface (unusual case),
// we do a type assert to the type bound.
mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
} else {
mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst, true)
// Note: we set nonEscaping==true, because we can assume the backing store for the
// interface conversion doesn't escape. The method call will immediately go to
// a wrapper function which copies all the data out of the interface value.
// (It only matters for non-pointer-shaped interface conversions. See issue 50182.)
}
}
transformDot(mse, false)
}
case ir.OCALL:
op := m.(*ir.CallExpr).X.Op()
call := m.(*ir.CallExpr)
op := call.X.Op()
if op == ir.OMETHVALUE {
// Redo the transformation of OXDOT, now that we
// know the method value is being called.
m.(*ir.CallExpr).X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
transformDot(m.(*ir.CallExpr).X.(*ir.SelectorExpr), true)
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
transformDot(call.X.(*ir.SelectorExpr), true)
}
transformCall(m.(*ir.CallExpr))
transformCall(call)
case ir.OCONVIFACE:
if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
@ -1290,7 +1275,7 @@ func (g *genInst) dictPass(info *instInfo) {
// Note: x's argument is still typed as a type parameter.
// m's argument now has an instantiated type.
if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type())
m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type(), false)
}
case ir.ODOTTYPE, ir.ODOTTYPE2:
if !m.Type().HasShape() {
@ -1383,7 +1368,9 @@ func findDictType(info *instInfo, t *types.Type) int {
// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the
// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the
// conversion.
func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node {
// If nonEscaping is true, the caller guarantees that the backing store needed for the interface data
// word will not escape.
func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type, nonEscaping bool) ir.Node {
assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
assert(dst.IsInterface())
@ -1453,6 +1440,7 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v
// Figure out what the data field of the interface will be.
data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
typed(types.Types[types.TUNSAFEPTR], data)
data.NonEscaping = nonEscaping
// Build an interface from the type and data parts.
var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
@ -1581,8 +1569,9 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
markTypeUsed(ts, lsym)
}
// Emit an entry for each subdictionary (after substituting targs)
for _, n := range info.subDictCalls {
for _, subDictInfo := range info.subDictCalls {
var sym *types.Sym
n := subDictInfo.callNode
switch n.Op() {
case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
call := n.(*ir.CallExpr)
@ -1592,9 +1581,9 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
if se.X.Type().IsShape() {
// This is a method call enabled by a type bound.
// We need this extra check for type expressions, which
// don't add in the implicit XDOTs.
tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel)
// We need this extra check for method expressions,
// which don't add in the implicit XDOTs.
tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
tmpse = typecheck.AddImplicitDots(tmpse)
tparam := tmpse.X.Type()
if !tparam.IsShape() {
@ -1629,31 +1618,31 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
} else {
// This is the case of a normal
// method call on a generic type.
recvType := deref(call.X.(*ir.SelectorExpr).X.Type())
genRecvType := recvType.OrigSym().Def.Type()
nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
subtargs := recvType.RParams()
s2targs := make([]*types.Type, len(subtargs))
for i, t := range subtargs {
s2targs[i] = subst.Typ(t)
}
sym = g.getDictionarySym(nameNode, s2targs, true)
assert(subDictInfo.savedXNode == se)
sym = g.getSymForMethodCall(se, &subst)
}
} else {
inst := call.X.(*ir.InstExpr)
var nameNode *ir.Name
var meth *ir.SelectorExpr
var isMeth bool
if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth {
nameNode = meth.Selection.Nname.(*ir.Name)
inst, ok := call.X.(*ir.InstExpr)
if ok {
// Code hasn't been transformed yet
assert(subDictInfo.savedXNode == inst)
}
// If !ok, then the generic method/function call has
// already been transformed to a shape instantiation
// call. Either way, use the SelectorExpr/InstExpr
// node saved in info.
cex := subDictInfo.savedXNode
if se, ok := cex.(*ir.SelectorExpr); ok {
sym = g.getSymForMethodCall(se, &subst)
} else {
nameNode = inst.X.(*ir.Name)
inst := cex.(*ir.InstExpr)
nameNode := inst.X.(*ir.Name)
subtargs := typecheck.TypesOf(inst.Targs)
for i, t := range subtargs {
subtargs[i] = subst.Typ(t)
}
sym = g.getDictionarySym(nameNode, subtargs, false)
}
subtargs := typecheck.TypesOf(inst.Targs)
for i, t := range subtargs {
subtargs[i] = subst.Typ(t)
}
sym = g.getDictionarySym(nameNode, subtargs, isMeth)
}
case ir.OFUNCINST:
@ -1666,16 +1655,7 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
sym = g.getDictionarySym(nameNode, subtargs, false)
case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
selExpr := n.(*ir.SelectorExpr)
recvType := deref(selExpr.Selection.Type.Recv().Type)
genRecvType := recvType.OrigSym().Def.Type()
subtargs := recvType.RParams()
s2targs := make([]*types.Type, len(subtargs))
for i, t := range subtargs {
s2targs[i] = subst.Typ(t)
}
nameNode := typecheck.Lookdot1(selExpr, selExpr.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
sym = g.getDictionarySym(nameNode, s2targs, true)
sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst)
default:
assert(false)
@ -1703,6 +1683,24 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
return sym
}
// getSymForMethodCall gets the dictionary sym for a method call, method value, or method
// expression that has selector se. subst gives the substitution from shape types to
// concrete types.
func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym {
// For everything except method expressions, 'recvType = deref(se.X.Type)' would
// also give the receiver type. For method expressions with embedded types, we
// need to look at the type of the selection to get the final receiver type.
recvType := deref(se.Selection.Type.Recv().Type)
genRecvType := recvType.OrigSym().Def.Type()
nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
subtargs := recvType.RParams()
s2targs := make([]*types.Type, len(subtargs))
for i, t := range subtargs {
s2targs[i] = subst.Typ(t)
}
return g.getDictionarySym(nameNode, s2targs, true)
}
// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
// any needed LSyms for itabs. The itab lsyms create wrappers which need various
// dictionaries and method instantiations to be complete, so, to avoid recursive
@ -1762,7 +1760,7 @@ func (g *genInst) finalizeSyms() {
g.dictSymsToFinalize = nil
}
func (g *genInst) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
func (g *genInst) getDictionaryValue(pos src.XPos, gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
sym := g.getDictionarySym(gf, targs, isMeth)
// Make (or reuse) a node referencing the dictionary symbol.
@ -1770,15 +1768,18 @@ func (g *genInst) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bo
if sym.Def != nil {
n = sym.Def.(*ir.Name)
} else {
n = typecheck.NewName(sym)
// We set the position of a static dictionary to be the position of
// one of its uses.
n = ir.NewNameAt(pos, sym)
n.Curfn = ir.CurFunc
n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
n.SetTypecheck(1)
n.Class = ir.PEXTERN
sym.Def = n
}
// Return the address of the dictionary.
np := typecheck.NodAddr(n)
// Return the address of the dictionary. Addr node gets position that was passed in.
np := typecheck.NodAddrAt(pos, n)
// Note: treat dictionary pointers as uintptrs, so they aren't pointers
// with respect to GC. That saves on stack scanning work, write barriers, etc.
// We can get away with it because dictionaries are global variables.
@ -1847,7 +1848,7 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
case ir.OFUNCINST:
if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
info.subDictCalls = append(info.subDictCalls, n)
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
}
case ir.OMETHEXPR, ir.OMETHVALUE:
if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
@ -1858,7 +1859,7 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
} else {
infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
}
info.subDictCalls = append(info.subDictCalls, n)
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
}
case ir.OCALL:
ce := n.(*ir.CallExpr)
@ -1866,14 +1867,22 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
callMap[ce.X] = true
if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
info.subDictCalls = append(info.subDictCalls, n)
// Save the instExpr node for the function call,
// since we will lose this information when the
// generic function call is transformed to a call
// on the shape instantiation.
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
}
}
if ce.X.Op() == ir.OXDOT &&
isShapeDeref(ce.X.(*ir.SelectorExpr).X.Type()) {
// Note: this XDOT code is not actually needed as long as we
// continue to disable type parameters on RHS of type
// declarations (#45639).
if ce.X.Op() == ir.OXDOT {
callMap[ce.X] = true
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
info.subDictCalls = append(info.subDictCalls, n)
if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) {
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
}
}
case ir.OCALLMETH:
ce := n.(*ir.CallExpr)
@ -1882,7 +1891,11 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
callMap[ce.X] = true
if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
infoPrint(" Subdictionary at generic method call: %v\n", n)
info.subDictCalls = append(info.subDictCalls, n)
// Save the selector for the method call, since we
// will eventually lose this information when the
// generic method call is transformed into a
// function call on the method shape instantiation.
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
}
}
case ir.OCONVIFACE:
@ -1892,7 +1905,8 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
info.itabConvs = append(info.itabConvs, n)
}
case ir.OXDOT:
if n.(*ir.SelectorExpr).X.Type().IsShape() {
se := n.(*ir.SelectorExpr)
if isBoundMethod(info, se) {
infoPrint(" Itab for bound call: %v\n", n)
info.itabConvs = append(info.itabConvs, n)
}
@ -1948,11 +1962,13 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
}
// isShapeDeref returns true if t is either a shape or a pointer to a shape. (We
// can't just use deref(t).IsShape(), since a shape type is a complex type and may
// have a pointer as part of its shape.)
func isShapeDeref(t *types.Type) bool {
return t.IsShape() || t.IsPtr() && t.Elem().IsShape()
// isBoundMethod returns true if the selection indicated by se is a bound method of
// se.X. se.X must be a shape type (i.e. substituted directly from a type param). If
// isBoundMethod returns false, then the selection must be a field access of a
// structural type.
func isBoundMethod(info *dictInfo, se *ir.SelectorExpr) bool {
bound := info.shapeToBound[se.X.Type()]
return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil
}
// addType adds t to info.derivedTypes if it is parameterized type (which is not
@ -2085,6 +2101,7 @@ func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*t
fn.Dcl = append(fn.Dcl, arg)
f := types.NewField(pos, arg.Sym(), t)
f.Nname = arg
f.SetIsDDD(typ.Params().Field(i).IsDDD())
formalParams = append(formalParams, f)
}
for i := 0; i < typ.NumResults(); i++ {
@ -2161,7 +2178,7 @@ func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node {
// the type bound.
rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
} else {
rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst)
rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst, false)
}
dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*ir.SelectorExpr).Sel)
dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)

View file

@ -13,8 +13,10 @@ import (
"cmd/internal/src"
)
// stmts creates nodes for a slice of statements that form a scope.
func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
var nodes []ir.Node
types.Markdcl()
for _, stmt := range stmts {
switch s := g.stmt(stmt).(type) {
case nil: // EmptyStmt
@ -24,6 +26,7 @@ func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
nodes = append(nodes, s)
}
}
types.Popdcl()
return nodes
}
@ -46,6 +49,12 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
n.SetTypecheck(1)
return n
case *syntax.DeclStmt:
if g.topFuncIsGeneric && len(stmt.DeclList) > 0 {
if _, ok := stmt.DeclList[0].(*syntax.TypeDecl); ok {
// TODO: remove this restriction. See issue 47631.
base.ErrorfAt(g.pos(stmt), "type declarations inside generic functions are not currently supported")
}
}
n := ir.NewBlockStmt(g.pos(stmt), nil)
g.decls(&n.List, stmt.DeclList)
return n

View file

@ -115,6 +115,31 @@ func transformConv(n *ir.ConvExpr) ir.Node {
if n.X.Op() == ir.OLITERAL {
return stringtoruneslit(n)
}
case ir.OBYTES2STR:
assert(t.IsSlice())
assert(t.Elem().Kind() == types.TUINT8)
if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] {
// If t is a slice of a user-defined byte type B (not uint8
// or byte), then add an extra CONVNOP from []B to []byte, so
// that the call to slicebytetostring() added in walk will
// typecheck correctly.
n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X)
n.X.SetTypecheck(1)
}
case ir.ORUNES2STR:
assert(t.IsSlice())
assert(t.Elem().Kind() == types.TINT32)
if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] {
// If t is a slice of a user-defined rune type B (not uint32
// or rune), then add an extra CONVNOP from []B to []rune, so
// that the call to slicerunetostring() added in walk will
// typecheck correctly.
n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X)
n.X.SetTypecheck(1)
}
}
return n
}
@ -133,6 +158,9 @@ func transformConvCall(n *ir.CallExpr) ir.Node {
// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even
// in the case of OCALL/OFUNCINST.
func transformCall(n *ir.CallExpr) {
// Set base.Pos, since transformArgs below may need it, but transformCall
// is called in some passes that don't set base.Pos.
ir.SetPos(n)
// n.Type() can be nil for calls with no return value
assert(n.Typecheck() == 1)
transformArgs(n)
@ -328,6 +356,37 @@ assignOK:
}
checkLHS(0, r.Type())
checkLHS(1, types.UntypedBool)
t := lhs[0].Type()
if t != nil && rhs[0].Type().HasShape() && t.IsInterface() && !types.IdenticalStrict(t, rhs[0].Type()) {
// This is a multi-value assignment (map, channel, or dot-type)
// where the main result is converted to an interface during the
// assignment. Normally, the needed CONVIFACE is not created
// until (*orderState).as2ok(), because the AS2* ops and their
// sub-ops are so tightly intertwined. But we need to create the
// CONVIFACE now to enable dictionary lookups. So, assign the
// results first to temps, so that we can manifest the CONVIFACE
// in assigning the first temp to lhs[0]. If we added the
// CONVIFACE into rhs[0] directly, we would break a lot of later
// code that depends on the tight coupling between the AS2* ops
// and their sub-ops. (Issue #50642).
v := typecheck.Temp(rhs[0].Type())
ok := typecheck.Temp(types.Types[types.TBOOL])
as := ir.NewAssignListStmt(base.Pos, stmt.Op(), []ir.Node{v, ok}, []ir.Node{r})
as.Def = true
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, v))
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, ok))
as.SetTypecheck(1)
// Change stmt to be a normal assignment of the temps to the final
// left-hand-sides. We re-create the original multi-value assignment
// so that it assigns to the temps and add it as an init of stmt.
//
// TODO: fix the order of evaluation, so that the lval of lhs[0]
// is evaluated before rhs[0] (similar to problem in #50672).
stmt.SetOp(ir.OAS2)
stmt.PtrInit().Append(as)
// assignconvfn inserts the CONVIFACE.
stmt.Rhs = []ir.Node{assignconvfn(v, t), ok}
}
return
}

View file

@ -26,6 +26,8 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
return types.NewPkg(pkg.Path(), pkg.Name())
}
var universeAny = types2.Universe.Lookup("any").Type()
// typ converts a types2.Type to a types.Type, including caching of previously
// translated types.
func (g *irgen) typ(typ types2.Type) *types.Type {
@ -53,6 +55,12 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
// constructed part of a recursive type. Should not be called from outside this
// file (g.typ is the "external" entry point).
func (g *irgen) typ1(typ types2.Type) *types.Type {
// See issue 49583: the type checker has trouble keeping track of aliases,
// but for such a common alias as any we can improve things by preserving a
// pointer identity that can be checked when formatting type strings.
if typ == universeAny {
return types.AnyType
}
// Cache type2-to-type mappings. Important so that each defined generic
// type (instantiated or not) has a single types.Type representation.
// Also saves a lot of computation and memory by avoiding re-translating
@ -105,6 +113,15 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
// based on the names of the type arguments.
instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
// Make sure the base generic type exists in type1 (it may
// not yet if we are referecing an imported generic type, as
// opposed to a generic type declared in this package). Make
// sure to do this lookup before checking s.Def, in case
// s.Def gets defined while importing base (if an imported
// type). (Issue #50486).
base := g.obj(typ.Origin().Obj())
if s.Def != nil {
// We have already encountered this instantiation.
// Use the type we previously created, since there
@ -112,10 +129,13 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
return s.Def.Type()
}
// Make sure the base generic type exists in type1 (it may
// not yet if we are referecing an imported generic type, as
// opposed to a generic type declared in this package).
_ = g.obj(typ.Origin().Obj())
if base.Class == ir.PAUTO {
// If the base type is a local type, we want to pop
// this instantiated type symbol/definition when we
// leave the containing block, so we don't use it
// incorrectly later.
types.Pushdcl(s)
}
// Create a forwarding type first and put it in the g.typs
// map, in order to deal with recursive generic types
@ -219,9 +239,13 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
// Save the name of the type parameter in the sym of the type.
// Include the types2 subscript in the sym name
pkg := g.tpkg(typ)
// Create the unique types1 name for a type param, using its context with a
// function, type, or method declaration.
nm := g.curDecl + "." + typ.Obj().Name()
// Create the unique types1 name for a type param, using its context
// with a function, type, or method declaration. Also, map blank type
// param names to a unique name based on their type param index. The
// unique blank names will be exported, but will be reverted during
// types2 and gcimporter import.
assert(g.curDecl != "")
nm := typecheck.TparamExportName(g.curDecl, typ.Obj().Name(), typ.Index())
sym := pkg.Lookup(nm)
if sym.Def != nil {
// Make sure we use the same type param type for the same
@ -323,11 +347,15 @@ func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
tparams := make([]*types.Type, rparams.Len())
// Set g.curDecl to be the method context, so type
// params in the receiver of the method that we are
// translating gets the right unique name.
// translating gets the right unique name. We could
// be in a top-level typeDecl, so save and restore
// the current contents of g.curDecl.
savedCurDecl := g.curDecl
g.curDecl = typ.Obj().Name() + "." + m.Name()
for i := range tparams {
tparams[i] = g.typ1(rparams.At(i))
}
g.curDecl = savedCurDecl
assert(len(tparams) == len(targs))
ts := typecheck.Tsubster{
Tparams: tparams,

View file

@ -229,6 +229,8 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) int {
// @@@ Types
var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)
func (w *writer) typ(typ types2.Type) {
w.typInfo(w.p.typIdx(typ, w.dict))
}
@ -350,6 +352,12 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
w.structType(typ)
case *types2.Interface:
if typ == anyTypeName.Type() {
w.code(typeNamed)
w.obj(anyTypeName, nil)
break
}
w.code(typeInterface)
w.interfaceType(typ)
@ -1210,6 +1218,7 @@ func (w *writer) expr(expr syntax.Expr) {
}
obj := obj.(*types2.Var)
assert(!obj.IsField())
assert(targs.Len() == 0)
w.code(exprLocal)
@ -1329,10 +1338,10 @@ func (w *writer) compLit(lit *syntax.CompositeLit) {
w.typ(tv.Type)
typ := tv.Type
if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
if ptr, ok := types2.StructuralType(typ).(*types2.Pointer); ok {
typ = ptr.Elem()
}
str, isStruct := typ.Underlying().(*types2.Struct)
str, isStruct := types2.StructuralType(typ).(*types2.Struct)
w.len(len(lit.ElemList))
for i, elem := range lit.ElemList {

View file

@ -16,7 +16,7 @@ func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &ppc64.Linkppc64le
}
arch.REGSP = ppc64.REGSP
arch.MAXWIDTH = 1 << 60
arch.MAXWIDTH = 1 << 50
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop

View file

@ -846,14 +846,19 @@ func TypePtr(t *types.Type) *ir.AddrExpr {
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
}
// ITabLsym returns the LSym representing the itab for concreate type typ
// implementing interface iface.
// ITabLsym returns the LSym representing the itab for concrete type typ implementing
// interface iface. A dummy tab will be created in the unusual case where typ doesn't
// implement iface. Normally, this wouldn't happen, because the typechecker would
// have reported a compile-time error. This situation can only happen when the
// destination type of a type assert or a type in a type switch is parameterized, so
// it may sometimes, but not always, be a type that can't implement the specified
// interface.
func ITabLsym(typ, iface *types.Type) *obj.LSym {
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
lsym := s.Linksym()
if !existed {
writeITab(lsym, typ, iface)
writeITab(lsym, typ, iface, true)
}
return lsym
}
@ -865,7 +870,7 @@ func ITabAddr(typ, iface *types.Type) *ir.AddrExpr {
lsym := s.Linksym()
if !existed {
writeITab(lsym, typ, iface)
writeITab(lsym, typ, iface, false)
}
n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
@ -924,11 +929,12 @@ func hashMightPanic(t *types.Type) bool {
}
}
// formalType replaces byte and rune aliases with real types.
// formalType replaces predeclared aliases with real types.
// They've been separate internally to make error messages
// better, but we have to merge them in the reflect tables.
func formalType(t *types.Type) *types.Type {
if t == types.ByteType || t == types.RuneType {
switch t {
case types.AnyType, types.ByteType, types.RuneType:
return types.Types[t.Kind()]
}
return t
@ -1303,9 +1309,10 @@ func WriteRuntimeTypes() {
}
}
// writeITab writes the itab for concrete type typ implementing
// interface iface.
func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
// writeITab writes the itab for concrete type typ implementing interface iface. If
// allowNonImplement is true, allow the case where typ does not implement iface, and just
// create a dummy itab with zeroed-out method entries.
func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) {
// TODO(mdempsky): Fix methodWrapper, geneq, and genhash (and maybe
// others) to stop clobbering these.
oldpos, oldfn := base.Pos, ir.CurFunc
@ -1331,14 +1338,9 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
break
}
}
if sigs[0].Sym.Name == "==" {
sigs = sigs[1:]
if len(sigs) == 0 {
break
}
}
}
if len(sigs) != 0 {
completeItab := len(sigs) == 0
if !allowNonImplement && !completeItab {
base.Fatalf("incomplete itab")
}
@ -1355,7 +1357,12 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
o += 4 // skip unused field
for _, fn := range entries {
o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
if !completeItab {
// If typ doesn't implement iface, make method entries be zero.
o = objw.Uintptr(lsym, o, 0)
} else {
o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
}
}
// Nothing writes static itabs, so they are read only.
objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
@ -1417,6 +1424,9 @@ func WriteBasicTypes() {
}
writeType(types.NewPtr(types.Types[types.TSTRING]))
writeType(types.NewPtr(types.Types[types.TUNSAFEPTR]))
if base.Flag.G > 0 {
writeType(types.AnyType)
}
// emit type structs for error and func(error) string.
// The latter is the type of an auto-generated wrapper.
@ -1938,18 +1948,25 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
// Target method uses shaped names.
targs2 := make([]*types.Type, len(targs))
origRParams := deref(orig).OrigSym().Def.(*ir.Name).Type().RParams()
for i, t := range targs {
targs2[i] = typecheck.Shapify(t, i)
targs2[i] = typecheck.Shapify(t, i, origRParams[i])
}
targs = targs2
sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, false, true)
if sym.Def == nil {
// Currently we make sure that we have all the instantiations
// we need by generating them all in ../noder/stencil.go:instantiateMethods
// TODO: maybe there's a better, more incremental way to generate
// only the instantiations we need?
base.Fatalf("instantiation %s not found", sym.Name)
// Currently we make sure that we have all the
// instantiations we need by generating them all in
// ../noder/stencil.go:instantiateMethods
// Extra instantiations because of an inlined function
// should have been exported, and so available via
// Resolve.
in := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
if in.Op() == ir.ONONAME {
base.Fatalf("instantiation %s not found", sym.Name)
}
sym = in.Sym()
}
target := ir.AsNode(sym.Def)
call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
@ -2075,8 +2092,14 @@ func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node {
sym := typecheck.MakeDictSym(gf, targs, true)
// Dictionary should already have been generated by instantiateMethods().
// Extra dictionaries needed because of an inlined function should have been
// exported, and so available via Resolve.
if lsym := sym.Linksym(); len(lsym.P) == 0 {
base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name)
in := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
if in.Op() == ir.ONONAME {
base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name)
}
sym = in.Sym()
}
// Make (or reuse) a node referencing the dictionary symbol.

View file

@ -34,6 +34,9 @@ type FuncDebug struct {
VarSlots [][]SlotID
// The location list data, indexed by VarID. Must be processed by PutLocationList.
LocationLists [][]byte
// Register-resident output parameters for the function. This is filled in at
// SSA generation time.
RegOutputParams []*ir.Name
// Filled in by the user. Translates Block and Value ID to PC.
GetPC func(ID, ID) int64
@ -548,10 +551,10 @@ func PopulateABIInRegArgOps(f *Func) {
f.Entry.Values = append(newValues, f.Entry.Values...)
}
// BuildFuncDebug returns debug information for f.
// BuildFuncDebug debug information for f, placing the results in "rval".
// f must be fully processed, so that each Value is where it will be when
// machine code is emitted.
func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
if f.RegAlloc == nil {
f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
}
@ -661,12 +664,11 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
blockLocs := state.liveness()
state.buildLocationLists(blockLocs)
return &FuncDebug{
Slots: state.slots,
VarSlots: state.varSlots,
Vars: state.vars,
LocationLists: state.lists,
}
// Populate "rval" with what we've computed.
rval.Slots = state.slots
rval.VarSlots = state.varSlots
rval.Vars = state.vars
rval.LocationLists = state.lists
}
// liveness walks the function in control flow order, calculating the start
@ -1120,54 +1122,93 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
v.Op == OpArgIntReg || v.Op == OpArgFloatReg
}
blockPrologComplete := func(v *Value) bool {
if b.ID != state.f.Entry.ID {
return !opcodeTable[v.Op].zeroWidth
} else {
return v.Op == OpInitMem
}
}
// Examine the prolog portion of the block to process special
// zero-width ops such as Arg, Phi, LoweredGetClosurePtr (etc)
// whose lifetimes begin at the block starting point. In an
// entry block, allow for the possibility that we may see Arg
// ops that appear _after_ other non-zero-width operations.
// Example:
//
// v33 = ArgIntReg <uintptr> {foo+0} [0] : AX (foo)
// v34 = ArgIntReg <uintptr> {bar+0} [0] : BX (bar)
// ...
// v77 = StoreReg <unsafe.Pointer> v67 : ctx+8[unsafe.Pointer]
// v78 = StoreReg <unsafe.Pointer> v68 : ctx[unsafe.Pointer]
// v79 = Arg <*uint8> {args} : args[*uint8] (args[*uint8])
// v80 = Arg <int> {args} [8] : args+8[int] (args+8[int])
// ...
// v1 = InitMem <mem>
//
// We can stop scanning the initial portion of the block when
// we either see the InitMem op (for entry blocks) or the
// first non-zero-width op (for other blocks).
for idx := 0; idx < len(b.Values); idx++ {
v := b.Values[idx]
if blockPrologComplete(v) {
break
}
// Consider only "lifetime begins at block start" ops.
if !mustBeFirst(v) && v.Op != OpArg {
continue
}
slots := state.valueNames[v.ID]
reg, _ := state.f.getHome(v.ID).(*Register)
changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
if changed {
for _, varID := range state.changedVars.contents() {
state.updateVar(VarID(varID), v.Block, BlockStart)
}
state.changedVars.clear()
}
}
// Now examine the block again, handling things other than the
// "begins at block start" lifetimes.
zeroWidthPending := false
blockPrologComplete := false // set to true at first non-zero-width op
apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
prologComplete := false
// expect to see values in pattern (apc)* (zerowidth|real)*
for _, v := range b.Values {
if blockPrologComplete(v) {
prologComplete = true
}
slots := state.valueNames[v.ID]
reg, _ := state.f.getHome(v.ID).(*Register)
changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
if opcodeTable[v.Op].zeroWidth {
if prologComplete && mustBeFirst(v) {
panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
}
if changed {
if mustBeFirst(v) || v.Op == OpArg {
// These ranges begin at true beginning of block, not after first instruction
if blockPrologComplete && mustBeFirst(v) {
panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
}
apcChangedSize = len(state.changedVars.contents())
// Other zero-width ops must wait on a "real" op.
zeroWidthPending = true
// already taken care of above
continue
}
zeroWidthPending = true
}
continue
}
if !changed && !zeroWidthPending {
continue
}
// Not zero-width; i.e., a "real" instruction.
// Not zero-width; i.e., a "real" instruction.
zeroWidthPending = false
blockPrologComplete = true
for i, varID := range state.changedVars.contents() {
if i < apcChangedSize { // buffered true start-of-block changes
state.updateVar(VarID(varID), v.Block, BlockStart)
} else {
state.updateVar(VarID(varID), v.Block, v)
}
for _, varID := range state.changedVars.contents() {
state.updateVar(VarID(varID), v.Block, v)
}
state.changedVars.clear()
apcChangedSize = 0
}
for i, varID := range state.changedVars.contents() {
if i < apcChangedSize { // buffered true start-of-block changes
state.updateVar(VarID(varID), b, BlockStart)
} else {
state.updateVar(VarID(varID), b, BlockEnd)
}
for _, varID := range state.changedVars.contents() {
state.updateVar(VarID(varID), b, BlockEnd)
}
prevBlock = b
@ -1554,7 +1595,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
return true
}
// BuildFuncDebugNoOptimized constructs a FuncDebug object with
// BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with
// entries corresponding to the register-resident input parameters for
// the function "f"; it is used when we are compiling without
// optimization but the register ABI is enabled. For each reg param,
@ -1562,8 +1603,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
// the input register, and the second element holds the stack location
// of the param (the assumption being that when optimization is off,
// each input param reg will be spilled in the prolog.
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
fd := FuncDebug{}
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())
@ -1577,7 +1617,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
}
if numRegParams == 0 {
return &fd
return
}
state := debugState{f: f}
@ -1587,7 +1627,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
// Allocate location lists.
fd.LocationLists = make([][]byte, numRegParams)
rval.LocationLists = make([][]byte, numRegParams)
// Locate the value corresponding to the last spill of
// an input register.
@ -1603,10 +1643,10 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
n := inp.Name.(*ir.Name)
sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
fd.Vars = append(fd.Vars, n)
fd.Slots = append(fd.Slots, sl)
slid := len(fd.VarSlots)
fd.VarSlots = append(fd.VarSlots, []SlotID{SlotID(slid)})
rval.Vars = append(rval.Vars, n)
rval.Slots = append(rval.Slots, sl)
slid := len(rval.VarSlots)
rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
if afterPrologVal == ID(-1) {
// This can happen for degenerate functions with infinite
@ -1623,7 +1663,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
// Param is arriving in one or more registers. We need a 2-element
// location expression for it. First entry in location list
// will correspond to lifetime in input registers.
list, sizeIdx := setupLocList(ctxt, f, fd.LocationLists[pidx],
list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
BlockStart.ID, afterPrologVal)
if list == nil {
pidx++
@ -1688,8 +1728,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
// fill in size
ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
fd.LocationLists[pidx] = list
rval.LocationLists[pidx] = list
pidx++
}
return &fd
}

View file

@ -8,10 +8,10 @@ import (
"bufio"
"bytes"
"flag"
"internal/buildcfg"
"runtime"
"sort"
// "flag"
"fmt"
"internal/testenv"
"io/ioutil"
@ -45,7 +45,7 @@ func testGoArch() string {
return *testGoArchFlag
}
func TestDebugLines(t *testing.T) {
func TestDebugLinesSayHi(t *testing.T) {
// This test is potentially fragile, the goal is that debugging should step properly through "sayhi"
// If the blocks are reordered in a way that changes the statement order but execution flows correctly,
// then rearrange the expected numbers. Register abi and not-register-abi also have different sequences,
@ -53,16 +53,54 @@ func TestDebugLines(t *testing.T) {
switch testGoArch() {
case "arm64", "amd64": // register ABI
testDebugLines(t, "sayhi.go", "sayhi", []int{8, 9, 10, 11})
testDebugLines(t, "-N -l", "sayhi.go", "sayhi", []int{8, 9, 10, 11}, false)
case "arm", "386": // probably not register ABI for a while
testDebugLines(t, "sayhi.go", "sayhi", []int{9, 10, 11})
testDebugLines(t, "-N -l", "sayhi.go", "sayhi", []int{9, 10, 11}, false)
default: // expect ppc64le and riscv will pick up register ABI soonish, not sure about others
t.Skip("skipped for many architectures, also changes w/ register ABI")
}
}
func TestDebugLinesPushback(t *testing.T) {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { // in particular, it could be windows.
t.Skip("this test depends on creating a file with a wonky name, only works for sure on Linux and Darwin")
}
switch testGoArch() {
default:
t.Skip("skipped for many architectures")
case "arm64", "amd64": // register ABI
fn := "(*List[go.shape.int_0]).PushBack"
if buildcfg.Experiment.Unified {
// Unified mangles differently
fn = "(*List[int]).PushBack"
}
testDebugLines(t, "-N -l -G=3", "pushback.go", fn, []int{17, 18, 19, 20, 21, 22, 24}, true)
}
}
func TestDebugLinesConvert(t *testing.T) {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { // in particular, it could be windows.
t.Skip("this test depends on creating a file with a wonky name, only works for sure on Linux and Darwin")
}
switch testGoArch() {
default:
t.Skip("skipped for many architectures")
case "arm64", "amd64": // register ABI
fn := "G[go.shape.int_0]"
if buildcfg.Experiment.Unified {
// Unified mangles differently
fn = "G[int]"
}
testDebugLines(t, "-N -l -G=3", "convertline.go", fn, []int{9, 10, 11}, true)
}
}
func TestInlineLines(t *testing.T) {
if runtime.GOARCH != "amd64" && *testGoArchFlag == "" {
// As of september 2021, works for everything except mips64, but still potentially fragile
@ -181,8 +219,8 @@ func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) {
// then verifies that the statement-marked lines in that file are the same as those in wantStmts
// These files must all be short because this is super-fragile.
// "go build" is run in a temporary directory that is normally deleted, unless -test.v
func testDebugLines(t *testing.T, file, function string, wantStmts []int) {
dumpBytes := compileAndDump(t, file, function, "-N -l")
func testDebugLines(t *testing.T, gcflags, file, function string, wantStmts []int, ignoreRepeats bool) {
dumpBytes := compileAndDump(t, file, function, gcflags)
dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
var gotStmts []int
dumpLineNum := 0
@ -201,8 +239,20 @@ func testDebugLines(t *testing.T, file, function string, wantStmts []int) {
gotStmts = append(gotStmts, int(stmt))
}
}
if !reflect.DeepEqual(wantStmts, gotStmts) {
t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts)
}
if ignoreRepeats { // remove repeats from gotStmts
newGotStmts := []int{gotStmts[0]}
for _, x := range gotStmts {
if x != newGotStmts[len(newGotStmts)-1] {
newGotStmts = append(newGotStmts, x)
}
}
if !reflect.DeepEqual(wantStmts, newGotStmts) {
t.Errorf("wanted stmts %v but got %v (with repeats still in: %v)", wantStmts, newGotStmts, gotStmts)
}
} else {
if !reflect.DeepEqual(wantStmts, gotStmts) {
t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts)
}
}
}

View file

@ -1270,8 +1270,8 @@
(SRLconst (SLLconst x [c]) [d]) && buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x)
// comparison simplification
((LT|LE|EQ|NE|GE|GT) (CMP x (RSBconst [0] y))) => ((LT|LE|EQ|NE|GE|GT) (CMN x y)) // sense of carry bit not preserved
((LT|LE|EQ|NE|GE|GT) (CMN x (RSBconst [0] y))) => ((LT|LE|EQ|NE|GE|GT) (CMP x y)) // sense of carry bit not preserved
((EQ|NE) (CMP x (RSBconst [0] y))) => ((EQ|NE) (CMN x y)) // sense of carry bit not preserved; see also #50854
((EQ|NE) (CMN x (RSBconst [0] y))) => ((EQ|NE) (CMP x y)) // sense of carry bit not preserved; see also #50864
(EQ (CMPconst [0] l:(SUB x y)) yes no) && l.Uses==1 => (EQ (CMP x y) yes no)
(EQ (CMPconst [0] l:(MULS x y a)) yes no) && l.Uses==1 => (EQ (CMP a (MUL <x.Type> x y)) yes no)
(EQ (CMPconst [0] l:(SUBconst [c] x)) yes no) && l.Uses==1 => (EQ (CMPconst [c] x) yes no)

View file

@ -649,19 +649,13 @@
(GT (CMPWconst [0] z:(ADD x y)) yes no) && z.Uses == 1 => (GTnoov (CMNW x y) yes no)
(GE (CMPWconst [0] z:(ADD x y)) yes no) && z.Uses == 1 => (GEnoov (CMNW x y) yes no)
// CMP(x,-y) -> CMN(x,y) is only valid for unordered comparison, if y can be -1<<63
(EQ (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (EQ (CMN x y) yes no)
(NE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (NE (CMN x y) yes no)
(LT (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (LT (CMN x y) yes no)
(LE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (LE (CMN x y) yes no)
(GT (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (GT (CMN x y) yes no)
(GE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (GE (CMN x y) yes no)
// CMPW(x,-y) -> CMNW(x,y) is only valid for unordered comparison, if y can be -1<<31
(EQ (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (EQ (CMNW x y) yes no)
(NE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (NE (CMNW x y) yes no)
(LT (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (LT (CMNW x y) yes no)
(LE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (LE (CMNW x y) yes no)
(GT (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (GT (CMNW x y) yes no)
(GE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (GE (CMNW x y) yes no)
(EQ (CMPconst [0] x) yes no) => (Z x yes no)
(NE (CMPconst [0] x) yes no) => (NZ x yes no)

View file

@ -285,9 +285,9 @@ func init() {
{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to auxInt
{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1, 32 bit
{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit
{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1
{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1, provided arg1 is not 1<<63
{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"}, // arg0 compare to -auxInt
{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags", commutative: true}, // arg0 compare to -arg1, 32 bit
{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags", commutative: true}, // arg0 compare to -arg1, 32 bit, provided arg1 is not 1<<31
{name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit
{name: "TST", argLength: 2, reg: gp2flags, asm: "TST", typ: "Flags", commutative: true}, // arg0 & arg1 compare to 0
{name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int64", typ: "Flags"}, // arg0 & auxInt compare to 0

View file

@ -331,7 +331,7 @@ func init() {
// comparisons
{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1
{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt
{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1
{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1, provided arg1 is not 1<<63
{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt
{name: "TST", argLength: 2, reg: gp2flags, asm: "TST", typ: "Flags", commutative: true}, // arg0 & arg1 compare to 0
{name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int32", typ: "Flags"}, // arg0 & auxInt compare to 0

View file

@ -509,8 +509,9 @@ func init() {
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
// It saves all GP registers if necessary,
// but clobbers R14 (LR) because it's a call.
{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
// but clobbers R14 (LR) because it's a call,
// and also clobbers R1 as the PLT stub does.
{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14") | r1}, clobberFlags: true, aux: "Sym", symEffect: "None"},
// There are three of these functions so that they can have three different register inputs.
// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the

View file

@ -32779,7 +32779,7 @@ var opcodeTable = [...]opInfo{
{0, 4}, // R2
{1, 8}, // R3
},
clobbers: 4294918144, // R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
clobbers: 4294918146, // R1 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
},
},
{

View file

@ -17153,42 +17153,6 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMLE, cmp)
return true
}
// match: (GE (CMP x (RSBconst [0] y)))
// result: (GE (CMN x y))
for b.Controls[0].Op == OpARMCMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
break
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMGE, v0)
return true
}
// match: (GE (CMN x (RSBconst [0] y)))
// result: (GE (CMP x y))
for b.Controls[0].Op == OpARMCMN {
v_0 := b.Controls[0]
_ = v_0.Args[1]
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
x := v_0_0
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
continue
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMGE, v0)
return true
}
break
}
// match: (GE (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (GEnoov (CMP x y) yes no)
@ -18069,42 +18033,6 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMLT, cmp)
return true
}
// match: (GT (CMP x (RSBconst [0] y)))
// result: (GT (CMN x y))
for b.Controls[0].Op == OpARMCMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
break
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMGT, v0)
return true
}
// match: (GT (CMN x (RSBconst [0] y)))
// result: (GT (CMP x y))
for b.Controls[0].Op == OpARMCMN {
v_0 := b.Controls[0]
_ = v_0.Args[1]
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
x := v_0_0
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
continue
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMGT, v0)
return true
}
break
}
// match: (GT (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (GTnoov (CMP x y) yes no)
@ -19076,42 +19004,6 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMGE, cmp)
return true
}
// match: (LE (CMP x (RSBconst [0] y)))
// result: (LE (CMN x y))
for b.Controls[0].Op == OpARMCMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
break
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMLE, v0)
return true
}
// match: (LE (CMN x (RSBconst [0] y)))
// result: (LE (CMP x y))
for b.Controls[0].Op == OpARMCMN {
v_0 := b.Controls[0]
_ = v_0.Args[1]
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
x := v_0_0
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
continue
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMLE, v0)
return true
}
break
}
// match: (LE (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (LEnoov (CMP x y) yes no)
@ -19992,42 +19884,6 @@ func rewriteBlockARM(b *Block) bool {
b.resetWithControl(BlockARMGT, cmp)
return true
}
// match: (LT (CMP x (RSBconst [0] y)))
// result: (LT (CMN x y))
for b.Controls[0].Op == OpARMCMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
break
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMLT, v0)
return true
}
// match: (LT (CMN x (RSBconst [0] y)))
// result: (LT (CMP x y))
for b.Controls[0].Op == OpARMCMN {
v_0 := b.Controls[0]
_ = v_0.Args[1]
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
x := v_0_0
if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 {
continue
}
y := v_0_1.Args[0]
v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARMLT, v0)
return true
}
break
}
// match: (LT (CMPconst [0] l:(SUB x y)) yes no)
// cond: l.Uses==1
// result: (LTnoov (CMP x y) yes no)

View file

@ -27983,46 +27983,6 @@ func rewriteBlockARM64(b *Block) bool {
}
break
}
// match: (GE (CMP x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (GE (CMN x y) yes no)
for b.Controls[0].Op == OpARM64CMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64GE, v0)
return true
}
// match: (GE (CMPW x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (GE (CMNW x y) yes no)
for b.Controls[0].Op == OpARM64CMPW {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64GE, v0)
return true
}
// match: (GE (CMPconst [0] z:(MADD a x y)) yes no)
// cond: z.Uses==1
// result: (GEnoov (CMN a (MUL <x.Type> x y)) yes no)
@ -28419,46 +28379,6 @@ func rewriteBlockARM64(b *Block) bool {
}
break
}
// match: (GT (CMP x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (GT (CMN x y) yes no)
for b.Controls[0].Op == OpARM64CMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64GT, v0)
return true
}
// match: (GT (CMPW x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (GT (CMNW x y) yes no)
for b.Controls[0].Op == OpARM64CMPW {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64GT, v0)
return true
}
// match: (GT (CMPconst [0] z:(MADD a x y)) yes no)
// cond: z.Uses==1
// result: (GTnoov (CMN a (MUL <x.Type> x y)) yes no)
@ -28951,46 +28871,6 @@ func rewriteBlockARM64(b *Block) bool {
}
break
}
// match: (LE (CMP x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (LE (CMN x y) yes no)
for b.Controls[0].Op == OpARM64CMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64LE, v0)
return true
}
// match: (LE (CMPW x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (LE (CMNW x y) yes no)
for b.Controls[0].Op == OpARM64CMPW {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64LE, v0)
return true
}
// match: (LE (CMPconst [0] z:(MADD a x y)) yes no)
// cond: z.Uses==1
// result: (LEnoov (CMN a (MUL <x.Type> x y)) yes no)
@ -29363,46 +29243,6 @@ func rewriteBlockARM64(b *Block) bool {
}
break
}
// match: (LT (CMP x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (LT (CMN x y) yes no)
for b.Controls[0].Op == OpARM64CMP {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64LT, v0)
return true
}
// match: (LT (CMPW x z:(NEG y)) yes no)
// cond: z.Uses == 1
// result: (LT (CMNW x y) yes no)
for b.Controls[0].Op == OpARM64CMPW {
v_0 := b.Controls[0]
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
b.resetWithControl(BlockARM64LT, v0)
return true
}
// match: (LT (CMPconst [0] z:(MADD a x y)) yes no)
// cond: z.Uses==1
// result: (LTnoov (CMN a (MUL <x.Type> x y)) yes no)

View file

@ -0,0 +1,16 @@
package main
import "fmt"
func F[T any](n T) {
fmt.Printf("called\n")
}
func G[T any](n T) {
F(n)
fmt.Printf("after\n")
}
func main() {
G(3)
}

View file

@ -0,0 +1,30 @@
package main
type Node struct {
Circular bool
}
type ExtNode[V any] struct {
v V
Node
}
type List[V any] struct {
root *ExtNode[V]
len int
}
func (list *List[V]) PushBack(arg V) {
if list.len == 0 {
list.root = &ExtNode[V]{v: arg}
list.root.Circular = true
list.len++
return
}
list.len++
}
func main() {
var v List[int]
v.PushBack(1)
}

View file

@ -484,6 +484,19 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
var params *abi.ABIParamResultInfo
params = s.f.ABISelf.ABIAnalyze(fn.Type(), true)
// The backend's stackframe pass prunes away entries from the fn's
// Dcl list, including PARAMOUT nodes that correspond to output
// params passed in registers. Walk the Dcl list and capture these
// nodes to a side list, so that we'll have them available during
// DWARF-gen later on. See issue 48573 for more details.
var debugInfo ssa.FuncDebug
for _, n := range fn.Dcl {
if n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters() {
debugInfo.RegOutputParams = append(debugInfo.RegOutputParams, n)
}
}
fn.DebugInfo = &debugInfo
// Generate addresses of local declarations
s.decladdrs = map[*ir.Name]*ssa.Value{}
for _, n := range fn.Dcl {
@ -2433,6 +2446,38 @@ func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.newValue1(op, tt, v)
}
if ft.IsComplex() && tt.IsComplex() {
var op ssa.Op
if ft.Size() == tt.Size() {
switch ft.Size() {
case 8:
op = ssa.OpRound32F
case 16:
op = ssa.OpRound64F
default:
s.Fatalf("weird complex conversion %v -> %v", ft, tt)
}
} else if ft.Size() == 8 && tt.Size() == 16 {
op = ssa.OpCvt32Fto64F
} else if ft.Size() == 16 && tt.Size() == 8 {
op = ssa.OpCvt64Fto32F
} else {
s.Fatalf("weird complex conversion %v -> %v", ft, tt)
}
ftp := types.FloatForComplex(ft)
ttp := types.FloatForComplex(tt)
return s.newValue2(ssa.OpComplexMake, tt,
s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, v)),
s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, v)))
}
if tt.IsComplex() { // and ft is not complex
// Needed for generics support - can't happen in normal Go code.
et := types.FloatForComplex(tt)
v = s.conv(n, v, ft, et)
return s.newValue2(ssa.OpComplexMake, tt, v, s.zeroVal(et))
}
if ft.IsFloat() || tt.IsFloat() {
conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat {
@ -2506,31 +2551,6 @@ func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value {
return nil
}
if ft.IsComplex() && tt.IsComplex() {
var op ssa.Op
if ft.Size() == tt.Size() {
switch ft.Size() {
case 8:
op = ssa.OpRound32F
case 16:
op = ssa.OpRound64F
default:
s.Fatalf("weird complex conversion %v -> %v", ft, tt)
}
} else if ft.Size() == 8 && tt.Size() == 16 {
op = ssa.OpCvt32Fto64F
} else if ft.Size() == 16 && tt.Size() == 8 {
op = ssa.OpCvt64Fto32F
} else {
s.Fatalf("weird complex conversion %v -> %v", ft, tt)
}
ftp := types.FloatForComplex(ft)
ttp := types.FloatForComplex(tt)
return s.newValue2(ssa.OpComplexMake, tt,
s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, v)),
s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, v)))
}
s.Fatalf("unhandled OCONV %s -> %s", ft.Kind(), tt.Kind())
return nil
}
@ -5075,6 +5095,18 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
for _, p := range params.InParams() { // includes receiver for interface calls
ACArgs = append(ACArgs, p.Type)
}
// Split the entry block if there are open defers, because later calls to
// openDeferSave may cause a mismatch between the mem for an OpDereference
// and the call site which uses it. See #49282.
if s.curBlock.ID == s.f.Entry.ID && s.hasOpenDefers {
b := s.endBlock()
b.Kind = ssa.BlockPlain
curb := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(curb)
s.startBlock(curb)
}
for i, n := range args {
callArgs = append(callArgs, s.putArg(n, t.Params().Field(i).Type))
}
@ -6552,6 +6584,22 @@ func (s *State) DebugFriendlySetPosFrom(v *ssa.Value) {
// explicit statement boundaries should appear
// in the generated code.
if p.IsStmt() != src.PosIsStmt {
if s.pp.Pos.IsStmt() == src.PosIsStmt && s.pp.Pos.SameFileAndLine(p) {
// If s.pp.Pos already has a statement mark, then it was set here (below) for
// the previous value. If an actual instruction had been emitted for that
// value, then the statement mark would have been reset. Since the statement
// mark of s.pp.Pos was not reset, this position (file/line) still needs a
// statement mark on an instruction. If file and line for this value are
// the same as the previous value, then the first instruction for this
// value will work to take the statement mark. Return early to avoid
// resetting the statement mark.
//
// The reset of s.pp.Pos occurs in (*Progs).Prog() -- if it emits
// an instruction, and the instruction's statement mark was set,
// and it is not one of the LosesStmtMark instructions,
// then Prog() resets the statement mark on the (*Progs).Pos.
return
}
p = p.WithNotStmt()
// Calls use the pos attached to v, but copy the statement mark from State
}
@ -6793,6 +6841,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
for i, b := range f.Blocks {
s.bstart[b.ID] = s.pp.Next
s.lineRunStart = nil
s.SetPos(s.pp.Pos.WithNotStmt()) // It needs a non-empty Pos, but cannot be a statement boundary (yet).
// Attach a "default" liveness info. Normally this will be
// overwritten in the Values loop below for each Value. But
@ -6991,12 +7040,12 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
if base.Ctxt.Flag_locationlists {
var debugInfo *ssa.FuncDebug
debugInfo = e.curfn.DebugInfo.(*ssa.FuncDebug)
if e.curfn.ABI == obj.ABIInternal && base.Flag.N != 0 {
debugInfo = ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset)
ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo)
} else {
debugInfo = ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset)
ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo)
}
e.curfn.DebugInfo = debugInfo
bstart := s.bstart
idToIdx := make([]int, f.NumBlocks())
for i, b := range f.Blocks {

View file

@ -588,44 +588,81 @@ func (p *parser) typeDecl(group *Group) Decl {
d.Name = p.name()
if p.allowGenerics() && p.tok == _Lbrack {
// d.Name "[" ...
// array/slice or type parameter list
// array/slice type or type parameter list
pos := p.pos()
p.next()
switch p.tok {
case _Name:
// d.Name "[" name ...
// array or type parameter list
name := p.name()
// Index or slice expressions are never constant and thus invalid
// array length expressions. Thus, if we see a "[" following name
// we can safely assume that "[" name starts a type parameter list.
var x Expr // x != nil means x is the array length expression
// We may have an array type or a type parameter list.
// In either case we expect an expression x (which may
// just be a name, or a more complex expression) which
// we can analyze further.
//
// A type parameter list may have a type bound starting
// with a "[" as in: P []E. In that case, simply parsing
// an expression would lead to an error: P[] is invalid.
// But since index or slice expressions are never constant
// and thus invalid array length expressions, if we see a
// "[" following a name it must be the start of an array
// or slice constraint. Only if we don't see a "[" do we
// need to parse a full expression.
var x Expr = p.name()
if p.tok != _Lbrack {
// d.Name "[" name ...
// If we reach here, the next token is not a "[", and we need to
// parse the expression starting with name. If that expression is
// just that name, not followed by a "]" (in which case we might
// have the array length "[" name "]"), we can also safely assume
// a type parameter list.
// To parse the expression starting with name, expand
// the call sequence we would get by passing in name
// to parser.expr, and pass in name to parser.pexpr.
p.xnest++
// To parse the expression starting with name, expand the call
// sequence we would get by passing in name to parser.expr, and
// pass in name to parser.pexpr.
x = p.binaryExpr(p.pexpr(name, false), 0)
x = p.binaryExpr(p.pexpr(x, false), 0)
p.xnest--
if x == name && p.tok != _Rbrack {
x = nil
}
// analyze the cases
var pname *Name // pname != nil means pname is the type parameter name
var ptype Expr // ptype != nil means ptype is the type parameter type; pname != nil in this case
switch t := x.(type) {
case *Name:
// Unless we see a "]", we are at the start of a type parameter list.
if p.tok != _Rbrack {
// d.Name "[" name ...
pname = t
// no ptype
}
case *Operation:
// If we have an expression of the form name*T, and T is a (possibly
// parenthesized) type literal or the next token is a comma, we are
// at the start of a type parameter list.
if name, _ := t.X.(*Name); name != nil {
if t.Op == Mul && (isTypeLit(t.Y) || p.tok == _Comma) {
// d.Name "[" name "*" t.Y
// d.Name "[" name "*" t.Y ","
t.X, t.Y = t.Y, nil // convert t into unary *t.Y
pname = name
ptype = t
}
}
case *CallExpr:
// If we have an expression of the form name(T), and T is a (possibly
// parenthesized) type literal or the next token is a comma, we are
// at the start of a type parameter list.
if name, _ := t.Fun.(*Name); name != nil {
if len(t.ArgList) == 1 && !t.HasDots && (isTypeLit(t.ArgList[0]) || p.tok == _Comma) {
// d.Name "[" name "(" t.ArgList[0] ")"
// d.Name "[" name "(" t.ArgList[0] ")" ","
pname = name
ptype = t.ArgList[0]
}
}
}
if x == nil {
// d.Name "[" name ...
// type parameter list
d.TParamList = p.paramList(name, _Rbrack, true)
if pname != nil {
// d.Name "[" pname ...
// d.Name "[" pname ptype ...
// d.Name "[" pname ptype "," ...
d.TParamList = p.paramList(pname, ptype, _Rbrack, true)
d.Alias = p.gotAssign()
d.Type = p.typeOrNil()
} else {
// d.Name "[" x "]" ...
// x is the array length expression
// d.Name "[" x ...
d.Type = p.arrayType(pos, x)
}
case _Rbrack:
@ -650,6 +687,21 @@ func (p *parser) typeDecl(group *Group) Decl {
return d
}
// isTypeLit reports whether x is a (possibly parenthesized) type literal.
func isTypeLit(x Expr) bool {
switch x := x.(type) {
case *ArrayType, *StructType, *FuncType, *InterfaceType, *SliceType, *MapType, *ChanType:
return true
case *Operation:
// *T may be a pointer dereferenciation.
// Only consider *T as type literal if T is a type literal.
return x.Op == Mul && x.Y == nil && isTypeLit(x.X)
case *ParenExpr:
return isTypeLit(x.X)
}
return false
}
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
func (p *parser) varDecl(group *Group) Decl {
if trace {
@ -689,7 +741,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
f.Pragma = p.takePragma()
if p.got(_Lparen) {
rcvr := p.paramList(nil, _Rparen, false)
rcvr := p.paramList(nil, nil, _Rparen, false)
switch len(rcvr) {
case 0:
p.error("method has no receiver")
@ -708,7 +760,13 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
}
f.Name = p.name()
f.TParamList, f.Type = p.funcType("")
context := ""
if f.Recv != nil && p.mode&AllowMethodTypeParams == 0 {
context = "method" // don't permit (method) type parameters in funcType
}
f.TParamList, f.Type = p.funcType(context)
if p.tok == _Lbrace {
f.Body = p.funcBody()
}
@ -1363,18 +1421,18 @@ func (p *parser) funcType(context string) ([]*Field, *FuncType) {
if p.allowGenerics() && p.got(_Lbrack) {
if context != "" {
// accept but complain
p.syntaxErrorAt(typ.pos, context+" cannot have type parameters")
p.syntaxErrorAt(typ.pos, context+" must have no type parameters")
}
if p.tok == _Rbrack {
p.syntaxError("empty type parameter list")
p.next()
} else {
tparamList = p.paramList(nil, _Rbrack, true)
tparamList = p.paramList(nil, nil, _Rbrack, true)
}
}
p.want(_Lparen)
typ.ParamList = p.paramList(nil, _Rparen, false)
typ.ParamList = p.paramList(nil, nil, _Rparen, false)
typ.ResultList = p.funcResult()
return tparamList, typ
@ -1392,6 +1450,13 @@ func (p *parser) arrayType(pos Pos, len Expr) Expr {
len = p.expr()
p.xnest--
}
if p.tok == _Comma {
// Trailing commas are accepted in type parameter
// lists but not in array type declarations.
// Accept for better error handling but complain.
p.syntaxError("unexpected comma; expecting ]")
p.next()
}
p.want(_Rbrack)
t := new(ArrayType)
t.pos = pos
@ -1516,7 +1581,7 @@ func (p *parser) funcResult() []*Field {
}
if p.got(_Lparen) {
return p.paramList(nil, _Rparen, false)
return p.paramList(nil, nil, _Rparen, false)
}
pos := p.pos()
@ -1742,7 +1807,7 @@ func (p *parser) methodDecl() *Field {
// A type argument list looks like a parameter list with only
// types. Parse a parameter list and decide afterwards.
list := p.paramList(nil, _Rbrack, false)
list := p.paramList(nil, nil, _Rbrack, false)
if len(list) == 0 {
// The type parameter list is not [] but we got nothing
// due to other errors (reported by paramList). Treat
@ -1764,7 +1829,7 @@ func (p *parser) methodDecl() *Field {
// TODO(gri) Record list as type parameter list with f.Type
// if we want to type-check the generic method.
// For now, report an error so this is not a silent event.
p.errorAt(pos, "interface method cannot have type parameters")
p.errorAt(pos, "interface method must have no type parameters")
break
}
@ -1948,17 +2013,41 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
// ParameterList = ParameterDecl { "," ParameterDecl } .
// "(" or "[" has already been consumed.
// If name != nil, it is the first name after "(" or "[".
// If typ != nil, name must be != nil, and (name, typ) is the first field in the list.
// In the result list, either all fields have a name, or no field has a name.
func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*Field) {
func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) (list []*Field) {
if trace {
defer p.trace("paramList")()
}
// p.list won't invoke its function argument if we're at the end of the
// parameter list. If we have a complete field, handle this case here.
if name != nil && typ != nil && p.tok == close {
p.next()
par := new(Field)
par.pos = name.pos
par.Name = name
par.Type = typ
return []*Field{par}
}
var named int // number of parameters that have an explicit name and type
var typed int // number of parameters that have an explicit type
end := p.list(_Comma, close, func() bool {
par := p.paramDeclOrNil(name, close)
var par *Field
if typ != nil {
if debug && name == nil {
panic("initial type provided without name")
}
par = new(Field)
par.pos = name.pos
par.Name = name
par.Type = typ
} else {
par = p.paramDeclOrNil(name, close)
}
name = nil // 1st name was consumed if present
typ = nil // 1st type was consumed if present
if par != nil {
if debug && par.Name == nil && par.Type == nil {
panic("parameter without name or type")

View file

@ -46,7 +46,7 @@ func TestParseGo2(t *testing.T) {
for _, fi := range list {
name := fi.Name()
if !fi.IsDir() && !strings.HasPrefix(name, ".") {
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics)
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowMethodTypeParams)
}
}
}

View file

@ -44,7 +44,7 @@ func Fprint(w io.Writer, x Node, form Form) (n int, err error) {
return
}
// String is a convenience functions that prints n in ShortForm
// String is a convenience function that prints n in ShortForm
// and returns the printed string.
func String(n Node) string {
var buf bytes.Buffer
@ -666,9 +666,7 @@ func (p *printer) printRawNode(n Node) {
}
p.print(n.Name)
if n.TParamList != nil {
p.print(_Lbrack)
p.printFieldList(n.TParamList, nil, _Comma)
p.print(_Rbrack)
p.printParameterList(n.TParamList, true)
}
p.print(blank)
if n.Alias {
@ -700,9 +698,7 @@ func (p *printer) printRawNode(n Node) {
}
p.print(n.Name)
if n.TParamList != nil {
p.print(_Lbrack)
p.printFieldList(n.TParamList, nil, _Comma)
p.print(_Rbrack)
p.printParameterList(n.TParamList, true)
}
p.printSignature(n.Type)
if n.Body != nil {
@ -887,38 +883,47 @@ func (p *printer) printDeclList(list []Decl) {
}
func (p *printer) printSignature(sig *FuncType) {
p.printParameterList(sig.ParamList)
p.printParameterList(sig.ParamList, false)
if list := sig.ResultList; list != nil {
p.print(blank)
if len(list) == 1 && list[0].Name == nil {
p.printNode(list[0].Type)
} else {
p.printParameterList(list)
p.printParameterList(list, false)
}
}
}
func (p *printer) printParameterList(list []*Field) {
p.print(_Lparen)
if len(list) > 0 {
for i, f := range list {
if i > 0 {
p.print(_Comma, blank)
}
if f.Name != nil {
p.printNode(f.Name)
if i+1 < len(list) {
f1 := list[i+1]
if f1.Name != nil && f1.Type == f.Type {
continue // no need to print type
}
func (p *printer) printParameterList(list []*Field, types bool) {
open, close := _Lparen, _Rparen
if types {
open, close = _Lbrack, _Rbrack
}
p.print(open)
for i, f := range list {
if i > 0 {
p.print(_Comma, blank)
}
if f.Name != nil {
p.printNode(f.Name)
if i+1 < len(list) {
f1 := list[i+1]
if f1.Name != nil && f1.Type == f.Type {
continue // no need to print type
}
p.print(blank)
}
p.printNode(f.Type)
p.print(blank)
}
p.printNode(unparen(f.Type)) // no need for (extra) parentheses around parameter types
}
// A type parameter list [P *T] where T is not a type literal requires a comma as in [P *T,]
// so that it's not parsed as [P*T].
if types && len(list) == 1 {
if t, _ := list[0].Type.(*Operation); t != nil && t.Op == Mul && t.Y == nil && !isTypeLit(t.X) {
p.print(_Comma)
}
}
p.print(_Rparen)
p.print(close)
}
func (p *printer) printStmtList(list []Stmt, braces bool) {

View file

@ -53,54 +53,77 @@ func TestPrintError(t *testing.T) {
}
}
var stringTests = []string{
"package p",
"package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )",
var stringTests = [][2]string{
dup("package p"),
dup("package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )"),
// generic type declarations
"package p; type _[T any] struct{}",
"package p; type _[A, B, C interface{m()}] struct{}",
"package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}] struct{}",
dup("package p; type _[T any] struct{}"),
dup("package p; type _[A, B, C interface{m()}] struct{}"),
dup("package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}] struct{}"),
dup("package p; type _[P *T,] struct{}"),
dup("package p; type _[P *T, _ any] struct{}"),
{"package p; type _[P (*T),] struct{}", "package p; type _[P *T,] struct{}"},
{"package p; type _[P (*T), _ any] struct{}", "package p; type _[P *T, _ any] struct{}"},
{"package p; type _[P (T),] struct{}", "package p; type _[P T] struct{}"},
{"package p; type _[P (T), _ any] struct{}", "package p; type _[P T, _ any] struct{}"},
dup("package p; type _[P *struct{}] struct{}"),
{"package p; type _[P (*struct{})] struct{}", "package p; type _[P *struct{}] struct{}"},
{"package p; type _[P ([]int)] struct{}", "package p; type _[P []int] struct{}"},
dup("package p; type _ [P(T)]struct{}"),
dup("package p; type _ [P((T))]struct{}"),
dup("package p; type _ [P * *T]struct{}"),
dup("package p; type _ [P * T]struct{}"),
dup("package p; type _ [P(*T)]struct{}"),
dup("package p; type _ [P(**T)]struct{}"),
dup("package p; type _ [P * T - T]struct{}"),
// array type declarations
dup("package p; type _ [P * T]struct{}"),
dup("package p; type _ [P * T - T]struct{}"),
// generic function declarations
"package p; func _[T any]()",
"package p; func _[A, B, C interface{m()}]()",
"package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}]()",
dup("package p; func _[T any]()"),
dup("package p; func _[A, B, C interface{m()}]()"),
dup("package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}]()"),
// methods with generic receiver types
"package p; func (R[T]) _()",
"package p; func (*R[A, B, C]) _()",
"package p; func (_ *R[A, B, C]) _()",
dup("package p; func (R[T]) _()"),
dup("package p; func (*R[A, B, C]) _()"),
dup("package p; func (_ *R[A, B, C]) _()"),
// type constraint literals with elided interfaces
"package p; func _[P ~int, Q int | string]() {}",
"package p; func _[P struct{f int}, Q *P]() {}",
dup("package p; func _[P ~int, Q int | string]() {}"),
dup("package p; func _[P struct{f int}, Q *P]() {}"),
// channels
"package p; type _ chan chan int",
"package p; type _ chan (<-chan int)",
"package p; type _ chan chan<- int",
dup("package p; type _ chan chan int"),
dup("package p; type _ chan (<-chan int)"),
dup("package p; type _ chan chan<- int"),
"package p; type _ <-chan chan int",
"package p; type _ <-chan <-chan int",
"package p; type _ <-chan chan<- int",
dup("package p; type _ <-chan chan int"),
dup("package p; type _ <-chan <-chan int"),
dup("package p; type _ <-chan chan<- int"),
"package p; type _ chan<- chan int",
"package p; type _ chan<- <-chan int",
"package p; type _ chan<- chan<- int",
dup("package p; type _ chan<- chan int"),
dup("package p; type _ chan<- <-chan int"),
dup("package p; type _ chan<- chan<- int"),
// TODO(gri) expand
}
func TestPrintString(t *testing.T) {
for _, want := range stringTests {
ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics)
for _, test := range stringTests {
ast, err := Parse(nil, strings.NewReader(test[0]), nil, nil, AllowGenerics)
if err != nil {
t.Error(err)
continue
}
if got := String(ast); got != want {
t.Errorf("%q: got %q", want, got)
if got := String(ast); got != test[1] {
t.Errorf("%q: got %q", test[1], got)
}
}
}

View file

@ -17,6 +17,7 @@ type Mode uint
const (
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
AllowGenerics
AllowMethodTypeParams // does not support interface methods yet; ignored if AllowGenerics is not set
)
// Error describes a syntax error. Error implements the error interface.

View file

@ -4,12 +4,12 @@
package p
type _ func /* ERROR function type cannot have type parameters */ [ /* ERROR empty type parameter list */ ]()
type _ func /* ERROR function type cannot have type parameters */ [ x /* ERROR missing type constraint */ ]()
type _ func /* ERROR function type cannot have type parameters */ [P any]()
type _ func /* ERROR function type must have no type parameters */ [ /* ERROR empty type parameter list */ ]()
type _ func /* ERROR function type must have no type parameters */ [ x /* ERROR missing type constraint */ ]()
type _ func /* ERROR function type must have no type parameters */ [P any]()
var _ = func /* ERROR function literal cannot have type parameters */ [P any]() {}
var _ = func /* ERROR function literal must have no type parameters */ [P any]() {}
type _ interface{
m /* ERROR interface method cannot have type parameters */ [P any]()
m /* ERROR interface method must have no type parameters */ [P any]()
}

View file

@ -0,0 +1,31 @@
// Copyright 2021 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
type (
// these need a comma to disambiguate
_[P *T,] struct{}
_[P *T, _ any] struct{}
_[P (*T),] struct{}
_[P (*T), _ any] struct{}
_[P (T),] struct{}
_[P (T), _ any] struct{}
// these parse as name followed by type
_[P *struct{}] struct{}
_[P (*struct{})] struct{}
_[P ([]int)] struct{}
// array declarations
_ [P(T)]struct{}
_ [P((T))]struct{}
_ [P * *T] struct{} // this could be a name followed by a type but it makes the rules more complicated
_ [P * T]struct{}
_ [P(*T)]struct{}
_ [P(**T)]struct{}
_ [P * T - T]struct{}
_ [P*T-T /* ERROR unexpected comma */ ,]struct{}
_ [10 /* ERROR unexpected comma */ ,]struct{}
)

View file

@ -13,7 +13,7 @@ type t struct {
}
type t interface {
t[a]
m /* ERROR method cannot have type parameters */ [_ _, /* ERROR mixed */ _]()
m /* ERROR method must have no type parameters */ [_ _, /* ERROR mixed */ _]()
t[a, b]
}

Some files were not shown because too many files have changed in this diff Show more