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!
|
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`)?
|
### What version of Go are you using (`go version`)?
|
||||||
|
|
@ -26,7 +31,7 @@ $ go env
|
||||||
<!--
|
<!--
|
||||||
If possible, provide a recipe for reproducing the error.
|
If possible, provide a recipe for reproducing the error.
|
||||||
A complete runnable program is good.
|
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?
|
### 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>
|
Benny Siegert <bsiegert@gmail.com>
|
||||||
Benoit Sigoure <tsunanet@gmail.com>
|
Benoit Sigoure <tsunanet@gmail.com>
|
||||||
Berengar Lehr <berengar.lehr@gmx.de>
|
Berengar Lehr <berengar.lehr@gmx.de>
|
||||||
|
Bharath Kumar Uppala <uppala.bharath@gmail.com>
|
||||||
Bill Zissimopoulos <billziss@navimatics.com>
|
Bill Zissimopoulos <billziss@navimatics.com>
|
||||||
Billie Harold Cleek <bhcleek@gmail.com>
|
Billie Harold Cleek <bhcleek@gmail.com>
|
||||||
Bjorn Tillenius <bjorn@tillenius.me>
|
Bjorn Tillenius <bjorn@tillenius.me>
|
||||||
|
|
@ -262,6 +263,7 @@ Casey Callendrello <squeed@gmail.com>
|
||||||
Casey Marshall <casey.marshall@gmail.com>
|
Casey Marshall <casey.marshall@gmail.com>
|
||||||
Cezar Sá Espinola <cezarsa@gmail.com>
|
Cezar Sá Espinola <cezarsa@gmail.com>
|
||||||
ChaiShushan <chaishushan@gmail.com>
|
ChaiShushan <chaishushan@gmail.com>
|
||||||
|
Chaoqun Han <hanssccv@gmail.com>
|
||||||
Charles Fenwick Elliott <Charles@FenwickElliott.io>
|
Charles Fenwick Elliott <Charles@FenwickElliott.io>
|
||||||
Charles L. Dorian <cldorian@gmail.com>
|
Charles L. Dorian <cldorian@gmail.com>
|
||||||
Charles Lee <zombie.fml@gmail.com>
|
Charles Lee <zombie.fml@gmail.com>
|
||||||
|
|
@ -1479,6 +1481,7 @@ Zemanta d.o.o.
|
||||||
Zev Goldstein <zev.goldstein@gmail.com>
|
Zev Goldstein <zev.goldstein@gmail.com>
|
||||||
Zheng Dayu <davidzheng23@gmail.com>
|
Zheng Dayu <davidzheng23@gmail.com>
|
||||||
Zhongtao Chen <chenzhongtao@126.com>
|
Zhongtao Chen <chenzhongtao@126.com>
|
||||||
|
Zhou Guangyuan <zhouguangyuan.xian@gmail.com>
|
||||||
Zhou Peng <p@ctriple.cn>
|
Zhou Peng <p@ctriple.cn>
|
||||||
Ziad Hatahet <hatahet@gmail.com>
|
Ziad Hatahet <hatahet@gmail.com>
|
||||||
Zizhao Zhang <btw515wolf2@gmail.com>
|
Zizhao Zhang <btw515wolf2@gmail.com>
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,7 @@ Benny Siegert <bsiegert@gmail.com>
|
||||||
Benoit Sigoure <tsunanet@gmail.com>
|
Benoit Sigoure <tsunanet@gmail.com>
|
||||||
Berengar Lehr <Berengar.Lehr@gmx.de>
|
Berengar Lehr <Berengar.Lehr@gmx.de>
|
||||||
Berkant Ipek <41230766+0xbkt@users.noreply.github.com>
|
Berkant Ipek <41230766+0xbkt@users.noreply.github.com>
|
||||||
|
Bharath Kumar Uppala <uppala.bharath@gmail.com>
|
||||||
Bharath Thiruveedula <tbharath91@gmail.com>
|
Bharath Thiruveedula <tbharath91@gmail.com>
|
||||||
Bhavin Gandhi <bhavin7392@gmail.com>
|
Bhavin Gandhi <bhavin7392@gmail.com>
|
||||||
Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@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>
|
Changkun Ou <hi@changkun.us>
|
||||||
Channing Kimble-Brown <channing@golang.org>
|
Channing Kimble-Brown <channing@golang.org>
|
||||||
Chao Xu <xuchao@google.com>
|
Chao Xu <xuchao@google.com>
|
||||||
|
Chaoqun Han <hanssccv@gmail.com>
|
||||||
Charles Fenwick Elliott <Charles@FenwickElliott.io>
|
Charles Fenwick Elliott <Charles@FenwickElliott.io>
|
||||||
Charles Kenney <charlesc.kenney@gmail.com>
|
Charles Kenney <charlesc.kenney@gmail.com>
|
||||||
Charles L. Dorian <cldorian@gmail.com>
|
Charles L. Dorian <cldorian@gmail.com>
|
||||||
|
|
@ -2746,6 +2748,7 @@ Zhengyu He <hzy@google.com>
|
||||||
Zhongpeng Lin <zplin@uber.com>
|
Zhongpeng Lin <zplin@uber.com>
|
||||||
Zhongtao Chen <chenzhongtao@126.com>
|
Zhongtao Chen <chenzhongtao@126.com>
|
||||||
Zhongwei Yao <zhongwei.yao@arm.com>
|
Zhongwei Yao <zhongwei.yao@arm.com>
|
||||||
|
Zhou Guangyuan <zhouguangyuan.xian@gmail.com>
|
||||||
Zhou Peng <p@ctriple.cn>
|
Zhou Peng <p@ctriple.cn>
|
||||||
Ziad Hatahet <hatahet@gmail.com>
|
Ziad Hatahet <hatahet@gmail.com>
|
||||||
Ziheng Liu <lzhfromustc@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
1325
doc/go_spec.html
1325
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.
|
# Consult https://www.iana.org/time-zones for the latest versions.
|
||||||
|
|
||||||
# Versions to use.
|
# Versions to use.
|
||||||
CODE=2021a
|
CODE=2021e
|
||||||
DATA=2021a
|
DATA=2021e
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
rm -rf work
|
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 Test31891(t *testing.T) { test31891(t) }
|
||||||
func Test42018(t *testing.T) { test42018(t) }
|
func Test42018(t *testing.T) { test42018(t) }
|
||||||
func Test45451(t *testing.T) { test45451(t) }
|
func Test45451(t *testing.T) { test45451(t) }
|
||||||
|
func Test49633(t *testing.T) { test49633(t) }
|
||||||
func TestAlign(t *testing.T) { testAlign(t) }
|
func TestAlign(t *testing.T) { testAlign(t) }
|
||||||
func TestAtol(t *testing.T) { testAtol(t) }
|
func TestAtol(t *testing.T) { testAtol(t) }
|
||||||
func TestBlocking(t *testing.T) { testBlocking(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.
|
// Issue 45451, bad handling of go:notinheap types.
|
||||||
typedef struct issue45451Undefined issue45451;
|
typedef struct issue45451Undefined issue45451;
|
||||||
|
|
||||||
|
// Issue 49633, example of cgo.Handle with void*.
|
||||||
|
extern void GoFunc49633(void*);
|
||||||
|
void cfunc49633(void *context) { GoFunc49633(context); }
|
||||||
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
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 (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -46,6 +47,14 @@ func test9400(t *testing.T) {
|
||||||
big[i] = pattern
|
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
|
// Temporarily rewind the stack and trigger SIGSETXID
|
||||||
issue9400.RewindAndSetgid()
|
issue9400.RewindAndSetgid()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ typedef struct {
|
||||||
int i;
|
int i;
|
||||||
} Issue38408, *PIssue38408;
|
} Issue38408, *PIssue38408;
|
||||||
|
|
||||||
|
extern void cfunc49633(void*); // definition is in test.go
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
|
@ -554,3 +555,26 @@ func GoFunc37033(handle C.uintptr_t) {
|
||||||
// A typedef pointer can be used as the element type.
|
// A typedef pointer can be used as the element type.
|
||||||
// No runtime test; just make sure it compiles.
|
// No runtime test; just make sure it compiles.
|
||||||
var _ C.PIssue38408 = &C.Issue38408{i: 1}
|
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"
|
"debug/elf"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -245,6 +248,29 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
|
||||||
|
|
||||||
var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
|
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
|
// checkLineComments checks that the export header generated by
|
||||||
// -buildmode=c-archive doesn't have any absolute paths in the #line
|
// -buildmode=c-archive doesn't have any absolute paths in the #line
|
||||||
// comments. We don't want those paths because they are unhelpful for
|
// 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) {
|
func TestInstall(t *testing.T) {
|
||||||
if !testWork {
|
if !testWork {
|
||||||
defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||||
|
|
@ -310,7 +503,7 @@ func TestEarlySignalHandler(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove("libgo2.a")
|
os.Remove("libgo2.a")
|
||||||
os.Remove("libgo2.h")
|
os.Remove("libgo2.h")
|
||||||
os.Remove("testp")
|
os.Remove("testp" + exeSuffix)
|
||||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -321,6 +514,7 @@ func TestEarlySignalHandler(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo2.h")
|
checkLineComments(t, "libgo2.h")
|
||||||
|
checkArchive(t, "libgo2.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
|
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
|
|
@ -350,7 +544,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove("libgo2.a")
|
os.Remove("libgo2.a")
|
||||||
os.Remove("libgo2.h")
|
os.Remove("libgo2.h")
|
||||||
os.Remove("testp")
|
os.Remove("testp" + exeSuffix)
|
||||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -361,6 +555,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo2.h")
|
checkLineComments(t, "libgo2.h")
|
||||||
|
checkArchive(t, "libgo2.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
|
|
@ -374,7 +569,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||||
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
|
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
|
||||||
|
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", cmd.Args, out)
|
||||||
expectSignal(t, err, syscall.SIGSEGV)
|
expectSignal(t, err, syscall.SIGSEGV)
|
||||||
|
|
||||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
// 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")...)
|
cmd = exec.Command(bin[0], append(bin[1:], "3")...)
|
||||||
|
|
||||||
out, err = cmd.CombinedOutput()
|
out, err = cmd.CombinedOutput()
|
||||||
|
if len(out) > 0 {
|
||||||
t.Logf("%s", out)
|
t.Logf("%s", out)
|
||||||
|
}
|
||||||
expectSignal(t, err, syscall.SIGPIPE)
|
expectSignal(t, err, syscall.SIGPIPE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -400,7 +597,7 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove("libgo2.a")
|
os.Remove("libgo2.a")
|
||||||
os.Remove("libgo2.h")
|
os.Remove("libgo2.h")
|
||||||
os.Remove("testp")
|
os.Remove("testp" + exeSuffix)
|
||||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -411,6 +608,7 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo2.h")
|
checkLineComments(t, "libgo2.h")
|
||||||
|
checkArchive(t, "libgo2.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
|
|
@ -517,7 +715,7 @@ func TestOsSignal(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove("libgo3.a")
|
os.Remove("libgo3.a")
|
||||||
os.Remove("libgo3.h")
|
os.Remove("libgo3.h")
|
||||||
os.Remove("testp")
|
os.Remove("testp" + exeSuffix)
|
||||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -528,6 +726,7 @@ func TestOsSignal(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo3.h")
|
checkLineComments(t, "libgo3.h")
|
||||||
|
checkArchive(t, "libgo3.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
|
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
|
|
@ -554,7 +753,7 @@ func TestSigaltstack(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove("libgo4.a")
|
os.Remove("libgo4.a")
|
||||||
os.Remove("libgo4.h")
|
os.Remove("libgo4.h")
|
||||||
os.Remove("testp")
|
os.Remove("testp" + exeSuffix)
|
||||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -565,6 +764,7 @@ func TestSigaltstack(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo4.h")
|
checkLineComments(t, "libgo4.h")
|
||||||
|
checkArchive(t, "libgo4.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
|
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
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")
|
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
out, err := cmd.CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", cmd.Args, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo6.h")
|
checkLineComments(t, "libgo6.h")
|
||||||
|
checkArchive(t, "libgo6.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
|
ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
ccArgs = append(ccArgs, "-lgo")
|
ccArgs = append(ccArgs, "-lgo")
|
||||||
}
|
}
|
||||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", ccArgs, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
checkIsExecutable(t, "./testp6"+exeSuffix)
|
||||||
|
|
||||||
argv := cmdToRun("./testp6")
|
argv := cmdToRun("./testp6")
|
||||||
cmd = exec.Command(argv[0], argv[1:]...)
|
cmd = exec.Command(argv[0], argv[1:]...)
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
out, err = cmd.CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", argv, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
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")
|
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
|
||||||
t.Log(cmd.Args)
|
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", cmd.Args, out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo2.h")
|
checkLineComments(t, "libgo2.h")
|
||||||
|
checkArchive(t, "libgo2.a")
|
||||||
|
|
||||||
exe := "./testnoshared" + exeSuffix
|
exe := "./testnoshared" + exeSuffix
|
||||||
|
|
||||||
|
|
@ -804,23 +1009,22 @@ func TestCompileWithoutShared(t *testing.T) {
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
ccArgs = append(ccArgs, "-lgo")
|
ccArgs = append(ccArgs, "-lgo")
|
||||||
}
|
}
|
||||||
t.Log(ccArgs)
|
|
||||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
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 -no-pie unrecognized, try -nopie if this is possibly clang
|
||||||
if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
|
if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
|
||||||
ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
|
ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
|
||||||
t.Log(ccArgs)
|
|
||||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||||
|
t.Logf("%v\n%s", ccArgs, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use either -no-pie or -nopie
|
// Don't use either -no-pie or -nopie
|
||||||
if err != nil && bytes.Contains(out, []byte("unrecognized")) {
|
if err != nil && bytes.Contains(out, []byte("unrecognized")) {
|
||||||
ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a")
|
ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a")
|
||||||
t.Log(ccArgs)
|
|
||||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||||
|
t.Logf("%v\n%s", ccArgs, out)
|
||||||
}
|
}
|
||||||
t.Logf("%s", out)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -829,17 +1033,15 @@ func TestCompileWithoutShared(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
binArgs := append(cmdToRun(exe), "1")
|
binArgs := append(cmdToRun(exe), "1")
|
||||||
t.Log(binArgs)
|
|
||||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
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)
|
expectSignal(t, err, syscall.SIGSEGV)
|
||||||
|
|
||||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||||
binArgs := append(cmdToRun(exe), "3")
|
binArgs := append(cmdToRun(exe), "3")
|
||||||
t.Log(binArgs)
|
|
||||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
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)
|
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")
|
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
out, err := cmd.CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", cmd.Args, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo7.h")
|
checkLineComments(t, "libgo7.h")
|
||||||
|
checkArchive(t, "libgo7.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
|
ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
ccArgs = append(ccArgs, "-lgo")
|
ccArgs = append(ccArgs, "-lgo")
|
||||||
}
|
}
|
||||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", ccArgs, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
checkIsExecutable(t, "./testp7"+exeSuffix)
|
||||||
|
|
||||||
argv := cmdToRun("./testp7")
|
argv := cmdToRun("./testp7")
|
||||||
cmd = exec.Command(argv[0], argv[1:]...)
|
cmd = exec.Command(argv[0], argv[1:]...)
|
||||||
var sb strings.Builder
|
sb := new(strings.Builder)
|
||||||
cmd.Stdout = &sb
|
cmd.Stdout = sb
|
||||||
cmd.Stderr = &sb
|
cmd.Stderr = sb
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -926,8 +1132,9 @@ func TestManyCalls(t *testing.T) {
|
||||||
)
|
)
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
err = cmd.Wait()
|
||||||
t.Log(sb.String())
|
t.Logf("%v\n%s", cmd.Args, sb)
|
||||||
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -949,23 +1156,27 @@ func TestPreemption(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
|
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
out, err := cmd.CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", cmd.Args, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
checkLineComments(t, "libgo8.h")
|
checkLineComments(t, "libgo8.h")
|
||||||
|
checkArchive(t, "libgo8.a")
|
||||||
|
|
||||||
ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
|
ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
|
||||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||||
t.Logf("%s", out)
|
t.Logf("%v\n%s", ccArgs, out)
|
||||||
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
checkIsExecutable(t, "./testp8"+exeSuffix)
|
||||||
|
|
||||||
argv := cmdToRun("./testp8")
|
argv := cmdToRun("./testp8")
|
||||||
cmd = exec.Command(argv[0], argv[1:]...)
|
cmd = exec.Command(argv[0], argv[1:]...)
|
||||||
var sb strings.Builder
|
sb := new(strings.Builder)
|
||||||
cmd.Stdout = &sb
|
cmd.Stdout = sb
|
||||||
cmd.Stderr = &sb
|
cmd.Stderr = sb
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -978,8 +1189,9 @@ func TestPreemption(t *testing.T) {
|
||||||
)
|
)
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
err = cmd.Wait()
|
||||||
t.Log(sb.String())
|
t.Logf("%v\n%s", cmd.Args, sb)
|
||||||
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -781,10 +781,10 @@ func copyFile(t *testing.T, dst, src string) {
|
||||||
|
|
||||||
func TestGo2C2Go(t *testing.T) {
|
func TestGo2C2Go(t *testing.T) {
|
||||||
switch GOOS {
|
switch GOOS {
|
||||||
case "darwin", "ios":
|
case "darwin", "ios", "windows":
|
||||||
// Darwin shared libraries don't support the multiple
|
// Non-ELF shared libraries don't support the multiple
|
||||||
// copies of the runtime package implied by this test.
|
// 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":
|
case "android":
|
||||||
t.Skip("test fails on android; issue 29087")
|
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
|
// Test with main using -buildmode=pie with plugin for issue #43228
|
||||||
func TestIssue25756pie(t *testing.T) {
|
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=plugin", "-o", "life.so", "./issue25756/plugin")
|
||||||
goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
|
goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
|
||||||
run(t, "./issue25756pie.exe")
|
run(t, "./issue25756pie.exe")
|
||||||
|
|
@ -293,3 +289,31 @@ func TestIssue44956(t *testing.T) {
|
||||||
goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
|
goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
|
||||||
run(t, "./issue44956.exe")
|
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 {
|
cases := []struct {
|
||||||
src string
|
src string
|
||||||
memoryAccessError string
|
memoryAccessError string
|
||||||
|
errorLocation string
|
||||||
}{
|
}{
|
||||||
{src: "asan1_fail.go", memoryAccessError: "heap-use-after-free"},
|
{src: "asan1_fail.go", memoryAccessError: "heap-use-after-free", errorLocation: "asan1_fail.go:25"},
|
||||||
{src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow"},
|
{src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow", errorLocation: "asan2_fail.go:31"},
|
||||||
{src: "asan3_fail.go", memoryAccessError: "use-after-poison"},
|
{src: "asan3_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan3_fail.go:13"},
|
||||||
{src: "asan4_fail.go", memoryAccessError: "use-after-poison"},
|
{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"},
|
{src: "asan_useAfterReturn.go"},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|
@ -54,8 +56,21 @@ func TestASAN(t *testing.T) {
|
||||||
|
|
||||||
cmd := hangProneCmd(outPath)
|
cmd := hangProneCmd(outPath)
|
||||||
if tc.memoryAccessError != "" {
|
if tc.memoryAccessError != "" {
|
||||||
out, err := cmd.CombinedOutput()
|
outb, err := cmd.CombinedOutput()
|
||||||
if err != nil && strings.Contains(string(out), tc.memoryAccessError) {
|
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
|
return
|
||||||
}
|
}
|
||||||
t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)
|
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
|
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 {
|
type compilerCheck struct {
|
||||||
once sync.Once
|
once sync.Once
|
||||||
err error
|
err error
|
||||||
|
|
@ -269,6 +286,8 @@ func configure(sanitizer string) *config {
|
||||||
|
|
||||||
case "address":
|
case "address":
|
||||||
c.goFlags = append(c.goFlags, "-asan")
|
c.goFlags = append(c.goFlags, "-asan")
|
||||||
|
// Set the debug mode to print the C stack trace.
|
||||||
|
c.cFlags = append(c.cFlags, "-g")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
|
panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ func main() {
|
||||||
// C passes Go an invalid pointer.
|
// C passes Go an invalid pointer.
|
||||||
a := C.test()
|
a := C.test()
|
||||||
// Use after free
|
// Use after free
|
||||||
*a = 2
|
*a = 2 // BOOM
|
||||||
// We shouldn't get here; asan should stop us first.
|
// We shouldn't get here; asan should stop us first.
|
||||||
fmt.Println(*a)
|
fmt.Println(*a)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func main() {
|
||||||
a := C.f()
|
a := C.f()
|
||||||
q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
|
q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
|
||||||
// Access to C pointer out of bounds.
|
// Access to C pointer out of bounds.
|
||||||
*q5 = 100
|
*q5 = 100 // BOOM
|
||||||
// We shouldn't get here; asan should stop us first.
|
// We shouldn't get here; asan should stop us first.
|
||||||
fmt.Printf("q5: %d, %x\n", *q5, q5)
|
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"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -55,7 +56,7 @@ func runWithEnv(t *testing.T, msg string, env []string, args ...string) {
|
||||||
// t.Fatalf if the command fails.
|
// t.Fatalf if the command fails.
|
||||||
func goCmd(t *testing.T, args ...string) string {
|
func goCmd(t *testing.T, args ...string) string {
|
||||||
newargs := []string{args[0]}
|
newargs := []string{args[0]}
|
||||||
if *testX {
|
if *testX && args[0] != "env" {
|
||||||
newargs = append(newargs, "-x")
|
newargs = append(newargs, "-x")
|
||||||
}
|
}
|
||||||
newargs = append(newargs, args[1:]...)
|
newargs = append(newargs, args[1:]...)
|
||||||
|
|
@ -461,7 +462,9 @@ func TestTrivialExecutable(t *testing.T) {
|
||||||
run(t, "trivial executable", "../../bin/trivial")
|
run(t, "trivial executable", "../../bin/trivial")
|
||||||
AssertIsLinkedTo(t, "../../bin/trivial", soname)
|
AssertIsLinkedTo(t, "../../bin/trivial", soname)
|
||||||
AssertHasRPath(t, "../../bin/trivial", gorootInstallDir)
|
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.
|
// 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")
|
run(t, "trivial executable", "./trivial.pie")
|
||||||
AssertIsLinkedTo(t, "./trivial.pie", soname)
|
AssertIsLinkedTo(t, "./trivial.pie", soname)
|
||||||
AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
|
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.
|
// Check that the file size does not exceed a limit.
|
||||||
|
|
@ -694,7 +699,15 @@ func requireGccgo(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output)
|
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)))
|
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.
|
# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang.
|
||||||
CLANG=`xcrun --sdk $SDK --find 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) {
|
func TestRepeatBootstrap(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skipf("skipping test that rebuilds the entire toolchain")
|
||||||
|
}
|
||||||
|
|
||||||
goroot, err := os.MkdirTemp("", "reboot-goroot")
|
goroot, err := os.MkdirTemp("", "reboot-goroot")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -538,7 +538,7 @@ type headerFileInfo struct {
|
||||||
func (fi headerFileInfo) Size() int64 { return fi.h.Size }
|
func (fi headerFileInfo) Size() int64 { return fi.h.Size }
|
||||||
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
||||||
func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
|
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.
|
// Name returns the base name of the file.
|
||||||
func (fi headerFileInfo) Name() string {
|
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
|
wantLCnt int64
|
||||||
wantPCnt int64
|
wantPCnt int64
|
||||||
}
|
}
|
||||||
testFnc interface{} // testRead | testWriteTo | testRemaining
|
testFnc any // testRead | testWriteTo | testRemaining
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
@ -1376,7 +1376,7 @@ func TestFileReader(t *testing.T) {
|
||||||
spd sparseDatas
|
spd sparseDatas
|
||||||
size int64
|
size int64
|
||||||
}
|
}
|
||||||
fileMaker interface{} // makeReg | makeSparse
|
fileMaker any // makeReg | makeSparse
|
||||||
)
|
)
|
||||||
|
|
||||||
vectors := []struct {
|
vectors := []struct {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
|
|
||||||
type testError struct{ error }
|
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
|
// testFile is an io.ReadWriteSeeker where the IO operations performed
|
||||||
// on it must match the list of operations in ops.
|
// on it must match the list of operations in ops.
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ func TestWriter(t *testing.T) {
|
||||||
testClose struct { // Close() == wantErr
|
testClose struct { // Close() == wantErr
|
||||||
wantErr error
|
wantErr error
|
||||||
}
|
}
|
||||||
testFnc interface{} // testHeader | testWrite | testReadFrom | testClose
|
testFnc any // testHeader | testWrite | testReadFrom | testClose
|
||||||
)
|
)
|
||||||
|
|
||||||
vectors := []struct {
|
vectors := []struct {
|
||||||
|
|
@ -1031,7 +1031,7 @@ func TestFileWriter(t *testing.T) {
|
||||||
wantLCnt int64
|
wantLCnt int64
|
||||||
wantPCnt int64
|
wantPCnt int64
|
||||||
}
|
}
|
||||||
testFnc interface{} // testWrite | testReadFrom | testRemaining
|
testFnc any // testWrite | testReadFrom | testRemaining
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
@ -1044,7 +1044,7 @@ func TestFileWriter(t *testing.T) {
|
||||||
sph sparseHoles
|
sph sparseHoles
|
||||||
size int64
|
size int64
|
||||||
}
|
}
|
||||||
fileMaker interface{} // makeReg | makeSparse
|
fileMaker any // makeReg | makeSparse
|
||||||
)
|
)
|
||||||
|
|
||||||
vectors := []struct {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.readDataDescriptor()
|
|
||||||
z.File = append(z.File, f)
|
z.File = append(z.File, f)
|
||||||
}
|
}
|
||||||
if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here
|
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
|
return nil, ErrAlgorithm
|
||||||
}
|
}
|
||||||
var rc io.ReadCloser = dcomp(r)
|
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 = &checksumReader{
|
||||||
rc: rc,
|
rc: rc,
|
||||||
hash: crc32.NewIEEE(),
|
hash: crc32.NewIEEE(),
|
||||||
f: f,
|
f: f,
|
||||||
|
desr: desr,
|
||||||
}
|
}
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
@ -205,48 +209,12 @@ func (f *File) OpenRaw() (io.Reader, error) {
|
||||||
return r, nil
|
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 {
|
type checksumReader struct {
|
||||||
rc io.ReadCloser
|
rc io.ReadCloser
|
||||||
hash hash.Hash32
|
hash hash.Hash32
|
||||||
nread uint64 // number of bytes read so far
|
nread uint64 // number of bytes read so far
|
||||||
f *File
|
f *File
|
||||||
|
desr io.Reader // if non-nil, where to read the data descriptor
|
||||||
err error // sticky error
|
err error // sticky error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,12 +236,12 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
|
||||||
if r.nread != r.f.UncompressedSize64 {
|
if r.nread != r.f.UncompressedSize64 {
|
||||||
return 0, io.ErrUnexpectedEOF
|
return 0, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
if r.f.hasDataDescriptor() {
|
if r.desr != nil {
|
||||||
if r.f.descErr != nil {
|
if err1 := readDataDescriptor(r.desr, r.f); err1 != nil {
|
||||||
if r.f.descErr == io.EOF {
|
if err1 == io.EOF {
|
||||||
err = io.ErrUnexpectedEOF
|
err = io.ErrUnexpectedEOF
|
||||||
} else {
|
} else {
|
||||||
err = r.f.descErr
|
err = err1
|
||||||
}
|
}
|
||||||
} else if r.hash.Sum32() != r.f.CRC32 {
|
} else if r.hash.Sum32() != r.f.CRC32 {
|
||||||
err = ErrChecksum
|
err = ErrChecksum
|
||||||
|
|
@ -485,10 +453,8 @@ parseExtras:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
|
func readDataDescriptor(r io.Reader, f *File) error {
|
||||||
// Create enough space for the largest possible size
|
var buf [dataDescriptorLen]byte
|
||||||
var buf [dataDescriptor64Len]byte
|
|
||||||
|
|
||||||
// The spec says: "Although not originally assigned a
|
// The spec says: "Although not originally assigned a
|
||||||
// signature, the value 0x08074b50 has commonly been adopted
|
// signature, the value 0x08074b50 has commonly been adopted
|
||||||
// as a signature value for the data descriptor record.
|
// 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
|
// descriptors and should account for either case when reading
|
||||||
// ZIP files to ensure compatibility."
|
// 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 {
|
if _, err := io.ReadFull(r, buf[:4]); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
off := 0
|
off := 0
|
||||||
maybeSig := readBuf(buf[:4])
|
maybeSig := readBuf(buf[:4])
|
||||||
|
|
@ -508,28 +475,21 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
|
||||||
// bytes.
|
// bytes.
|
||||||
off += 4
|
off += 4
|
||||||
}
|
}
|
||||||
|
if _, err := io.ReadFull(r, buf[off:12]); err != nil {
|
||||||
end := dataDescriptorLen - 4
|
return err
|
||||||
if zip64 {
|
|
||||||
end = dataDescriptor64Len - 4
|
|
||||||
}
|
}
|
||||||
if _, err := io.ReadFull(r, buf[off:end]); err != nil {
|
b := readBuf(buf[:12])
|
||||||
return nil, err
|
if b.uint32() != f.CRC32 {
|
||||||
}
|
return ErrChecksum
|
||||||
b := readBuf(buf[:end])
|
|
||||||
|
|
||||||
out := &dataDescriptor{
|
|
||||||
crc32: b.uint32(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if zip64 {
|
// The two sizes that follow here can be either 32 bits or 64 bits
|
||||||
out.compressedSize = b.uint64()
|
// but the spec is not very clear on this and different
|
||||||
out.uncompressedSize = b.uint64()
|
// interpretations has been made causing incompatibilities. We
|
||||||
} else {
|
// already have the sizes from the central directory so we can
|
||||||
out.compressedSize = uint64(b.uint32())
|
// just ignore these.
|
||||||
out.uncompressedSize = uint64(b.uint32())
|
|
||||||
}
|
return nil
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
|
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) Mode() fs.FileMode { return fs.ModeDir | 0555 }
|
||||||
func (f *fileListEntry) Type() fs.FileMode { return fs.ModeDir }
|
func (f *fileListEntry) Type() fs.FileMode { return fs.ModeDir }
|
||||||
func (f *fileListEntry) IsDir() bool { return true }
|
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 {
|
func (f *fileListEntry) ModTime() time.Time {
|
||||||
if f.file == nil {
|
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) {
|
func TestCVE202133196(t *testing.T) {
|
||||||
// Archive that indicates it has 1 << 128 -1 files,
|
// Archive that indicates it has 1 << 128 -1 files,
|
||||||
// this would previously cause a panic due to attempting
|
// 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) Mode() fs.FileMode { return fi.fh.Mode() }
|
||||||
func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() }
|
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 }
|
func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil }
|
||||||
|
|
||||||
|
|
@ -390,11 +390,3 @@ func unixModeToFileMode(m uint32) fs.FileMode {
|
||||||
}
|
}
|
||||||
return mode
|
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
|
// 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])
|
n = copy(p, b.buf[b.r:b.w])
|
||||||
b.r += n
|
b.r += n
|
||||||
b.lastByte = int(b.buf[b.r-1])
|
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.
|
// 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 {
|
func NewWriter(w io.Writer) *Writer {
|
||||||
return NewWriterSize(w, defaultBufSize)
|
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.
|
// 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 {
|
if rn.Intn(8) == 0 && cap(b) > 0 {
|
||||||
b = b[1:1:cap(b)]
|
b = b[1:1:cap(b)]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,16 @@ type byte = uint8
|
||||||
// used, by convention, to distinguish character values from integer values.
|
// used, by convention, to distinguish character values from integer values.
|
||||||
type rune = int32
|
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
|
// iota is a predeclared identifier representing the untyped integer ordinal
|
||||||
// number of the current const specification in a (usually parenthesized)
|
// number of the current const specification in a (usually parenthesized)
|
||||||
// const declaration. It is zero-indexed.
|
// 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
|
// that point, the program is terminated with a non-zero exit code. This
|
||||||
// termination sequence is called panicking and can be controlled by the
|
// termination sequence is called panicking and can be controlled by the
|
||||||
// built-in function recover.
|
// 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
|
// The recover built-in function allows a program to manage behavior of a
|
||||||
// panicking goroutine. Executing a call to recover inside a deferred
|
// 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
|
// panicking, or if the argument supplied to panic was nil, recover returns
|
||||||
// nil. Thus the return value from recover reports whether the goroutine is
|
// nil. Thus the return value from recover reports whether the goroutine is
|
||||||
// panicking.
|
// panicking.
|
||||||
func recover() interface{}
|
func recover() any
|
||||||
|
|
||||||
// The print built-in function formats its arguments in an
|
// The print built-in function formats its arguments in an
|
||||||
// implementation-specific way and writes the result to standard error.
|
// implementation-specific way and writes the result to standard error.
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func TestReaderAt(t *testing.T) {
|
||||||
off int64
|
off int64
|
||||||
n int
|
n int
|
||||||
want string
|
want string
|
||||||
wanterr interface{}
|
wanterr any
|
||||||
}{
|
}{
|
||||||
{0, 10, "0123456789", nil},
|
{0, 10, "0123456789", nil},
|
||||||
{1, 10, "123456789", io.EOF},
|
{1, 10, "123456789", io.EOF},
|
||||||
|
|
|
||||||
|
|
@ -459,8 +459,11 @@ type listImports struct {
|
||||||
|
|
||||||
var listCache sync.Map // map[string]listImports, keyed by contextName
|
var listCache sync.Map // map[string]listImports, keyed by contextName
|
||||||
|
|
||||||
// listSem is a semaphore restricting concurrent invocations of 'go list'.
|
// listSem is a semaphore restricting concurrent invocations of 'go list'. 'go
|
||||||
var listSem = make(chan semToken, runtime.GOMAXPROCS(0))
|
// 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{}
|
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))
|
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...)
|
f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...)
|
||||||
if strings.Contains(f, "\n") {
|
if strings.Contains(f, "\n") {
|
||||||
panic("feature contains newlines: " + f)
|
panic("feature contains newlines: " + f)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !amd64
|
||||||
// +build !amd64
|
// +build !amd64
|
||||||
|
|
||||||
package p
|
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 chanVar chan int
|
||||||
|
|
||||||
var ifaceVar interface{} = 5
|
var ifaceVar any = 5
|
||||||
|
|
||||||
var assertVar = ifaceVar.(int)
|
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
|
package p4
|
||||||
|
|
||||||
type Pair[T1 interface { M() }, T2 ~int] struct {
|
type Pair[T1 interface{ M() }, T2 ~int] struct {
|
||||||
f1 T1
|
f1 T1
|
||||||
f2 T2
|
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}
|
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)
|
* `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
|
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 it, so the first thing that the gc
|
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
|
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.
|
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
|
inference, which determine which object belongs to which identifier, and what
|
||||||
type each expression has. Type-checking includes certain extra checks, such as
|
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
|
"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
|
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.
|
concern any single computer architecture, and thus run on all `GOARCH` variants.
|
||||||
|
These passes include dead code elimination, removal of
|
||||||
Some examples of these generic passes include dead code elimination, removal of
|
|
||||||
unneeded nil checks, and removal of unused branches. The generic rewrite rules
|
unneeded nil checks, and removal of unused branches. The generic rewrite rules
|
||||||
mainly concern expressions, such as replacing some expressions with constant
|
mainly concern expressions, such as replacing some expressions with constant
|
||||||
values, and optimizing multiplications and float operations.
|
values, and optimizing multiplications and float operations.
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,11 @@ Special-purpose registers are as follows:
|
||||||
| R13 | Scratch | Scratch | Scratch |
|
| R13 | Scratch | Scratch | Scratch |
|
||||||
| R14 | Current goroutine | Same | Same |
|
| R14 | Current goroutine | Same | Same |
|
||||||
| R15 | GOT reference temporary if dynlink | 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
|
*Rationale*: These register meanings are compatible with Go’s
|
||||||
stack-based calling convention except for R14 and X15, which will have
|
stack-based calling convention except for R14 and X15, which will have
|
||||||
|
|
|
||||||
|
|
@ -715,19 +715,20 @@ func setup() {
|
||||||
synthOnce.Do(func() {
|
synthOnce.Do(func() {
|
||||||
fname := types.BuiltinPkg.Lookup
|
fname := types.BuiltinPkg.Lookup
|
||||||
nxp := src.NoXPos
|
nxp := src.NoXPos
|
||||||
unsp := types.Types[types.TUNSAFEPTR]
|
bp := types.NewPtr(types.Types[types.TUINT8])
|
||||||
ui := types.Types[types.TUINTPTR]
|
it := types.Types[types.TINT]
|
||||||
synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
|
synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
|
||||||
types.NewField(nxp, fname("ptr"), unsp),
|
types.NewField(nxp, fname("ptr"), bp),
|
||||||
types.NewField(nxp, fname("len"), ui),
|
types.NewField(nxp, fname("len"), it),
|
||||||
types.NewField(nxp, fname("cap"), ui),
|
types.NewField(nxp, fname("cap"), it),
|
||||||
})
|
})
|
||||||
types.CalcStructSize(synthSlice)
|
types.CalcStructSize(synthSlice)
|
||||||
synthString = types.NewStruct(types.NoPkg, []*types.Field{
|
synthString = types.NewStruct(types.NoPkg, []*types.Field{
|
||||||
types.NewField(nxp, fname("data"), unsp),
|
types.NewField(nxp, fname("data"), bp),
|
||||||
types.NewField(nxp, fname("len"), ui),
|
types.NewField(nxp, fname("len"), it),
|
||||||
})
|
})
|
||||||
types.CalcStructSize(synthString)
|
types.CalcStructSize(synthString)
|
||||||
|
unsp := types.Types[types.TUNSAFEPTR]
|
||||||
synthIface = types.NewStruct(types.NoPkg, []*types.Field{
|
synthIface = types.NewStruct(types.NoPkg, []*types.Field{
|
||||||
types.NewField(nxp, fname("f1"), unsp),
|
types.NewField(nxp, fname("f1"), unsp),
|
||||||
types.NewField(nxp, fname("f2"), unsp),
|
types.NewField(nxp, fname("f2"), unsp),
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,18 @@ func TestGoAMD64v1(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("couldn't execute test: %s", err)
|
t.Fatalf("couldn't execute test: %s", err)
|
||||||
}
|
}
|
||||||
if string(out) != "PASS\n" {
|
// Expect to see output of the form "PASS\n", unless the test binary
|
||||||
t.Fatalf("test reported error: %s", string(out))
|
// 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")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
// If this is a released compiler version, ask for a bug report.
|
// 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("\n")
|
||||||
fmt.Printf("Please file a bug report including a short program that triggers the error.\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 {
|
} else {
|
||||||
// Not a release; dump a stack trace, too.
|
// Not a release; dump a stack trace, too.
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,19 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
|
||||||
dcl := apDecls
|
dcl := apDecls
|
||||||
if fnsym.WasInlined() {
|
if fnsym.WasInlined() {
|
||||||
dcl = preInliningDcls(fnsym)
|
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
|
// 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.
|
// TODO(mdempsky): Update tests to expect this.
|
||||||
goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
|
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 loc.escapes {
|
||||||
if n.Op() == ir.ONAME {
|
if n.Op() == ir.ONAME {
|
||||||
if base.Flag.CompilingRuntime {
|
if base.Flag.CompilingRuntime {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func dumpasmhdr() {
|
||||||
if t == constant.Float || t == constant.Complex {
|
if t == constant.Float || t == constant.Complex {
|
||||||
break
|
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:
|
case ir.OTYPE:
|
||||||
t := n.Type()
|
t := n.Type()
|
||||||
|
|
|
||||||
|
|
@ -35,18 +35,18 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func hidePanic() {
|
// handlePanic ensures that we print out an "internal compiler error" for any panic
|
||||||
if base.Debug.Panic == 0 && base.Errors() > 0 {
|
// or runtime exception during front-end compiler processing (unless there have
|
||||||
// If we've already complained about things
|
// already been some compiler errors). It may also be invoked from the explicit panic in
|
||||||
// in the program, don't bother complaining
|
// hcrash(), in which case, we pass the panic on through.
|
||||||
// about a panic too; let the user clean up
|
func handlePanic() {
|
||||||
// the code and try again.
|
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
if err == "-h" {
|
if err == "-h" {
|
||||||
|
// Force real panic now with -h option (hcrash) - the error
|
||||||
|
// information will have already been printed.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
base.ErrorExit()
|
base.Fatalf("panic: %v", err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ func hidePanic() {
|
||||||
func Main(archInit func(*ssagen.ArchInfo)) {
|
func Main(archInit func(*ssagen.ArchInfo)) {
|
||||||
base.Timer.Start("fe", "init")
|
base.Timer.Start("fe", "init")
|
||||||
|
|
||||||
defer hidePanic()
|
defer handlePanic()
|
||||||
|
|
||||||
archInit(&ssagen.Arch)
|
archInit(&ssagen.Arch)
|
||||||
|
|
||||||
|
|
@ -245,11 +245,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
||||||
base.Timer.Start("fe", "inlining")
|
base.Timer.Start("fe", "inlining")
|
||||||
if base.Flag.LowerL != 0 {
|
if base.Flag.LowerL != 0 {
|
||||||
inline.InlinePackage()
|
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
|
noder.MakeWrappers(typecheck.Target) // must happen after inlining
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmd/compile/internal/types2"
|
"cmd/compile/internal/types2"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/goexperiment"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -107,28 +108,32 @@ func TestImportTestdata(t *testing.T) {
|
||||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testfiles := map[string][]string{
|
||||||
|
"exports.go": {"go/ast", "go/token"},
|
||||||
|
}
|
||||||
|
if !goexperiment.Unified {
|
||||||
|
testfiles["generics.go"] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for testfile, wantImports := range testfiles {
|
||||||
tmpdir := mktmpdir(t)
|
tmpdir := mktmpdir(t)
|
||||||
defer os.RemoveAll(tmpdir)
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
|
compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
|
||||||
|
path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
|
||||||
|
|
||||||
if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
|
if pkg := testPath(t, path, tmpdir); pkg != nil {
|
||||||
// The package's Imports list must include all packages
|
// The package's Imports list must include all packages
|
||||||
// explicitly imported by exports.go, plus all packages
|
// explicitly imported by testfile, plus all packages
|
||||||
// referenced indirectly via exported objects in exports.go.
|
// referenced indirectly via exported objects in testfile.
|
||||||
// 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())
|
got := fmt.Sprint(pkg.Imports())
|
||||||
for _, want := range []string{"go/ast", "go/token"} {
|
for _, want := range wantImports {
|
||||||
if !strings.Contains(got, want) {
|
if !strings.Contains(got, want) {
|
||||||
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersionHandling(t *testing.T) {
|
func TestVersionHandling(t *testing.T) {
|
||||||
|
|
@ -253,7 +258,7 @@ var importedObjectTests = []struct {
|
||||||
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
|
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
|
||||||
|
|
||||||
// interfaces
|
// 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}"},
|
{"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)}"},
|
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
|
||||||
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
|
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ package importer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
"cmd/compile/internal/types2"
|
"cmd/compile/internal/types2"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -45,7 +46,7 @@ func (r *intReader) uint64() uint64 {
|
||||||
const (
|
const (
|
||||||
iexportVersionGo1_11 = 0
|
iexportVersionGo1_11 = 0
|
||||||
iexportVersionPosCol = 1
|
iexportVersionPosCol = 1
|
||||||
iexportVersionGenerics = 1 // probably change to 2 before release
|
iexportVersionGenerics = 2
|
||||||
iexportVersionGo1_18 = 2
|
iexportVersionGo1_18 = 2
|
||||||
|
|
||||||
iexportVersionCurrent = 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),
|
typCache: make(map[uint64]types2.Type),
|
||||||
// Separate map for typeparams, keyed by their package and unique
|
// Separate map for typeparams, keyed by their package and unique
|
||||||
// name (name with subscript).
|
// name (name with subscript).
|
||||||
tparamIndex: make(map[ident]types2.Type),
|
tparamIndex: make(map[ident]*types2.TypeParam),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, pt := range predeclared {
|
for i, pt := range predeclared {
|
||||||
|
|
@ -202,7 +203,7 @@ type iimporter struct {
|
||||||
declData string
|
declData string
|
||||||
pkgIndex map[*types2.Package]map[string]uint64
|
pkgIndex map[*types2.Package]map[string]uint64
|
||||||
typCache map[uint64]types2.Type
|
typCache map[uint64]types2.Type
|
||||||
tparamIndex map[ident]types2.Type
|
tparamIndex map[ident]*types2.TypeParam
|
||||||
|
|
||||||
interfaceList []*types2.Interface
|
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 {
|
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
|
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:])
|
r.declReader = *strings.NewReader(p.declData[off-predeclReserved:])
|
||||||
t := r.doType(base)
|
t := r.doType(base)
|
||||||
|
|
||||||
if base == nil || !isInterface(t) {
|
if canReuse(base, t) {
|
||||||
p.typCache[off] = t
|
p.typCache[off] = t
|
||||||
}
|
}
|
||||||
return 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 {
|
type importReader struct {
|
||||||
p *iimporter
|
p *iimporter
|
||||||
declReader strings.Reader
|
declReader strings.Reader
|
||||||
|
|
@ -358,12 +377,12 @@ func (r *importReader) obj(name string) {
|
||||||
if r.p.exportVersion < iexportVersionGenerics {
|
if r.p.exportVersion < iexportVersionGenerics {
|
||||||
errorf("unexpected type param type")
|
errorf("unexpected type param type")
|
||||||
}
|
}
|
||||||
// Remove the "path" from the type param name that makes it unique
|
name0 := typecheck.TparamName(name)
|
||||||
ix := strings.LastIndex(name, ".")
|
if name0 == "" {
|
||||||
if ix < 0 {
|
errorf("malformed type parameter export name %s: missing prefix", name)
|
||||||
errorf("missing path for type param")
|
|
||||||
}
|
}
|
||||||
tn := types2.NewTypeName(pos, r.currPkg, name[ix+1:], nil)
|
|
||||||
|
tn := types2.NewTypeName(pos, r.currPkg, name0, nil)
|
||||||
t := types2.NewTypeParam(tn, nil)
|
t := types2.NewTypeParam(tn, nil)
|
||||||
// To handle recursive references to the typeparam within its
|
// To handle recursive references to the typeparam within its
|
||||||
// bound, save the partial type in tparamIndex before reading the bounds.
|
// 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)
|
xs := make([]*types2.TypeParam, n)
|
||||||
for i := range xs {
|
for i := range xs {
|
||||||
typ := r.typ()
|
xs[i] = r.typ().(*types2.TypeParam)
|
||||||
xs[i] = types2.AsTypeParam(typ)
|
|
||||||
}
|
}
|
||||||
return xs
|
return xs
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,14 @@ var predeclared = []types2.Type{
|
||||||
types2.Typ[types2.Invalid], // only appears in packages with errors
|
types2.Typ[types2.Invalid], // only appears in packages with errors
|
||||||
|
|
||||||
// used internally by gc; never used by this package or in .a files
|
// used internally by gc; never used by this package or in .a files
|
||||||
|
// not to be confused with the universe any
|
||||||
anyType{},
|
anyType{},
|
||||||
|
|
||||||
// comparable
|
// comparable
|
||||||
types2.Universe.Lookup("comparable").Type(),
|
types2.Universe.Lookup("comparable").Type(),
|
||||||
|
|
||||||
|
// any
|
||||||
|
types2.Universe.Lookup("any").Type(),
|
||||||
}
|
}
|
||||||
|
|
||||||
type anyType struct{}
|
type anyType struct{}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ const (
|
||||||
C5 = 1234i
|
C5 = 1234i
|
||||||
C6 = "foo\n"
|
C6 = "foo\n"
|
||||||
C7 = `bar\n`
|
C7 = `bar\n`
|
||||||
|
C8 = 42
|
||||||
|
C9 int = 42
|
||||||
|
C10 float64 = 42
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
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 does the necessary substitions for a ClosureExpr n and returns the new
|
||||||
// closure node.
|
// closure node.
|
||||||
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
||||||
// Prior to the subst edit, set a flag in the inlsubst to
|
// Prior to the subst edit, set a flag in the inlsubst to indicate
|
||||||
// indicated that we don't want to update the source positions in
|
// that we don't want to update the source positions in the new
|
||||||
// the new closure. If we do this, it will appear that the closure
|
// closure function. If we do this, it will appear that the
|
||||||
// itself has things inlined into it, which is not the case. See
|
// closure itself has things inlined into it, which is not the
|
||||||
// issue #46234 for more details.
|
// 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)
|
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
|
||||||
subst.noPosUpdate = true
|
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
|
// Actually create the named function for the closure, now that
|
||||||
// the closure is inlined in a specific function.
|
// the closure is inlined in a specific function.
|
||||||
newclo := newfn.OClosure
|
newclo := newfn.OClosure
|
||||||
|
newclo.SetPos(newClosurePos)
|
||||||
newclo.SetInit(subst.list(n.Init()))
|
newclo.SetInit(subst.list(n.Init()))
|
||||||
return typecheck.Expr(newclo)
|
return typecheck.Expr(newclo)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,7 @@ func (n *ConstExpr) Val() constant.Value { return n.val }
|
||||||
type ConvExpr struct {
|
type ConvExpr struct {
|
||||||
miniExpr
|
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 {
|
func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr {
|
||||||
|
|
|
||||||
|
|
@ -471,7 +471,7 @@ const (
|
||||||
UintptrEscapes // pointers converted to uintptr escape
|
UintptrEscapes // pointers converted to uintptr escape
|
||||||
|
|
||||||
// Runtime-only func pragmas.
|
// 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
|
Systemstack // func must run on system stack
|
||||||
Nowritebarrier // emit compiler error instead of write barrier
|
Nowritebarrier // emit compiler error instead of write barrier
|
||||||
Nowritebarrierrec // error on write barrier in this or recursive callees
|
Nowritebarrierrec // error on write barrier in this or recursive callees
|
||||||
|
|
@ -584,7 +584,7 @@ func OuterValue(n Node) Node {
|
||||||
for {
|
for {
|
||||||
switch nn := n; nn.Op() {
|
switch nn := n; nn.Op() {
|
||||||
case OXDOT:
|
case OXDOT:
|
||||||
base.FatalfAt(n.Pos(), "OXDOT in walk: %v", n)
|
base.FatalfAt(n.Pos(), "OXDOT in OuterValue: %v", n)
|
||||||
case ODOT:
|
case ODOT:
|
||||||
nn := nn.(*SelectorExpr)
|
nn := nn.(*SelectorExpr)
|
||||||
n = nn.X
|
n = nn.X
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
||||||
for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPS64MOVVreg {
|
for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPS64MOVVreg {
|
||||||
a = a.Args[0]
|
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
|
t := a.Type
|
||||||
switch {
|
switch {
|
||||||
case v.Op == ssa.OpMIPS64MOVBreg && t.Size() == 1 && t.IsSigned(),
|
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) {
|
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
|
// 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.
|
// during types2-to-types1 translation if this is a generic function.
|
||||||
g.curDecl = decl.Name.Value
|
g.curDecl = decl.Name.Value
|
||||||
|
|
@ -94,7 +95,7 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
|
||||||
if recv != nil {
|
if recv != nil {
|
||||||
t2 := deref2(recv.Type())
|
t2 := deref2(recv.Type())
|
||||||
// This is a method, so set g.curDecl to recvTypeName.methName instead.
|
// 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))
|
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)
|
g.target.Inits = append(g.target.Inits, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
haveEmbed := g.haveEmbed
|
saveHaveEmbed := g.haveEmbed
|
||||||
|
saveCurDecl := g.curDecl
|
||||||
|
g.curDecl = ""
|
||||||
g.later(func() {
|
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() {
|
if fn.Type().HasTParam() {
|
||||||
g.topFuncIsGeneric = true
|
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) {
|
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
|
// 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.
|
// during types2-to-types1 translation if this is a generic type.
|
||||||
|
saveCurDecl := g.curDecl
|
||||||
g.curDecl = decl.Name.Value
|
g.curDecl = decl.Name.Value
|
||||||
if decl.Alias {
|
if decl.Alias {
|
||||||
name, _ := g.def(decl.Name)
|
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
|
assert(name.Alias()) // should be set by irgen.obj
|
||||||
|
|
||||||
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
|
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
|
||||||
|
g.curDecl = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,6 +234,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
|
||||||
}
|
}
|
||||||
types.ResumeCheckSize()
|
types.ResumeCheckSize()
|
||||||
|
|
||||||
|
g.curDecl = saveCurDecl
|
||||||
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
|
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
|
||||||
methods := make([]*types.Field, otyp.NumMethods())
|
methods := make([]*types.Field, otyp.NumMethods())
|
||||||
for i := range methods {
|
for i := range methods {
|
||||||
|
|
@ -229,6 +245,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
|
||||||
meth := g.obj(m)
|
meth := g.obj(m)
|
||||||
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
|
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
|
||||||
methods[i].Nname = meth
|
methods[i].Nname = meth
|
||||||
|
g.curDecl = ""
|
||||||
}
|
}
|
||||||
ntyp.Methods().Set(methods)
|
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) {
|
func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
|
||||||
pos := g.pos(decl)
|
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))
|
names := make([]*ir.Name, len(decl.NameList))
|
||||||
for i, name := range decl.NameList {
|
for i, name := range decl.NameList {
|
||||||
names[i], _ = g.def(name)
|
names[i], _ = g.def(name)
|
||||||
|
|
@ -276,6 +295,7 @@ func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
|
||||||
} else if ir.CurFunc == nil {
|
} else if ir.CurFunc == nil {
|
||||||
name.Defn = as
|
name.Defn = as
|
||||||
}
|
}
|
||||||
|
if !g.delayTransform() {
|
||||||
lhs := []ir.Node{as.X}
|
lhs := []ir.Node{as.X}
|
||||||
rhs := []ir.Node{}
|
rhs := []ir.Node{}
|
||||||
if as.Y != nil {
|
if as.Y != nil {
|
||||||
|
|
@ -286,12 +306,15 @@ func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
|
||||||
if as.Y != nil {
|
if as.Y != nil {
|
||||||
as.Y = rhs[0]
|
as.Y = rhs[0]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
as.SetTypecheck(1)
|
as.SetTypecheck(1)
|
||||||
out.Append(as)
|
out.Append(as)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if as2 != nil {
|
if as2 != nil {
|
||||||
|
if !g.delayTransform() {
|
||||||
transformAssign(as2, as2.Lhs, as2.Rhs)
|
transformAssign(as2, as2.Lhs, as2.Rhs)
|
||||||
|
}
|
||||||
as2.SetTypecheck(1)
|
as2.SetTypecheck(1)
|
||||||
out.Append(as2)
|
out.Append(as2)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,7 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
|
||||||
if wantPtr {
|
if wantPtr {
|
||||||
recvType2Base = types2.AsPointer(recvType2).Elem()
|
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
|
// recvType2 is the original generic type that is
|
||||||
// instantiated for this method call.
|
// instantiated for this method call.
|
||||||
// selinfo.Recv() is the instantiated type
|
// 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 {
|
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 := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
|
||||||
n.SetOp(ir.OPTRLIT)
|
n.SetOp(ir.OPTRLIT)
|
||||||
return typed(g.typ(typ), n)
|
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))
|
exprs := make([]ir.Node, len(lit.ElemList))
|
||||||
for i, elem := range 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
|
// dictInfo is the dictionary format for an instantiation of a generic function with
|
||||||
// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
|
// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
|
||||||
// the actual dictionary entries in order, and the remaining fields are other info
|
// 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
|
// Nodes in the instantiation that requires a subdictionary. Includes
|
||||||
// method and function calls (OCALL), function values (OFUNCINST), method
|
// method and function calls (OCALL), function values (OFUNCINST), method
|
||||||
// values/expressions (OXDOT).
|
// values/expressions (OXDOT).
|
||||||
subDictCalls []ir.Node
|
subDictCalls []subDictInfo
|
||||||
// Nodes in the instantiation that are a conversion from a typeparam/derived
|
// Nodes in the instantiation that are a conversion from a typeparam/derived
|
||||||
// type to a specific interface.
|
// type to a specific interface.
|
||||||
itabConvs []ir.Node
|
itabConvs []ir.Node
|
||||||
|
|
@ -317,7 +328,7 @@ Outer:
|
||||||
|
|
||||||
// Create any needed instantiations of generic functions and transform
|
// Create any needed instantiations of generic functions and transform
|
||||||
// existing and new functions to use those instantiations.
|
// existing and new functions to use those instantiations.
|
||||||
BuildInstantiations(true)
|
BuildInstantiations()
|
||||||
|
|
||||||
// Remove all generic functions from g.target.Decl, since they have been
|
// Remove all generic functions from g.target.Decl, since they have been
|
||||||
// used for stenciling, but don't compile. Generic functions will already
|
// 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
|
// Quirk: If deadcode elimination turned a non-empty function into
|
||||||
// an empty one, we need to set the position for the empty block
|
// 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
|
// an empty string gets added into the DWARF file name listing at
|
||||||
// the appropriate index.
|
// the appropriate index.
|
||||||
if quirksMode() && len(body) == 1 {
|
if quirksMode() && len(body) == 1 {
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,7 @@ func (r *reader2) doTyp() (res types2.Type) {
|
||||||
case typePointer:
|
case typePointer:
|
||||||
return types2.NewPointer(r.typ())
|
return types2.NewPointer(r.typ())
|
||||||
case typeSignature:
|
case typeSignature:
|
||||||
return r.signature(nil)
|
return r.signature(nil, nil, nil)
|
||||||
case typeSlice:
|
case typeSlice:
|
||||||
return types2.NewSlice(r.typ())
|
return types2.NewSlice(r.typ())
|
||||||
case typeStruct:
|
case typeStruct:
|
||||||
|
|
@ -298,7 +298,7 @@ func (r *reader2) interfaceType() *types2.Interface {
|
||||||
for i := range methods {
|
for i := range methods {
|
||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
pkg, name := r.selector()
|
pkg, name := r.selector()
|
||||||
mtyp := r.signature(nil)
|
mtyp := r.signature(nil, nil, nil)
|
||||||
methods[i] = types2.NewFunc(pos, pkg, name, mtyp)
|
methods[i] = types2.NewFunc(pos, pkg, name, mtyp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,14 +309,14 @@ func (r *reader2) interfaceType() *types2.Interface {
|
||||||
return types2.NewInterfaceType(methods, embeddeds)
|
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)
|
r.sync(syncSignature)
|
||||||
|
|
||||||
params := r.params()
|
params := r.params()
|
||||||
results := r.params()
|
results := r.params()
|
||||||
variadic := r.bool()
|
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 {
|
func (r *reader2) params() *types2.Tuple {
|
||||||
|
|
@ -393,8 +393,7 @@ func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) {
|
||||||
case objFunc:
|
case objFunc:
|
||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
tparams := r.typeParamNames()
|
tparams := r.typeParamNames()
|
||||||
sig := r.signature(nil)
|
sig := r.signature(nil, nil, tparams)
|
||||||
sig.SetTypeParams(tparams)
|
|
||||||
return types2.NewFunc(pos, objPkg, objName, sig)
|
return types2.NewFunc(pos, objPkg, objName, sig)
|
||||||
|
|
||||||
case objType:
|
case objType:
|
||||||
|
|
@ -490,9 +489,8 @@ func (r *reader2) method() *types2.Func {
|
||||||
pos := r.pos()
|
pos := r.pos()
|
||||||
pkg, name := r.selector()
|
pkg, name := r.selector()
|
||||||
|
|
||||||
rparams := r.typeParamNames()
|
rtparams := r.typeParamNames()
|
||||||
sig := r.signature(r.param())
|
sig := r.signature(r.param(), rtparams, nil)
|
||||||
sig.SetRecvTypeParams(rparams)
|
|
||||||
|
|
||||||
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
|
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
|
||||||
return types2.NewFunc(pos, pkg, name, sig)
|
return types2.NewFunc(pos, pkg, name, sig)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package noder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/base"
|
"cmd/compile/internal/base"
|
||||||
"cmd/compile/internal/inline"
|
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/objw"
|
"cmd/compile/internal/objw"
|
||||||
"cmd/compile/internal/reflectdata"
|
"cmd/compile/internal/reflectdata"
|
||||||
|
|
@ -22,7 +21,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enable extra consistency checks.
|
// Enable extra consistency checks.
|
||||||
const doubleCheck = true
|
const doubleCheck = false
|
||||||
|
|
||||||
func assert(p bool) {
|
func assert(p bool) {
|
||||||
base.Assert(p)
|
base.Assert(p)
|
||||||
|
|
@ -40,34 +39,29 @@ func infoPrint(format string, a ...interface{}) {
|
||||||
|
|
||||||
var geninst genInst
|
var geninst genInst
|
||||||
|
|
||||||
func BuildInstantiations(preinliningMainScan bool) {
|
func BuildInstantiations() {
|
||||||
if geninst.instInfoMap == nil {
|
|
||||||
geninst.instInfoMap = make(map[*types.Sym]*instInfo)
|
geninst.instInfoMap = make(map[*types.Sym]*instInfo)
|
||||||
}
|
geninst.buildInstantiations()
|
||||||
geninst.buildInstantiations(preinliningMainScan)
|
geninst.instInfoMap = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildInstantiations scans functions for generic function calls and methods, and
|
// buildInstantiations scans functions for generic function calls and methods, and
|
||||||
// creates the required instantiations. It also creates instantiated methods for all
|
// creates the required instantiations. It also creates instantiated methods for all
|
||||||
// fully-instantiated generic types that have been encountered already or new ones
|
// fully-instantiated generic types that have been encountered already or new ones
|
||||||
// that are encountered during the instantiation process. If preinliningMainScan is
|
// that are encountered during the instantiation process. It scans all declarations
|
||||||
// true, it scans all declarations in typecheck.Target.Decls first, before scanning
|
// in typecheck.Target.Decls first, before scanning any new instantiations created.
|
||||||
// any new instantiations created. If preinliningMainScan is false, we do not scan
|
func (g *genInst) buildInstantiations() {
|
||||||
// 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) {
|
|
||||||
// Instantiate the methods of instantiated generic types that we have seen so far.
|
// Instantiate the methods of instantiated generic types that we have seen so far.
|
||||||
g.instantiateMethods()
|
g.instantiateMethods()
|
||||||
|
|
||||||
if preinliningMainScan {
|
// Scan all currentdecls for call to generic functions/methods.
|
||||||
n := len(typecheck.Target.Decls)
|
n := len(typecheck.Target.Decls)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
g.scanForGenCalls(typecheck.Target.Decls[i])
|
g.scanForGenCalls(typecheck.Target.Decls[i])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Scan all new instantiations created due to g.instantiateMethods() and the
|
// 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.
|
// instantiations are created.
|
||||||
for i := 0; i < len(g.newInsts); i++ {
|
for i := 0; i < len(g.newInsts); i++ {
|
||||||
g.scanForGenCalls(g.newInsts[i])
|
g.scanForGenCalls(g.newInsts[i])
|
||||||
|
|
@ -82,10 +76,6 @@ func (g *genInst) buildInstantiations(preinliningMainScan bool) {
|
||||||
for _, fun := range g.newInsts {
|
for _, fun := range g.newInsts {
|
||||||
info := g.instInfoMap[fun.Sym()]
|
info := g.instInfoMap[fun.Sym()]
|
||||||
g.dictPass(info)
|
g.dictPass(info)
|
||||||
if !preinliningMainScan {
|
|
||||||
// Prepare for the round of inlining below.
|
|
||||||
inline.CanInline(fun.(*ir.Func))
|
|
||||||
}
|
|
||||||
if doubleCheck {
|
if doubleCheck {
|
||||||
ir.Visit(info.fun, func(n ir.Node) {
|
ir.Visit(info.fun, func(n ir.Node) {
|
||||||
if n.Op() != ir.OCONVIFACE {
|
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)
|
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))
|
assert(l == len(g.newInsts))
|
||||||
g.newInsts = nil
|
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
|
// explicitly traverse any embedded fields in the receiver
|
||||||
// argument in order to call the method instantiation.
|
// argument in order to call the method instantiation.
|
||||||
arg0 := formalParams[0].Nname.(ir.Node)
|
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() {
|
if valueMethod && arg0.Type().IsPtr() {
|
||||||
// For handling the (*T).M case: if we have a pointer
|
// For handling the (*T).M case: if we have a pointer
|
||||||
// receiver after following all the embedded fields,
|
// 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.
|
// Build call itself.
|
||||||
var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
|
var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
|
||||||
|
innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic()
|
||||||
if len(formalResults) > 0 {
|
if len(formalResults) > 0 {
|
||||||
innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
|
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 {
|
if declInfo != nil {
|
||||||
entry := -1
|
entry := -1
|
||||||
for i, de := range declInfo.dictInfo.subDictCalls {
|
for i, de := range declInfo.dictInfo.subDictCalls {
|
||||||
if n == de {
|
if n == de.callNode {
|
||||||
entry = declInfo.dictInfo.startSubDict + i
|
entry = declInfo.dictInfo.startSubDict + i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -615,7 +598,7 @@ func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.N
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !usingSubdict {
|
if !usingSubdict {
|
||||||
dict = g.getDictionaryValue(nameNode, targs, isMeth)
|
dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth)
|
||||||
}
|
}
|
||||||
return dict, usingSubdict
|
return dict, usingSubdict
|
||||||
}
|
}
|
||||||
|
|
@ -651,17 +634,38 @@ func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMe
|
||||||
checkFetchBody(nameNode)
|
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
|
// 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
|
// number of instantiations we have to generate. You can actually have a mix
|
||||||
// of shape and non-shape arguments, because of inferred or explicitly
|
// of shape and non-shape arguments, because of inferred or explicitly
|
||||||
// specified concrete type args.
|
// specified concrete type args.
|
||||||
s1 := make([]*types.Type, len(shapes))
|
s1 := make([]*types.Type, len(shapes))
|
||||||
for i, t := range 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() {
|
if !t.IsShape() {
|
||||||
s1[i] = typecheck.Shapify(t, i)
|
s1[i] = typecheck.Shapify(t, i, tparam)
|
||||||
} else {
|
} else {
|
||||||
// Already a shape, but make sure it has the correct index.
|
// 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
|
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)
|
info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)
|
||||||
|
|
||||||
// genericSubst fills in info.dictParam and info.tparamToBound.
|
if sym.Def != nil {
|
||||||
st := g.genericSubst(sym, nameNode, shapes, isMeth, info)
|
// 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
|
info.fun = st
|
||||||
g.instInfoMap[sym] = info
|
g.instInfoMap[sym] = info
|
||||||
|
|
||||||
|
|
@ -714,22 +733,8 @@ type subster struct {
|
||||||
// args shapes. For a method with a generic receiver, it returns an instantiated
|
// 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
|
// 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
|
// method or function, a dictionary parameter is the added as the very first
|
||||||
// parameter. genericSubst fills in info.dictParam and info.tparamToBound.
|
// parameter. genericSubst fills in info.dictParam and info.shapeToBound.
|
||||||
func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
|
func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gf := nameNode.Func
|
gf := nameNode.Func
|
||||||
// Pos of the instantiated function is same as the generic function
|
// Pos of the instantiated function is same as the generic function
|
||||||
newf := ir.NewFunc(gf.Pos())
|
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.
|
// Make sure name/type of newf is set before substituting the body.
|
||||||
newf.Body = subst.list(gf.Body)
|
newf.Body = subst.list(gf.Body)
|
||||||
|
if len(newf.Body) == 0 {
|
||||||
// Add code to check that the dictionary is correct.
|
// Ensure the body is nonempty, for issue 49524.
|
||||||
// TODO: must be adjusted to deal with shapes, but will go away soon when we move
|
// TODO: have some other way to detect the difference between
|
||||||
// to many->1 shape to concrete mapping.
|
// a function declared with no body, vs. one with an empty body?
|
||||||
// newf.Body.Prepend(subst.checkDictionary(dictionaryName, shapes)...)
|
newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil))
|
||||||
|
}
|
||||||
|
|
||||||
if len(subst.defnMap) > 0 {
|
if len(subst.defnMap) > 0 {
|
||||||
base.Fatalf("defnMap is not empty")
|
base.Fatalf("defnMap is not empty")
|
||||||
|
|
@ -859,49 +865,6 @@ func (subst *subster) localvar(name *ir.Name) *ir.Name {
|
||||||
return m
|
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.
|
// getDictionaryEntry gets the i'th entry in the dictionary dict.
|
||||||
func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
|
func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
|
||||||
// Convert dictionary to *[N]uintptr
|
// 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.
|
// Use closure to capture all state needed by the ir.EditChildren argument.
|
||||||
var edit func(ir.Node) ir.Node
|
var edit func(ir.Node) ir.Node
|
||||||
edit = func(x 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() {
|
switch x.Op() {
|
||||||
case ir.OTYPE:
|
case ir.OTYPE:
|
||||||
return ir.TypeNode(subst.ts.Typ(x.Type()))
|
return ir.TypeNode(subst.ts.Typ(x.Type()))
|
||||||
|
|
@ -1069,13 +1036,13 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
case ir.OXDOT:
|
case ir.OXDOT:
|
||||||
// Finish the transformation of an OXDOT, unless this was a
|
// Finish the transformation of an OXDOT, unless this is
|
||||||
// bound call (a direct call on a type param). A bound call
|
// bound call or field access on a type param. A bound call
|
||||||
// will be transformed during the dictPass. Otherwise, m
|
// or field access on a type param will be transformed during
|
||||||
// will be transformed to an OMETHVALUE node. It will be
|
// the dictPass. Otherwise, m will be transformed to an
|
||||||
// transformed to an ODOTMETH or ODOTINTER node if we find in
|
// OMETHVALUE node. It will be transformed to an ODOTMETH or
|
||||||
// the OCALL case below that the method value is actually
|
// ODOTINTER node if we find in the OCALL case below that the
|
||||||
// called.
|
// method value is actually called.
|
||||||
mse := m.(*ir.SelectorExpr)
|
mse := m.(*ir.SelectorExpr)
|
||||||
if src := mse.X.Type(); !src.IsShape() {
|
if src := mse.X.Type(); !src.IsShape() {
|
||||||
transformDot(mse, false)
|
transformDot(mse, false)
|
||||||
|
|
@ -1134,8 +1101,12 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
transformEarlyCall(call)
|
transformEarlyCall(call)
|
||||||
|
|
||||||
case ir.OXDOT:
|
case ir.OXDOT:
|
||||||
// This is the case of a bound call on a typeparam,
|
// This is the case of a bound call or a field access
|
||||||
// which will be handled in the dictPass.
|
// 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:
|
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
||||||
// These are DOTTYPEs that could get transformed into
|
// These are DOTTYPEs that could get transformed into
|
||||||
|
|
@ -1245,39 +1216,53 @@ func (g *genInst) dictPass(info *instInfo) {
|
||||||
ir.CurFunc = info.fun
|
ir.CurFunc = info.fun
|
||||||
|
|
||||||
case ir.OXDOT:
|
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)
|
mse := m.(*ir.SelectorExpr)
|
||||||
src := mse.X.Type()
|
src := mse.X.Type()
|
||||||
assert(src.IsShape())
|
assert(src.IsShape())
|
||||||
|
|
||||||
// The only dot on a shape type value are methods.
|
|
||||||
if mse.X.Op() == ir.OTYPE {
|
if mse.X.Op() == ir.OTYPE {
|
||||||
// Method expression T.M
|
// Method expression T.M
|
||||||
m = g.buildClosure2(info, m)
|
m = g.buildClosure2(info, m)
|
||||||
// No need for transformDot - buildClosure2 has already
|
// No need for transformDot - buildClosure2 has already
|
||||||
// transformed to OCALLINTER/ODOTINTER.
|
// transformed to OCALLINTER/ODOTINTER.
|
||||||
} else {
|
} else {
|
||||||
|
// 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
|
// Implement x.M as a conversion-to-bound-interface
|
||||||
// 1) convert x to the bound interface
|
// 1) convert x to the bound interface
|
||||||
// 2) call M on that interface
|
// 2) call M on that interface
|
||||||
dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
|
|
||||||
if src.IsInterface() {
|
if src.IsInterface() {
|
||||||
// If type arg is an interface (unusual case),
|
// If type arg is an interface (unusual case),
|
||||||
// we do a type assert to the type bound.
|
// we do a type assert to the type bound.
|
||||||
mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
|
mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
|
||||||
} else {
|
} else {
|
||||||
mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst)
|
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)
|
transformDot(mse, false)
|
||||||
}
|
}
|
||||||
case ir.OCALL:
|
case ir.OCALL:
|
||||||
op := m.(*ir.CallExpr).X.Op()
|
call := m.(*ir.CallExpr)
|
||||||
|
op := call.X.Op()
|
||||||
if op == ir.OMETHVALUE {
|
if op == ir.OMETHVALUE {
|
||||||
// Redo the transformation of OXDOT, now that we
|
// Redo the transformation of OXDOT, now that we
|
||||||
// know the method value is being called.
|
// know the method value is being called.
|
||||||
m.(*ir.CallExpr).X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
||||||
transformDot(m.(*ir.CallExpr).X.(*ir.SelectorExpr), true)
|
transformDot(call.X.(*ir.SelectorExpr), true)
|
||||||
}
|
}
|
||||||
transformCall(m.(*ir.CallExpr))
|
transformCall(call)
|
||||||
|
|
||||||
case ir.OCONVIFACE:
|
case ir.OCONVIFACE:
|
||||||
if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
|
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.
|
// Note: x's argument is still typed as a type parameter.
|
||||||
// m's argument now has an instantiated type.
|
// m's argument now has an instantiated type.
|
||||||
if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
|
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:
|
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
||||||
if !m.Type().HasShape() {
|
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
|
// 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
|
// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the
|
||||||
// conversion.
|
// 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(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
|
||||||
assert(dst.IsInterface())
|
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.
|
// Figure out what the data field of the interface will be.
|
||||||
data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
|
data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
|
||||||
typed(types.Types[types.TUNSAFEPTR], data)
|
typed(types.Types[types.TUNSAFEPTR], data)
|
||||||
|
data.NonEscaping = nonEscaping
|
||||||
|
|
||||||
// Build an interface from the type and data parts.
|
// Build an interface from the type and data parts.
|
||||||
var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
|
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)
|
markTypeUsed(ts, lsym)
|
||||||
}
|
}
|
||||||
// Emit an entry for each subdictionary (after substituting targs)
|
// Emit an entry for each subdictionary (after substituting targs)
|
||||||
for _, n := range info.subDictCalls {
|
for _, subDictInfo := range info.subDictCalls {
|
||||||
var sym *types.Sym
|
var sym *types.Sym
|
||||||
|
n := subDictInfo.callNode
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
|
case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
|
||||||
call := n.(*ir.CallExpr)
|
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() {
|
if se.X.Type().IsShape() {
|
||||||
// This is a method call enabled by a type bound.
|
// This is a method call enabled by a type bound.
|
||||||
|
|
||||||
// We need this extra check for type expressions, which
|
// We need this extra check for method expressions,
|
||||||
// don't add in the implicit XDOTs.
|
// which don't add in the implicit XDOTs.
|
||||||
tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel)
|
tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
|
||||||
tmpse = typecheck.AddImplicitDots(tmpse)
|
tmpse = typecheck.AddImplicitDots(tmpse)
|
||||||
tparam := tmpse.X.Type()
|
tparam := tmpse.X.Type()
|
||||||
if !tparam.IsShape() {
|
if !tparam.IsShape() {
|
||||||
|
|
@ -1629,31 +1618,31 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
||||||
} else {
|
} else {
|
||||||
// This is the case of a normal
|
// This is the case of a normal
|
||||||
// method call on a generic type.
|
// method call on a generic type.
|
||||||
recvType := deref(call.X.(*ir.SelectorExpr).X.Type())
|
assert(subDictInfo.savedXNode == se)
|
||||||
genRecvType := recvType.OrigSym().Def.Type()
|
sym = g.getSymForMethodCall(se, &subst)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inst := call.X.(*ir.InstExpr)
|
inst, ok := call.X.(*ir.InstExpr)
|
||||||
var nameNode *ir.Name
|
if ok {
|
||||||
var meth *ir.SelectorExpr
|
// Code hasn't been transformed yet
|
||||||
var isMeth bool
|
assert(subDictInfo.savedXNode == inst)
|
||||||
if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth {
|
|
||||||
nameNode = meth.Selection.Nname.(*ir.Name)
|
|
||||||
} else {
|
|
||||||
nameNode = inst.X.(*ir.Name)
|
|
||||||
}
|
}
|
||||||
|
// 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 {
|
||||||
|
inst := cex.(*ir.InstExpr)
|
||||||
|
nameNode := inst.X.(*ir.Name)
|
||||||
subtargs := typecheck.TypesOf(inst.Targs)
|
subtargs := typecheck.TypesOf(inst.Targs)
|
||||||
for i, t := range subtargs {
|
for i, t := range subtargs {
|
||||||
subtargs[i] = subst.Typ(t)
|
subtargs[i] = subst.Typ(t)
|
||||||
}
|
}
|
||||||
sym = g.getDictionarySym(nameNode, subtargs, isMeth)
|
sym = g.getDictionarySym(nameNode, subtargs, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case ir.OFUNCINST:
|
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)
|
sym = g.getDictionarySym(nameNode, subtargs, false)
|
||||||
|
|
||||||
case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
|
case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
|
||||||
selExpr := n.(*ir.SelectorExpr)
|
sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst)
|
||||||
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)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(false)
|
assert(false)
|
||||||
|
|
@ -1703,6 +1683,24 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
||||||
return sym
|
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
|
// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
|
||||||
// any needed LSyms for itabs. The itab lsyms create wrappers which need various
|
// any needed LSyms for itabs. The itab lsyms create wrappers which need various
|
||||||
// dictionaries and method instantiations to be complete, so, to avoid recursive
|
// dictionaries and method instantiations to be complete, so, to avoid recursive
|
||||||
|
|
@ -1762,7 +1760,7 @@ func (g *genInst) finalizeSyms() {
|
||||||
g.dictSymsToFinalize = nil
|
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)
|
sym := g.getDictionarySym(gf, targs, isMeth)
|
||||||
|
|
||||||
// Make (or reuse) a node referencing the dictionary symbol.
|
// 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 {
|
if sym.Def != nil {
|
||||||
n = sym.Def.(*ir.Name)
|
n = sym.Def.(*ir.Name)
|
||||||
} else {
|
} 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.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
|
||||||
n.SetTypecheck(1)
|
n.SetTypecheck(1)
|
||||||
n.Class = ir.PEXTERN
|
n.Class = ir.PEXTERN
|
||||||
sym.Def = n
|
sym.Def = n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the address of the dictionary.
|
// Return the address of the dictionary. Addr node gets position that was passed in.
|
||||||
np := typecheck.NodAddr(n)
|
np := typecheck.NodAddrAt(pos, n)
|
||||||
// Note: treat dictionary pointers as uintptrs, so they aren't pointers
|
// Note: treat dictionary pointers as uintptrs, so they aren't pointers
|
||||||
// with respect to GC. That saves on stack scanning work, write barriers, etc.
|
// with respect to GC. That saves on stack scanning work, write barriers, etc.
|
||||||
// We can get away with it because dictionaries are global variables.
|
// 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:
|
case ir.OFUNCINST:
|
||||||
if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
|
if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
|
||||||
infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
|
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:
|
case ir.OMETHEXPR, ir.OMETHVALUE:
|
||||||
if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
|
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 {
|
} else {
|
||||||
infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
|
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:
|
case ir.OCALL:
|
||||||
ce := n.(*ir.CallExpr)
|
ce := n.(*ir.CallExpr)
|
||||||
|
|
@ -1866,14 +1867,22 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
||||||
callMap[ce.X] = true
|
callMap[ce.X] = true
|
||||||
if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
|
if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
|
||||||
infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
|
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 &&
|
// Note: this XDOT code is not actually needed as long as we
|
||||||
isShapeDeref(ce.X.(*ir.SelectorExpr).X.Type()) {
|
// continue to disable type parameters on RHS of type
|
||||||
|
// declarations (#45639).
|
||||||
|
if ce.X.Op() == ir.OXDOT {
|
||||||
callMap[ce.X] = true
|
callMap[ce.X] = true
|
||||||
|
if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) {
|
||||||
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
|
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
|
||||||
info.subDictCalls = append(info.subDictCalls, n)
|
info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case ir.OCALLMETH:
|
case ir.OCALLMETH:
|
||||||
ce := n.(*ir.CallExpr)
|
ce := n.(*ir.CallExpr)
|
||||||
|
|
@ -1882,7 +1891,11 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
||||||
callMap[ce.X] = true
|
callMap[ce.X] = true
|
||||||
if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
|
if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
|
||||||
infoPrint(" Subdictionary at generic method call: %v\n", n)
|
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:
|
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)
|
info.itabConvs = append(info.itabConvs, n)
|
||||||
}
|
}
|
||||||
case ir.OXDOT:
|
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)
|
infoPrint(" Itab for bound call: %v\n", n)
|
||||||
info.itabConvs = append(info.itabConvs, 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)
|
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
|
// isBoundMethod returns true if the selection indicated by se is a bound method of
|
||||||
// can't just use deref(t).IsShape(), since a shape type is a complex type and may
|
// se.X. se.X must be a shape type (i.e. substituted directly from a type param). If
|
||||||
// have a pointer as part of its shape.)
|
// isBoundMethod returns false, then the selection must be a field access of a
|
||||||
func isShapeDeref(t *types.Type) bool {
|
// structural type.
|
||||||
return t.IsShape() || t.IsPtr() && t.Elem().IsShape()
|
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
|
// 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)
|
fn.Dcl = append(fn.Dcl, arg)
|
||||||
f := types.NewField(pos, arg.Sym(), t)
|
f := types.NewField(pos, arg.Sym(), t)
|
||||||
f.Nname = arg
|
f.Nname = arg
|
||||||
|
f.SetIsDDD(typ.Params().Field(i).IsDDD())
|
||||||
formalParams = append(formalParams, f)
|
formalParams = append(formalParams, f)
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.NumResults(); i++ {
|
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.
|
// the type bound.
|
||||||
rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
|
rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
|
||||||
} else {
|
} 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 := 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)
|
dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@ import (
|
||||||
"cmd/internal/src"
|
"cmd/internal/src"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// stmts creates nodes for a slice of statements that form a scope.
|
||||||
func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
|
func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
|
||||||
var nodes []ir.Node
|
var nodes []ir.Node
|
||||||
|
types.Markdcl()
|
||||||
for _, stmt := range stmts {
|
for _, stmt := range stmts {
|
||||||
switch s := g.stmt(stmt).(type) {
|
switch s := g.stmt(stmt).(type) {
|
||||||
case nil: // EmptyStmt
|
case nil: // EmptyStmt
|
||||||
|
|
@ -24,6 +26,7 @@ func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
|
||||||
nodes = append(nodes, s)
|
nodes = append(nodes, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
types.Popdcl()
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +49,12 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
|
||||||
n.SetTypecheck(1)
|
n.SetTypecheck(1)
|
||||||
return n
|
return n
|
||||||
case *syntax.DeclStmt:
|
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)
|
n := ir.NewBlockStmt(g.pos(stmt), nil)
|
||||||
g.decls(&n.List, stmt.DeclList)
|
g.decls(&n.List, stmt.DeclList)
|
||||||
return n
|
return n
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,31 @@ func transformConv(n *ir.ConvExpr) ir.Node {
|
||||||
if n.X.Op() == ir.OLITERAL {
|
if n.X.Op() == ir.OLITERAL {
|
||||||
return stringtoruneslit(n)
|
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
|
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
|
// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even
|
||||||
// in the case of OCALL/OFUNCINST.
|
// in the case of OCALL/OFUNCINST.
|
||||||
func transformCall(n *ir.CallExpr) {
|
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
|
// n.Type() can be nil for calls with no return value
|
||||||
assert(n.Typecheck() == 1)
|
assert(n.Typecheck() == 1)
|
||||||
transformArgs(n)
|
transformArgs(n)
|
||||||
|
|
@ -328,6 +356,37 @@ assignOK:
|
||||||
}
|
}
|
||||||
checkLHS(0, r.Type())
|
checkLHS(0, r.Type())
|
||||||
checkLHS(1, types.UntypedBool)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
|
||||||
return types.NewPkg(pkg.Path(), pkg.Name())
|
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
|
// typ converts a types2.Type to a types.Type, including caching of previously
|
||||||
// translated types.
|
// translated types.
|
||||||
func (g *irgen) typ(typ types2.Type) *types.Type {
|
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
|
// constructed part of a recursive type. Should not be called from outside this
|
||||||
// file (g.typ is the "external" entry point).
|
// file (g.typ is the "external" entry point).
|
||||||
func (g *irgen) typ1(typ types2.Type) *types.Type {
|
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
|
// Cache type2-to-type mappings. Important so that each defined generic
|
||||||
// type (instantiated or not) has a single types.Type representation.
|
// type (instantiated or not) has a single types.Type representation.
|
||||||
// Also saves a lot of computation and memory by avoiding re-translating
|
// 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.
|
// based on the names of the type arguments.
|
||||||
instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
|
instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
|
||||||
s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
|
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 {
|
if s.Def != nil {
|
||||||
// We have already encountered this instantiation.
|
// We have already encountered this instantiation.
|
||||||
// Use the type we previously created, since there
|
// 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()
|
return s.Def.Type()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the base generic type exists in type1 (it may
|
if base.Class == ir.PAUTO {
|
||||||
// not yet if we are referecing an imported generic type, as
|
// If the base type is a local type, we want to pop
|
||||||
// opposed to a generic type declared in this package).
|
// this instantiated type symbol/definition when we
|
||||||
_ = g.obj(typ.Origin().Obj())
|
// 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
|
// Create a forwarding type first and put it in the g.typs
|
||||||
// map, in order to deal with recursive generic types
|
// 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.
|
// Save the name of the type parameter in the sym of the type.
|
||||||
// Include the types2 subscript in the sym name
|
// Include the types2 subscript in the sym name
|
||||||
pkg := g.tpkg(typ)
|
pkg := g.tpkg(typ)
|
||||||
// Create the unique types1 name for a type param, using its context with a
|
// Create the unique types1 name for a type param, using its context
|
||||||
// function, type, or method declaration.
|
// with a function, type, or method declaration. Also, map blank type
|
||||||
nm := g.curDecl + "." + typ.Obj().Name()
|
// 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)
|
sym := pkg.Lookup(nm)
|
||||||
if sym.Def != nil {
|
if sym.Def != nil {
|
||||||
// Make sure we use the same type param type for the same
|
// 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())
|
tparams := make([]*types.Type, rparams.Len())
|
||||||
// Set g.curDecl to be the method context, so type
|
// Set g.curDecl to be the method context, so type
|
||||||
// params in the receiver of the method that we are
|
// 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()
|
g.curDecl = typ.Obj().Name() + "." + m.Name()
|
||||||
for i := range tparams {
|
for i := range tparams {
|
||||||
tparams[i] = g.typ1(rparams.At(i))
|
tparams[i] = g.typ1(rparams.At(i))
|
||||||
}
|
}
|
||||||
|
g.curDecl = savedCurDecl
|
||||||
assert(len(tparams) == len(targs))
|
assert(len(tparams) == len(targs))
|
||||||
ts := typecheck.Tsubster{
|
ts := typecheck.Tsubster{
|
||||||
Tparams: tparams,
|
Tparams: tparams,
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,8 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) int {
|
||||||
|
|
||||||
// @@@ Types
|
// @@@ Types
|
||||||
|
|
||||||
|
var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)
|
||||||
|
|
||||||
func (w *writer) typ(typ types2.Type) {
|
func (w *writer) typ(typ types2.Type) {
|
||||||
w.typInfo(w.p.typIdx(typ, w.dict))
|
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)
|
w.structType(typ)
|
||||||
|
|
||||||
case *types2.Interface:
|
case *types2.Interface:
|
||||||
|
if typ == anyTypeName.Type() {
|
||||||
|
w.code(typeNamed)
|
||||||
|
w.obj(anyTypeName, nil)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
w.code(typeInterface)
|
w.code(typeInterface)
|
||||||
w.interfaceType(typ)
|
w.interfaceType(typ)
|
||||||
|
|
||||||
|
|
@ -1210,6 +1218,7 @@ func (w *writer) expr(expr syntax.Expr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := obj.(*types2.Var)
|
obj := obj.(*types2.Var)
|
||||||
|
assert(!obj.IsField())
|
||||||
assert(targs.Len() == 0)
|
assert(targs.Len() == 0)
|
||||||
|
|
||||||
w.code(exprLocal)
|
w.code(exprLocal)
|
||||||
|
|
@ -1329,10 +1338,10 @@ func (w *writer) compLit(lit *syntax.CompositeLit) {
|
||||||
w.typ(tv.Type)
|
w.typ(tv.Type)
|
||||||
|
|
||||||
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()
|
typ = ptr.Elem()
|
||||||
}
|
}
|
||||||
str, isStruct := typ.Underlying().(*types2.Struct)
|
str, isStruct := types2.StructuralType(typ).(*types2.Struct)
|
||||||
|
|
||||||
w.len(len(lit.ElemList))
|
w.len(len(lit.ElemList))
|
||||||
for i, elem := range lit.ElemList {
|
for i, elem := range lit.ElemList {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func Init(arch *ssagen.ArchInfo) {
|
||||||
arch.LinkArch = &ppc64.Linkppc64le
|
arch.LinkArch = &ppc64.Linkppc64le
|
||||||
}
|
}
|
||||||
arch.REGSP = ppc64.REGSP
|
arch.REGSP = ppc64.REGSP
|
||||||
arch.MAXWIDTH = 1 << 60
|
arch.MAXWIDTH = 1 << 50
|
||||||
|
|
||||||
arch.ZeroRange = zerorange
|
arch.ZeroRange = zerorange
|
||||||
arch.Ginsnop = ginsnop
|
arch.Ginsnop = ginsnop
|
||||||
|
|
|
||||||
|
|
@ -846,14 +846,19 @@ func TypePtr(t *types.Type) *ir.AddrExpr {
|
||||||
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
|
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ITabLsym returns the LSym representing the itab for concreate type typ
|
// ITabLsym returns the LSym representing the itab for concrete type typ implementing
|
||||||
// implementing interface iface.
|
// 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 {
|
func ITabLsym(typ, iface *types.Type) *obj.LSym {
|
||||||
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
|
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
|
||||||
lsym := s.Linksym()
|
lsym := s.Linksym()
|
||||||
|
|
||||||
if !existed {
|
if !existed {
|
||||||
writeITab(lsym, typ, iface)
|
writeITab(lsym, typ, iface, true)
|
||||||
}
|
}
|
||||||
return lsym
|
return lsym
|
||||||
}
|
}
|
||||||
|
|
@ -865,7 +870,7 @@ func ITabAddr(typ, iface *types.Type) *ir.AddrExpr {
|
||||||
lsym := s.Linksym()
|
lsym := s.Linksym()
|
||||||
|
|
||||||
if !existed {
|
if !existed {
|
||||||
writeITab(lsym, typ, iface)
|
writeITab(lsym, typ, iface, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
|
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
|
// They've been separate internally to make error messages
|
||||||
// better, but we have to merge them in the reflect tables.
|
// better, but we have to merge them in the reflect tables.
|
||||||
func formalType(t *types.Type) *types.Type {
|
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 types.Types[t.Kind()]
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
|
|
@ -1303,9 +1309,10 @@ func WriteRuntimeTypes() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeITab writes the itab for concrete type typ implementing
|
// writeITab writes the itab for concrete type typ implementing interface iface. If
|
||||||
// interface iface.
|
// allowNonImplement is true, allow the case where typ does not implement iface, and just
|
||||||
func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
|
// 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
|
// TODO(mdempsky): Fix methodWrapper, geneq, and genhash (and maybe
|
||||||
// others) to stop clobbering these.
|
// others) to stop clobbering these.
|
||||||
oldpos, oldfn := base.Pos, ir.CurFunc
|
oldpos, oldfn := base.Pos, ir.CurFunc
|
||||||
|
|
@ -1331,14 +1338,9 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sigs[0].Sym.Name == "==" {
|
|
||||||
sigs = sigs[1:]
|
|
||||||
if len(sigs) == 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
completeItab := len(sigs) == 0
|
||||||
}
|
if !allowNonImplement && !completeItab {
|
||||||
if len(sigs) != 0 {
|
|
||||||
base.Fatalf("incomplete itab")
|
base.Fatalf("incomplete itab")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1355,8 +1357,13 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
|
||||||
o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
|
o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
|
||||||
o += 4 // skip unused field
|
o += 4 // skip unused field
|
||||||
for _, fn := range entries {
|
for _, fn := range entries {
|
||||||
|
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
|
o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Nothing writes static itabs, so they are read only.
|
// Nothing writes static itabs, so they are read only.
|
||||||
objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
|
objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
|
||||||
lsym.Set(obj.AttrContentAddressable, true)
|
lsym.Set(obj.AttrContentAddressable, true)
|
||||||
|
|
@ -1417,6 +1424,9 @@ func WriteBasicTypes() {
|
||||||
}
|
}
|
||||||
writeType(types.NewPtr(types.Types[types.TSTRING]))
|
writeType(types.NewPtr(types.Types[types.TSTRING]))
|
||||||
writeType(types.NewPtr(types.Types[types.TUNSAFEPTR]))
|
writeType(types.NewPtr(types.Types[types.TUNSAFEPTR]))
|
||||||
|
if base.Flag.G > 0 {
|
||||||
|
writeType(types.AnyType)
|
||||||
|
}
|
||||||
|
|
||||||
// emit type structs for error and func(error) string.
|
// emit type structs for error and func(error) string.
|
||||||
// The latter is the type of an auto-generated wrapper.
|
// The latter is the type of an auto-generated wrapper.
|
||||||
|
|
@ -1938,19 +1948,26 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||||
|
|
||||||
// Target method uses shaped names.
|
// Target method uses shaped names.
|
||||||
targs2 := make([]*types.Type, len(targs))
|
targs2 := make([]*types.Type, len(targs))
|
||||||
|
origRParams := deref(orig).OrigSym().Def.(*ir.Name).Type().RParams()
|
||||||
for i, t := range targs {
|
for i, t := range targs {
|
||||||
targs2[i] = typecheck.Shapify(t, i)
|
targs2[i] = typecheck.Shapify(t, i, origRParams[i])
|
||||||
}
|
}
|
||||||
targs = targs2
|
targs = targs2
|
||||||
|
|
||||||
sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, false, true)
|
sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, false, true)
|
||||||
if sym.Def == nil {
|
if sym.Def == nil {
|
||||||
// Currently we make sure that we have all the instantiations
|
// Currently we make sure that we have all the
|
||||||
// we need by generating them all in ../noder/stencil.go:instantiateMethods
|
// instantiations we need by generating them all in
|
||||||
// TODO: maybe there's a better, more incremental way to generate
|
// ../noder/stencil.go:instantiateMethods
|
||||||
// only the instantiations we need?
|
// 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)
|
base.Fatalf("instantiation %s not found", sym.Name)
|
||||||
}
|
}
|
||||||
|
sym = in.Sym()
|
||||||
|
}
|
||||||
target := ir.AsNode(sym.Def)
|
target := ir.AsNode(sym.Def)
|
||||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
|
call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
|
||||||
// Fill-in the generic method node that was not filled in
|
// Fill-in the generic method node that was not filled in
|
||||||
|
|
@ -2075,9 +2092,15 @@ func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node {
|
||||||
sym := typecheck.MakeDictSym(gf, targs, true)
|
sym := typecheck.MakeDictSym(gf, targs, true)
|
||||||
|
|
||||||
// Dictionary should already have been generated by instantiateMethods().
|
// 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 {
|
if lsym := sym.Linksym(); len(lsym.P) == 0 {
|
||||||
|
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)
|
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.
|
// Make (or reuse) a node referencing the dictionary symbol.
|
||||||
var n *ir.Name
|
var n *ir.Name
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ type FuncDebug struct {
|
||||||
VarSlots [][]SlotID
|
VarSlots [][]SlotID
|
||||||
// The location list data, indexed by VarID. Must be processed by PutLocationList.
|
// The location list data, indexed by VarID. Must be processed by PutLocationList.
|
||||||
LocationLists [][]byte
|
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.
|
// Filled in by the user. Translates Block and Value ID to PC.
|
||||||
GetPC func(ID, ID) int64
|
GetPC func(ID, ID) int64
|
||||||
|
|
@ -548,10 +551,10 @@ func PopulateABIInRegArgOps(f *Func) {
|
||||||
f.Entry.Values = append(newValues, f.Entry.Values...)
|
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
|
// f must be fully processed, so that each Value is where it will be when
|
||||||
// machine code is emitted.
|
// 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 {
|
if f.RegAlloc == nil {
|
||||||
f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
|
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()
|
blockLocs := state.liveness()
|
||||||
state.buildLocationLists(blockLocs)
|
state.buildLocationLists(blockLocs)
|
||||||
|
|
||||||
return &FuncDebug{
|
// Populate "rval" with what we've computed.
|
||||||
Slots: state.slots,
|
rval.Slots = state.slots
|
||||||
VarSlots: state.varSlots,
|
rval.VarSlots = state.varSlots
|
||||||
Vars: state.vars,
|
rval.Vars = state.vars
|
||||||
LocationLists: state.lists,
|
rval.LocationLists = state.lists
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// liveness walks the function in control flow order, calculating the start
|
// liveness walks the function in control flow order, calculating the start
|
||||||
|
|
@ -1120,55 +1122,94 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
|
||||||
v.Op == OpArgIntReg || v.Op == OpArgFloatReg
|
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
|
zeroWidthPending := false
|
||||||
blockPrologComplete := false // set to true at first non-zero-width op
|
prologComplete := false
|
||||||
apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
|
|
||||||
// expect to see values in pattern (apc)* (zerowidth|real)*
|
// expect to see values in pattern (apc)* (zerowidth|real)*
|
||||||
for _, v := range b.Values {
|
for _, v := range b.Values {
|
||||||
|
if blockPrologComplete(v) {
|
||||||
|
prologComplete = true
|
||||||
|
}
|
||||||
slots := state.valueNames[v.ID]
|
slots := state.valueNames[v.ID]
|
||||||
reg, _ := state.f.getHome(v.ID).(*Register)
|
reg, _ := state.f.getHome(v.ID).(*Register)
|
||||||
changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
|
changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
|
||||||
|
|
||||||
if opcodeTable[v.Op].zeroWidth {
|
if opcodeTable[v.Op].zeroWidth {
|
||||||
if changed {
|
if prologComplete && mustBeFirst(v) {
|
||||||
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))
|
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())
|
if changed {
|
||||||
// Other zero-width ops must wait on a "real" op.
|
if mustBeFirst(v) || v.Op == OpArg {
|
||||||
|
// already taken care of above
|
||||||
|
continue
|
||||||
|
}
|
||||||
zeroWidthPending = true
|
zeroWidthPending = true
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !changed && !zeroWidthPending {
|
if !changed && !zeroWidthPending {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Not zero-width; i.e., a "real" instruction.
|
|
||||||
|
|
||||||
|
// Not zero-width; i.e., a "real" instruction.
|
||||||
zeroWidthPending = false
|
zeroWidthPending = false
|
||||||
blockPrologComplete = true
|
for _, varID := range state.changedVars.contents() {
|
||||||
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)
|
state.updateVar(VarID(varID), v.Block, v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
state.changedVars.clear()
|
state.changedVars.clear()
|
||||||
apcChangedSize = 0
|
|
||||||
}
|
}
|
||||||
for i, varID := range state.changedVars.contents() {
|
for _, 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)
|
state.updateVar(VarID(varID), b, BlockEnd)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
prevBlock = b
|
prevBlock = b
|
||||||
}
|
}
|
||||||
|
|
@ -1554,7 +1595,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildFuncDebugNoOptimized constructs a FuncDebug object with
|
// BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with
|
||||||
// entries corresponding to the register-resident input parameters for
|
// entries corresponding to the register-resident input parameters for
|
||||||
// the function "f"; it is used when we are compiling without
|
// the function "f"; it is used when we are compiling without
|
||||||
// optimization but the register ABI is enabled. For each reg param,
|
// 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
|
// the input register, and the second element holds the stack location
|
||||||
// of the param (the assumption being that when optimization is off,
|
// of the param (the assumption being that when optimization is off,
|
||||||
// each input param reg will be spilled in the prolog.
|
// each input param reg will be spilled in the prolog.
|
||||||
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
|
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
|
||||||
fd := FuncDebug{}
|
|
||||||
|
|
||||||
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())
|
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())
|
||||||
|
|
||||||
|
|
@ -1577,7 +1617,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if numRegParams == 0 {
|
if numRegParams == 0 {
|
||||||
return &fd
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := debugState{f: f}
|
state := debugState{f: f}
|
||||||
|
|
@ -1587,7 +1627,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate location lists.
|
// Allocate location lists.
|
||||||
fd.LocationLists = make([][]byte, numRegParams)
|
rval.LocationLists = make([][]byte, numRegParams)
|
||||||
|
|
||||||
// Locate the value corresponding to the last spill of
|
// Locate the value corresponding to the last spill of
|
||||||
// an input register.
|
// an input register.
|
||||||
|
|
@ -1603,10 +1643,10 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
|
||||||
|
|
||||||
n := inp.Name.(*ir.Name)
|
n := inp.Name.(*ir.Name)
|
||||||
sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
|
sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
|
||||||
fd.Vars = append(fd.Vars, n)
|
rval.Vars = append(rval.Vars, n)
|
||||||
fd.Slots = append(fd.Slots, sl)
|
rval.Slots = append(rval.Slots, sl)
|
||||||
slid := len(fd.VarSlots)
|
slid := len(rval.VarSlots)
|
||||||
fd.VarSlots = append(fd.VarSlots, []SlotID{SlotID(slid)})
|
rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
|
||||||
|
|
||||||
if afterPrologVal == ID(-1) {
|
if afterPrologVal == ID(-1) {
|
||||||
// This can happen for degenerate functions with infinite
|
// 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
|
// Param is arriving in one or more registers. We need a 2-element
|
||||||
// location expression for it. First entry in location list
|
// location expression for it. First entry in location list
|
||||||
// will correspond to lifetime in input registers.
|
// 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)
|
BlockStart.ID, afterPrologVal)
|
||||||
if list == nil {
|
if list == nil {
|
||||||
pidx++
|
pidx++
|
||||||
|
|
@ -1688,8 +1728,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
|
||||||
// fill in size
|
// fill in size
|
||||||
ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
|
ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
|
||||||
|
|
||||||
fd.LocationLists[pidx] = list
|
rval.LocationLists[pidx] = list
|
||||||
pidx++
|
pidx++
|
||||||
}
|
}
|
||||||
return &fd
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
|
"internal/buildcfg"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
// "flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -45,7 +45,7 @@ func testGoArch() string {
|
||||||
return *testGoArchFlag
|
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"
|
// 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,
|
// 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,
|
// 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() {
|
switch testGoArch() {
|
||||||
case "arm64", "amd64": // register ABI
|
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
|
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
|
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")
|
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) {
|
func TestInlineLines(t *testing.T) {
|
||||||
if runtime.GOARCH != "amd64" && *testGoArchFlag == "" {
|
if runtime.GOARCH != "amd64" && *testGoArchFlag == "" {
|
||||||
// As of september 2021, works for everything except mips64, but still potentially fragile
|
// 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
|
// 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.
|
// 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
|
// "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) {
|
func testDebugLines(t *testing.T, gcflags, file, function string, wantStmts []int, ignoreRepeats bool) {
|
||||||
dumpBytes := compileAndDump(t, file, function, "-N -l")
|
dumpBytes := compileAndDump(t, file, function, gcflags)
|
||||||
dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
|
dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
|
||||||
var gotStmts []int
|
var gotStmts []int
|
||||||
dumpLineNum := 0
|
dumpLineNum := 0
|
||||||
|
|
@ -201,8 +239,20 @@ func testDebugLines(t *testing.T, file, function string, wantStmts []int) {
|
||||||
gotStmts = append(gotStmts, int(stmt))
|
gotStmts = append(gotStmts, int(stmt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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) {
|
if !reflect.DeepEqual(wantStmts, gotStmts) {
|
||||||
t.Errorf("wanted stmts %v but got %v", 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)
|
(SRLconst (SLLconst x [c]) [d]) && buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x)
|
||||||
|
|
||||||
// comparison simplification
|
// 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
|
((EQ|NE) (CMP x (RSBconst [0] y))) => ((EQ|NE) (CMN x y)) // sense of carry bit not preserved; see also #50854
|
||||||
((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) (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:(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:(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)
|
(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)
|
(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)
|
(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)
|
(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)
|
(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)
|
(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)
|
(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)
|
(EQ (CMPconst [0] x) yes no) => (Z x yes no)
|
||||||
(NE (CMPconst [0] x) yes no) => (NZ 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: "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: "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: "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: "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: "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: "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
|
{name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int64", typ: "Flags"}, // arg0 & auxInt compare to 0
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,7 @@ func init() {
|
||||||
// comparisons
|
// comparisons
|
||||||
{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1
|
{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: "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: "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: "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
|
{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
|
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
|
||||||
// It saves all GP registers if necessary,
|
// It saves all GP registers if necessary,
|
||||||
// but clobbers R14 (LR) because it's a call.
|
// 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"},
|
// 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.
|
// 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
|
// 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
|
{0, 4}, // R2
|
||||||
{1, 8}, // R3
|
{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)
|
b.resetWithControl(BlockARMLE, cmp)
|
||||||
return true
|
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)
|
// match: (GE (CMPconst [0] l:(SUB x y)) yes no)
|
||||||
// cond: l.Uses==1
|
// cond: l.Uses==1
|
||||||
// result: (GEnoov (CMP x y) yes no)
|
// result: (GEnoov (CMP x y) yes no)
|
||||||
|
|
@ -18069,42 +18033,6 @@ func rewriteBlockARM(b *Block) bool {
|
||||||
b.resetWithControl(BlockARMLT, cmp)
|
b.resetWithControl(BlockARMLT, cmp)
|
||||||
return true
|
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)
|
// match: (GT (CMPconst [0] l:(SUB x y)) yes no)
|
||||||
// cond: l.Uses==1
|
// cond: l.Uses==1
|
||||||
// result: (GTnoov (CMP x y) yes no)
|
// result: (GTnoov (CMP x y) yes no)
|
||||||
|
|
@ -19076,42 +19004,6 @@ func rewriteBlockARM(b *Block) bool {
|
||||||
b.resetWithControl(BlockARMGE, cmp)
|
b.resetWithControl(BlockARMGE, cmp)
|
||||||
return true
|
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)
|
// match: (LE (CMPconst [0] l:(SUB x y)) yes no)
|
||||||
// cond: l.Uses==1
|
// cond: l.Uses==1
|
||||||
// result: (LEnoov (CMP x y) yes no)
|
// result: (LEnoov (CMP x y) yes no)
|
||||||
|
|
@ -19992,42 +19884,6 @@ func rewriteBlockARM(b *Block) bool {
|
||||||
b.resetWithControl(BlockARMGT, cmp)
|
b.resetWithControl(BlockARMGT, cmp)
|
||||||
return true
|
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)
|
// match: (LT (CMPconst [0] l:(SUB x y)) yes no)
|
||||||
// cond: l.Uses==1
|
// cond: l.Uses==1
|
||||||
// result: (LTnoov (CMP x y) yes no)
|
// result: (LTnoov (CMP x y) yes no)
|
||||||
|
|
|
||||||
|
|
@ -27983,46 +27983,6 @@ func rewriteBlockARM64(b *Block) bool {
|
||||||
}
|
}
|
||||||
break
|
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)
|
// match: (GE (CMPconst [0] z:(MADD a x y)) yes no)
|
||||||
// cond: z.Uses==1
|
// cond: z.Uses==1
|
||||||
// result: (GEnoov (CMN a (MUL <x.Type> x y)) yes no)
|
// result: (GEnoov (CMN a (MUL <x.Type> x y)) yes no)
|
||||||
|
|
@ -28419,46 +28379,6 @@ func rewriteBlockARM64(b *Block) bool {
|
||||||
}
|
}
|
||||||
break
|
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)
|
// match: (GT (CMPconst [0] z:(MADD a x y)) yes no)
|
||||||
// cond: z.Uses==1
|
// cond: z.Uses==1
|
||||||
// result: (GTnoov (CMN a (MUL <x.Type> x y)) yes no)
|
// result: (GTnoov (CMN a (MUL <x.Type> x y)) yes no)
|
||||||
|
|
@ -28951,46 +28871,6 @@ func rewriteBlockARM64(b *Block) bool {
|
||||||
}
|
}
|
||||||
break
|
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)
|
// match: (LE (CMPconst [0] z:(MADD a x y)) yes no)
|
||||||
// cond: z.Uses==1
|
// cond: z.Uses==1
|
||||||
// result: (LEnoov (CMN a (MUL <x.Type> x y)) yes no)
|
// result: (LEnoov (CMN a (MUL <x.Type> x y)) yes no)
|
||||||
|
|
@ -29363,46 +29243,6 @@ func rewriteBlockARM64(b *Block) bool {
|
||||||
}
|
}
|
||||||
break
|
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)
|
// match: (LT (CMPconst [0] z:(MADD a x y)) yes no)
|
||||||
// cond: z.Uses==1
|
// cond: z.Uses==1
|
||||||
// result: (LTnoov (CMN a (MUL <x.Type> x y)) yes no)
|
// 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
|
var params *abi.ABIParamResultInfo
|
||||||
params = s.f.ABISelf.ABIAnalyze(fn.Type(), true)
|
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
|
// Generate addresses of local declarations
|
||||||
s.decladdrs = map[*ir.Name]*ssa.Value{}
|
s.decladdrs = map[*ir.Name]*ssa.Value{}
|
||||||
for _, n := range fn.Dcl {
|
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)
|
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() {
|
if ft.IsFloat() || tt.IsFloat() {
|
||||||
conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
|
conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
|
||||||
if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat {
|
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
|
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())
|
s.Fatalf("unhandled OCONV %s -> %s", ft.Kind(), tt.Kind())
|
||||||
return nil
|
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
|
for _, p := range params.InParams() { // includes receiver for interface calls
|
||||||
ACArgs = append(ACArgs, p.Type)
|
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 {
|
for i, n := range args {
|
||||||
callArgs = append(callArgs, s.putArg(n, t.Params().Field(i).Type))
|
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
|
// explicit statement boundaries should appear
|
||||||
// in the generated code.
|
// in the generated code.
|
||||||
if p.IsStmt() != src.PosIsStmt {
|
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()
|
p = p.WithNotStmt()
|
||||||
// Calls use the pos attached to v, but copy the statement mark from State
|
// 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 {
|
for i, b := range f.Blocks {
|
||||||
s.bstart[b.ID] = s.pp.Next
|
s.bstart[b.ID] = s.pp.Next
|
||||||
s.lineRunStart = nil
|
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
|
// Attach a "default" liveness info. Normally this will be
|
||||||
// overwritten in the Values loop below for each Value. But
|
// 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 {
|
if base.Ctxt.Flag_locationlists {
|
||||||
var debugInfo *ssa.FuncDebug
|
var debugInfo *ssa.FuncDebug
|
||||||
|
debugInfo = e.curfn.DebugInfo.(*ssa.FuncDebug)
|
||||||
if e.curfn.ABI == obj.ABIInternal && base.Flag.N != 0 {
|
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 {
|
} 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
|
bstart := s.bstart
|
||||||
idToIdx := make([]int, f.NumBlocks())
|
idToIdx := make([]int, f.NumBlocks())
|
||||||
for i, b := range f.Blocks {
|
for i, b := range f.Blocks {
|
||||||
|
|
|
||||||
|
|
@ -588,44 +588,81 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||||
d.Name = p.name()
|
d.Name = p.name()
|
||||||
if p.allowGenerics() && p.tok == _Lbrack {
|
if p.allowGenerics() && p.tok == _Lbrack {
|
||||||
// d.Name "[" ...
|
// d.Name "[" ...
|
||||||
// array/slice or type parameter list
|
// array/slice type or type parameter list
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
p.next()
|
p.next()
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case _Name:
|
case _Name:
|
||||||
// d.Name "[" name ...
|
// We may have an array type or a type parameter list.
|
||||||
// array or type parameter list
|
// In either case we expect an expression x (which may
|
||||||
name := p.name()
|
// just be a name, or a more complex expression) which
|
||||||
// Index or slice expressions are never constant and thus invalid
|
// we can analyze further.
|
||||||
// array length expressions. Thus, if we see a "[" following name
|
//
|
||||||
// we can safely assume that "[" name starts a type parameter list.
|
// A type parameter list may have a type bound starting
|
||||||
var x Expr // x != nil means x is the array length expression
|
// 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 {
|
if p.tok != _Lbrack {
|
||||||
// d.Name "[" name ...
|
// To parse the expression starting with name, expand
|
||||||
// If we reach here, the next token is not a "[", and we need to
|
// the call sequence we would get by passing in name
|
||||||
// parse the expression starting with name. If that expression is
|
// to parser.expr, and pass in name to parser.pexpr.
|
||||||
// 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.
|
|
||||||
p.xnest++
|
p.xnest++
|
||||||
// To parse the expression starting with name, expand the call
|
x = p.binaryExpr(p.pexpr(x, false), 0)
|
||||||
// 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)
|
|
||||||
p.xnest--
|
p.xnest--
|
||||||
if x == name && p.tok != _Rbrack {
|
|
||||||
x = nil
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if 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 ...
|
// d.Name "[" name ...
|
||||||
// type parameter list
|
pname = t
|
||||||
d.TParamList = p.paramList(name, _Rbrack, true)
|
// 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 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.Alias = p.gotAssign()
|
||||||
d.Type = p.typeOrNil()
|
d.Type = p.typeOrNil()
|
||||||
} else {
|
} else {
|
||||||
// d.Name "[" x "]" ...
|
// d.Name "[" x ...
|
||||||
// x is the array length expression
|
|
||||||
d.Type = p.arrayType(pos, x)
|
d.Type = p.arrayType(pos, x)
|
||||||
}
|
}
|
||||||
case _Rbrack:
|
case _Rbrack:
|
||||||
|
|
@ -650,6 +687,21 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||||
return d
|
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 ) .
|
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
|
||||||
func (p *parser) varDecl(group *Group) Decl {
|
func (p *parser) varDecl(group *Group) Decl {
|
||||||
if trace {
|
if trace {
|
||||||
|
|
@ -689,7 +741,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
|
||||||
f.Pragma = p.takePragma()
|
f.Pragma = p.takePragma()
|
||||||
|
|
||||||
if p.got(_Lparen) {
|
if p.got(_Lparen) {
|
||||||
rcvr := p.paramList(nil, _Rparen, false)
|
rcvr := p.paramList(nil, nil, _Rparen, false)
|
||||||
switch len(rcvr) {
|
switch len(rcvr) {
|
||||||
case 0:
|
case 0:
|
||||||
p.error("method has no receiver")
|
p.error("method has no receiver")
|
||||||
|
|
@ -708,7 +760,13 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Name = p.name()
|
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 {
|
if p.tok == _Lbrace {
|
||||||
f.Body = p.funcBody()
|
f.Body = p.funcBody()
|
||||||
}
|
}
|
||||||
|
|
@ -1363,18 +1421,18 @@ func (p *parser) funcType(context string) ([]*Field, *FuncType) {
|
||||||
if p.allowGenerics() && p.got(_Lbrack) {
|
if p.allowGenerics() && p.got(_Lbrack) {
|
||||||
if context != "" {
|
if context != "" {
|
||||||
// accept but complain
|
// 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 {
|
if p.tok == _Rbrack {
|
||||||
p.syntaxError("empty type parameter list")
|
p.syntaxError("empty type parameter list")
|
||||||
p.next()
|
p.next()
|
||||||
} else {
|
} else {
|
||||||
tparamList = p.paramList(nil, _Rbrack, true)
|
tparamList = p.paramList(nil, nil, _Rbrack, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.want(_Lparen)
|
p.want(_Lparen)
|
||||||
typ.ParamList = p.paramList(nil, _Rparen, false)
|
typ.ParamList = p.paramList(nil, nil, _Rparen, false)
|
||||||
typ.ResultList = p.funcResult()
|
typ.ResultList = p.funcResult()
|
||||||
|
|
||||||
return tparamList, typ
|
return tparamList, typ
|
||||||
|
|
@ -1392,6 +1450,13 @@ func (p *parser) arrayType(pos Pos, len Expr) Expr {
|
||||||
len = p.expr()
|
len = p.expr()
|
||||||
p.xnest--
|
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)
|
p.want(_Rbrack)
|
||||||
t := new(ArrayType)
|
t := new(ArrayType)
|
||||||
t.pos = pos
|
t.pos = pos
|
||||||
|
|
@ -1516,7 +1581,7 @@ func (p *parser) funcResult() []*Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.got(_Lparen) {
|
if p.got(_Lparen) {
|
||||||
return p.paramList(nil, _Rparen, false)
|
return p.paramList(nil, nil, _Rparen, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
|
|
@ -1742,7 +1807,7 @@ func (p *parser) methodDecl() *Field {
|
||||||
|
|
||||||
// A type argument list looks like a parameter list with only
|
// A type argument list looks like a parameter list with only
|
||||||
// types. Parse a parameter list and decide afterwards.
|
// 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 {
|
if len(list) == 0 {
|
||||||
// The type parameter list is not [] but we got nothing
|
// The type parameter list is not [] but we got nothing
|
||||||
// due to other errors (reported by paramList). Treat
|
// 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
|
// TODO(gri) Record list as type parameter list with f.Type
|
||||||
// if we want to type-check the generic method.
|
// if we want to type-check the generic method.
|
||||||
// For now, report an error so this is not a silent event.
|
// 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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1948,17 +2013,41 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
|
||||||
// ParameterList = ParameterDecl { "," ParameterDecl } .
|
// ParameterList = ParameterDecl { "," ParameterDecl } .
|
||||||
// "(" or "[" has already been consumed.
|
// "(" or "[" has already been consumed.
|
||||||
// If name != nil, it is the first name after "(" or "[".
|
// 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.
|
// 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 {
|
if trace {
|
||||||
defer p.trace("paramList")()
|
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 named int // number of parameters that have an explicit name and type
|
||||||
var typed int // number of parameters that have an explicit type
|
var typed int // number of parameters that have an explicit type
|
||||||
end := p.list(_Comma, close, func() bool {
|
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
|
name = nil // 1st name was consumed if present
|
||||||
|
typ = nil // 1st type was consumed if present
|
||||||
if par != nil {
|
if par != nil {
|
||||||
if debug && par.Name == nil && par.Type == nil {
|
if debug && par.Name == nil && par.Type == nil {
|
||||||
panic("parameter without name or type")
|
panic("parameter without name or type")
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func TestParseGo2(t *testing.T) {
|
||||||
for _, fi := range list {
|
for _, fi := range list {
|
||||||
name := fi.Name()
|
name := fi.Name()
|
||||||
if !fi.IsDir() && !strings.HasPrefix(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
|
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.
|
// and returns the printed string.
|
||||||
func String(n Node) string {
|
func String(n Node) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -666,9 +666,7 @@ func (p *printer) printRawNode(n Node) {
|
||||||
}
|
}
|
||||||
p.print(n.Name)
|
p.print(n.Name)
|
||||||
if n.TParamList != nil {
|
if n.TParamList != nil {
|
||||||
p.print(_Lbrack)
|
p.printParameterList(n.TParamList, true)
|
||||||
p.printFieldList(n.TParamList, nil, _Comma)
|
|
||||||
p.print(_Rbrack)
|
|
||||||
}
|
}
|
||||||
p.print(blank)
|
p.print(blank)
|
||||||
if n.Alias {
|
if n.Alias {
|
||||||
|
|
@ -700,9 +698,7 @@ func (p *printer) printRawNode(n Node) {
|
||||||
}
|
}
|
||||||
p.print(n.Name)
|
p.print(n.Name)
|
||||||
if n.TParamList != nil {
|
if n.TParamList != nil {
|
||||||
p.print(_Lbrack)
|
p.printParameterList(n.TParamList, true)
|
||||||
p.printFieldList(n.TParamList, nil, _Comma)
|
|
||||||
p.print(_Rbrack)
|
|
||||||
}
|
}
|
||||||
p.printSignature(n.Type)
|
p.printSignature(n.Type)
|
||||||
if n.Body != nil {
|
if n.Body != nil {
|
||||||
|
|
@ -887,20 +883,23 @@ func (p *printer) printDeclList(list []Decl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printSignature(sig *FuncType) {
|
func (p *printer) printSignature(sig *FuncType) {
|
||||||
p.printParameterList(sig.ParamList)
|
p.printParameterList(sig.ParamList, false)
|
||||||
if list := sig.ResultList; list != nil {
|
if list := sig.ResultList; list != nil {
|
||||||
p.print(blank)
|
p.print(blank)
|
||||||
if len(list) == 1 && list[0].Name == nil {
|
if len(list) == 1 && list[0].Name == nil {
|
||||||
p.printNode(list[0].Type)
|
p.printNode(list[0].Type)
|
||||||
} else {
|
} else {
|
||||||
p.printParameterList(list)
|
p.printParameterList(list, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printParameterList(list []*Field) {
|
func (p *printer) printParameterList(list []*Field, types bool) {
|
||||||
p.print(_Lparen)
|
open, close := _Lparen, _Rparen
|
||||||
if len(list) > 0 {
|
if types {
|
||||||
|
open, close = _Lbrack, _Rbrack
|
||||||
|
}
|
||||||
|
p.print(open)
|
||||||
for i, f := range list {
|
for i, f := range list {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
p.print(_Comma, blank)
|
p.print(_Comma, blank)
|
||||||
|
|
@ -915,10 +914,16 @@ func (p *printer) printParameterList(list []*Field) {
|
||||||
}
|
}
|
||||||
p.print(blank)
|
p.print(blank)
|
||||||
}
|
}
|
||||||
p.printNode(f.Type)
|
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) {
|
func (p *printer) printStmtList(list []Stmt, braces bool) {
|
||||||
|
|
|
||||||
|
|
@ -53,54 +53,77 @@ func TestPrintError(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stringTests = []string{
|
var stringTests = [][2]string{
|
||||||
"package p",
|
dup("package p"),
|
||||||
"package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )",
|
dup("package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )"),
|
||||||
|
|
||||||
// generic type declarations
|
// generic type declarations
|
||||||
"package p; type _[T any] struct{}",
|
dup("package p; type _[T any] struct{}"),
|
||||||
"package p; type _[A, B, C interface{m()}] struct{}",
|
dup("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, 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
|
// generic function declarations
|
||||||
"package p; func _[T any]()",
|
dup("package p; func _[T any]()"),
|
||||||
"package p; func _[A, B, C interface{m()}]()",
|
dup("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, A, B, C interface{m()}, X, Y, Z interface{~int}]()"),
|
||||||
|
|
||||||
// methods with generic receiver types
|
// methods with generic receiver types
|
||||||
"package p; func (R[T]) _()",
|
dup("package p; func (R[T]) _()"),
|
||||||
"package p; func (*R[A, B, C]) _()",
|
dup("package p; func (*R[A, B, C]) _()"),
|
||||||
"package p; func (_ *R[A, B, C]) _()",
|
dup("package p; func (_ *R[A, B, C]) _()"),
|
||||||
|
|
||||||
// type constraint literals with elided interfaces
|
// type constraint literals with elided interfaces
|
||||||
"package p; func _[P ~int, Q int | string]() {}",
|
dup("package p; func _[P ~int, Q int | string]() {}"),
|
||||||
"package p; func _[P struct{f int}, Q *P]() {}",
|
dup("package p; func _[P struct{f int}, Q *P]() {}"),
|
||||||
|
|
||||||
// channels
|
// channels
|
||||||
"package p; type _ chan chan int",
|
dup("package p; type _ chan chan int"),
|
||||||
"package p; type _ chan (<-chan int)",
|
dup("package p; type _ chan (<-chan int)"),
|
||||||
"package p; type _ chan chan<- int",
|
dup("package p; type _ chan chan<- int"),
|
||||||
|
|
||||||
"package p; type _ <-chan chan int",
|
dup("package p; type _ <-chan chan int"),
|
||||||
"package p; type _ <-chan <-chan int",
|
dup("package p; type _ <-chan <-chan int"),
|
||||||
"package p; type _ <-chan chan<- int",
|
dup("package p; type _ <-chan chan<- int"),
|
||||||
|
|
||||||
"package p; type _ chan<- chan int",
|
dup("package p; type _ chan<- chan int"),
|
||||||
"package p; type _ chan<- <-chan int",
|
dup("package p; type _ chan<- <-chan int"),
|
||||||
"package p; type _ chan<- chan<- int",
|
dup("package p; type _ chan<- chan<- int"),
|
||||||
|
|
||||||
// TODO(gri) expand
|
// TODO(gri) expand
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrintString(t *testing.T) {
|
func TestPrintString(t *testing.T) {
|
||||||
for _, want := range stringTests {
|
for _, test := range stringTests {
|
||||||
ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics)
|
ast, err := Parse(nil, strings.NewReader(test[0]), nil, nil, AllowGenerics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if got := String(ast); got != want {
|
if got := String(ast); got != test[1] {
|
||||||
t.Errorf("%q: got %q", want, got)
|
t.Errorf("%q: got %q", test[1], got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ type Mode uint
|
||||||
const (
|
const (
|
||||||
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
|
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
|
||||||
AllowGenerics
|
AllowGenerics
|
||||||
|
AllowMethodTypeParams // does not support interface methods yet; ignored if AllowGenerics is not set
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error describes a syntax error. Error implements the error interface.
|
// Error describes a syntax error. Error implements the error interface.
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
package p
|
package p
|
||||||
|
|
||||||
type _ func /* ERROR function type cannot have type parameters */ [ /* ERROR empty type parameter list */ ]()
|
type _ func /* ERROR function type must have no 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 must have no 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 */ [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{
|
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 {
|
type t interface {
|
||||||
t[a]
|
t[a]
|
||||||
m /* ERROR method cannot have type parameters */ [_ _, /* ERROR mixed */ _]()
|
m /* ERROR method must have no type parameters */ [_ _, /* ERROR mixed */ _]()
|
||||||
t[a, b]
|
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