mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: I18dbf4f9fa7e2334fccedd862a523126cf38164e
This commit is contained in:
commit
e14fee553a
1603 changed files with 47792 additions and 24800 deletions
|
|
@ -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
47
.github/ISSUE_TEMPLATE/01-pkgsite.md
vendored
Normal 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?
|
||||
|
||||
|
||||
39
.github/ISSUE_TEMPLATE/02-pkgsite-removal.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/02-pkgsite-removal.md
vendored
Normal 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
61
.github/ISSUE_TEMPLATE/03-gopls.md
vendored
Normal 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
13
.github/ISSUE_TEMPLATE/10-proposal.md
vendored
Normal 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
|
||||
-->
|
||||
|
||||
|
||||
52
.github/ISSUE_TEMPLATE/11-language-change.md
vendored
Normal file
52
.github/ISSUE_TEMPLATE/11-language-change.md
vendored
Normal 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
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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
|
||||
3
AUTHORS
3
AUTHORS
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
239
api/go1.18.txt
Normal 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
|
||||
47
api/next.txt
47
api/next.txt
|
|
@ -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
6858
doc/go1.17_spec.html
Normal file
File diff suppressed because it is too large
Load diff
915
doc/go1.18.html
915
doc/go1.18.html
File diff suppressed because it is too large
Load diff
1329
doc/go_spec.html
1329
doc/go_spec.html
File diff suppressed because it is too large
Load diff
|
|
@ -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.
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
9
misc/cgo/test/testdata/issue9400_linux.go
vendored
9
misc/cgo/test/testdata/issue9400_linux.go
vendored
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
misc/cgo/testplugin/testdata/forkexec/main.go
vendored
Normal file
30
misc/cgo/testplugin/testdata/forkexec/main.go
vendored
Normal 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()
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
21
misc/cgo/testsanitizers/testdata/asan5_fail.go
vendored
Normal file
21
misc/cgo/testsanitizers/testdata/asan5_fail.go
vendored
Normal 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))
|
||||
}
|
||||
|
|
@ -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)))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 "$@"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
80
src/archive/tar/fuzz_test.go
Normal file
80
src/archive/tar/fuzz_test.go
Normal 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.
|
||||
})
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
81
src/archive/zip/fuzz_test.go
Normal file
81
src/archive/zip/fuzz_test.go
Normal 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.
|
||||
})
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//go:build !amd64
|
||||
// +build !amd64
|
||||
|
||||
package p
|
||||
|
|
|
|||
2
src/cmd/api/testdata/src/pkg/p1/p1.go
vendored
2
src/cmd/api/testdata/src/pkg/p1/p1.go
vendored
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
4
src/cmd/api/testdata/src/pkg/p4/p4.go
vendored
4
src/cmd/api/testdata/src/pkg/p4/p4.go
vendored
|
|
@ -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}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 Go’s
|
||||
stack-based calling convention except for R14 and X15, which will have
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)}"},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
29
src/cmd/compile/internal/importer/testdata/generics.go
vendored
Normal file
29
src/cmd/compile/internal/importer/testdata/generics.go
vendored
Normal 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
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
16
src/cmd/compile/internal/ssa/testdata/convertline.go
vendored
Normal file
16
src/cmd/compile/internal/ssa/testdata/convertline.go
vendored
Normal 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)
|
||||
}
|
||||
30
src/cmd/compile/internal/ssa/testdata/pushback.go
vendored
Normal file
30
src/cmd/compile/internal/ssa/testdata/pushback.go
vendored
Normal 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)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]()
|
||||
}
|
||||
|
|
|
|||
31
src/cmd/compile/internal/syntax/testdata/issue49482.go2
vendored
Normal file
31
src/cmd/compile/internal/syntax/testdata/issue49482.go2
vendored
Normal 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{}
|
||||
)
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue