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

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

View file

@ -1,6 +1,11 @@
---
name: Bugs
about: The go command, standard library, or anything else
title: "affected/package: "
---
<!-- <!--
Please answer these questions before submitting your issue. Thanks! 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
View file

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

View file

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

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

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

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

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

View file

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

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

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

View file

@ -209,6 +209,7 @@ Benjamin Hsieh <tanookiben@users.noreply.github.com>
Benny Siegert <bsiegert@gmail.com> 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>

View file

@ -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
View file

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

View file

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

6858
doc/go1.17_spec.html Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,8 +8,8 @@
# Consult https://www.iana.org/time-zones for the latest versions. # 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.

View file

@ -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) }

View file

@ -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"

View file

@ -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()

View file

@ -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)
}
}

View file

@ -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)
} }
} }

View file

@ -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")
} }

View file

@ -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")
}
}

View file

@ -0,0 +1,30 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"os"
"os/exec"
_ "plugin"
"sync"
)
func main() {
if os.Args[1] != "1" {
return
}
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// does not matter what we exec, just exec itself
cmd := exec.Command("./forkexec.exe", "0")
cmd.Run()
}()
}
wg.Wait()
}

View file

@ -33,11 +33,13 @@ func TestASAN(t *testing.T) {
cases := []struct { 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)

View file

@ -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))

View file

@ -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)
} }

View file

@ -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)
} }

View file

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

View file

@ -20,6 +20,7 @@ import (
"regexp" "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)))
} }

View file

@ -17,4 +17,4 @@ export IPHONEOS_DEPLOYMENT_TARGET=5.1
# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang. # 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 "$@"

View file

@ -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)

View file

@ -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 {

View file

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

View file

@ -1363,7 +1363,7 @@ func TestFileReader(t *testing.T) {
wantLCnt int64 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 {

View file

@ -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.

View file

@ -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 {

View file

@ -0,0 +1,81 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package zip
import (
"bytes"
"io"
"os"
"path/filepath"
"testing"
)
func FuzzReader(f *testing.F) {
testdata, err := os.ReadDir("testdata")
if err != nil {
f.Fatalf("failed to read testdata directory: %s", err)
}
for _, de := range testdata {
if de.IsDir() {
continue
}
b, err := os.ReadFile(filepath.Join("testdata", de.Name()))
if err != nil {
f.Fatalf("failed to read testdata: %s", err)
}
f.Add(b)
}
f.Fuzz(func(t *testing.T, b []byte) {
r, err := NewReader(bytes.NewReader(b), int64(len(b)))
if err != nil {
return
}
type file struct {
header *FileHeader
content []byte
}
files := []file{}
for _, f := range r.File {
fr, err := f.Open()
if err != nil {
continue
}
content, err := io.ReadAll(fr)
if err != nil {
continue
}
files = append(files, file{header: &f.FileHeader, content: content})
if _, err := r.Open(f.Name); err != nil {
continue
}
}
// If we were unable to read anything out of the archive don't
// bother trying to roundtrip it.
if len(files) == 0 {
return
}
w := NewWriter(io.Discard)
for _, f := range files {
ww, err := w.CreateHeader(f.header)
if err != nil {
t.Fatalf("unable to write previously parsed header: %s", err)
}
if _, err := ww.Write(f.content); err != nil {
t.Fatalf("unable to write previously parsed content: %s", err)
}
}
if err := w.Close(); err != nil {
t.Fatalf("Unable to write archive: %s", err)
}
// TODO: We may want to check if the archive roundtrips.
})
}

View file

@ -125,7 +125,6 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
if err != nil { 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 {

View file

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

View file

@ -163,7 +163,7 @@ func (fi headerFileInfo) ModTime() time.Time {
} }
func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() } func (fi headerFileInfo) 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
}

View file

@ -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)
} }

View file

@ -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)]
} }

View file

@ -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.

View file

@ -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},

View file

@ -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)

View file

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

View file

@ -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)

View file

@ -40,12 +40,12 @@ which is used for error reporting and the creation of debugging information.
* `cmd/compile/internal/gc` (create compiler AST, type checking, AST transformations) * `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.

View file

@ -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 Gos *Rationale*: These register meanings are compatible with Gos
stack-based calling convention except for R14 and X15, which will have stack-based calling convention except for R14 and X15, which will have

View file

@ -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),

View file

@ -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)
} }
} }

View file

@ -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()

View file

@ -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

View file

@ -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 {

View file

@ -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()

View file

@ -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

View file

@ -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,29 +108,33 @@ 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) {
skipSpecialPlatforms(t) skipSpecialPlatforms(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)}"},

View file

@ -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
} }

View file

@ -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{}

View file

@ -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 (

View file

@ -0,0 +1,29 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is used to generate an object file which
// serves as test file for gcimporter_test.go.
package generics
type Any any
var x any
type T[A, B any] struct {
Left A
Right B
}
var X T[int, string] = T[int, string]{1, "hi"}
func ToInt[P interface{ ~int }](p P) int { return int(p) }
var IntID = ToInt[int]
type G[C comparable] int
func ImplicitFunc[T ~int]() {}
type ImplicitType[T ~int] int

View file

@ -1108,11 +1108,15 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
// closure does the necessary substitions for a ClosureExpr n and returns the new // closure 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)
} }

View file

@ -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 {

View file

@ -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

View file

@ -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(),

View file

@ -86,6 +86,7 @@ func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
} }
func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) { 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)
} }

View file

@ -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 {

View file

@ -96,6 +96,17 @@ func check2(noders []*noder) {
} }
} }
// Information about sub-dictionary entries in a dictionary
type subDictInfo struct {
// Call or XDOT node that requires a dictionary.
callNode ir.Node
// Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic
// method or function call, since this node will get dropped when the generic
// method/function call is transformed to a call on the instantiated shape
// function. Nil for other kinds of calls or XDOTs.
savedXNode ir.Node
}
// dictInfo is the dictionary format for an instantiation of a generic function with // 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

View file

@ -2029,7 +2029,7 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
// Quirk: If deadcode elimination turned a non-empty function into // 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 {

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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
} }

View file

@ -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,

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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)
} }
}
} }

View file

@ -1270,8 +1270,8 @@
(SRLconst (SLLconst x [c]) [d]) && buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x) (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)

View file

@ -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)

View file

@ -285,9 +285,9 @@ func init() {
{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to auxInt {name: "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

View file

@ -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

View file

@ -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

View file

@ -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
}, },
}, },
{ {

View file

@ -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)

View file

@ -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)

View file

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

View file

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

View file

@ -484,6 +484,19 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
var params *abi.ABIParamResultInfo 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 {

View file

@ -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")

View file

@ -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)
} }
} }
} }

View file

@ -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) {

View file

@ -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)
} }
} }
} }

View file

@ -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.

View file

@ -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]()
} }

View file

@ -0,0 +1,31 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type (
// these need a comma to disambiguate
_[P *T,] struct{}
_[P *T, _ any] struct{}
_[P (*T),] struct{}
_[P (*T), _ any] struct{}
_[P (T),] struct{}
_[P (T), _ any] struct{}
// these parse as name followed by type
_[P *struct{}] struct{}
_[P (*struct{})] struct{}
_[P ([]int)] struct{}
// array declarations
_ [P(T)]struct{}
_ [P((T))]struct{}
_ [P * *T] struct{} // this could be a name followed by a type but it makes the rules more complicated
_ [P * T]struct{}
_ [P(*T)]struct{}
_ [P(**T)]struct{}
_ [P * T - T]struct{}
_ [P*T-T /* ERROR unexpected comma */ ,]struct{}
_ [10 /* ERROR unexpected comma */ ,]struct{}
)

View file

@ -13,7 +13,7 @@ type t struct {
} }
type t interface { 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]
} }

View file

@ -0,0 +1,62 @@
// 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 test
import (
"fmt"
"sort"
"testing"
)
// Test that calling methods on generic types doesn't cause allocations.
func genericSorted[T sort.Interface](data T) bool {
n := data.Len()
for i := n - 1; i > 0; i-- {
if data.Less(i, i-1) {
return false
}
}
return true
}
func TestGenericSorted(t *testing.T) {
var data = sort.IntSlice{-10, -5, 0, 1, 2, 3, 5, 7, 11, 100, 100, 100, 1000, 10000}
f := func() {
genericSorted(data)
}
if n := testing.AllocsPerRun(10, f); n > 0 {
t.Errorf("got %f allocs, want 0", n)
}
}
// Test that escape analysis correctly tracks escaping inside of methods
// called on generic types.
type fooer interface {
foo()
}
type P struct {
p *int
q int
}
var esc []*int
func (p P) foo() {
esc = append(esc, p.p) // foo escapes the pointer from inside of p
}
func f[T fooer](t T) {
t.foo()
}
func TestGenericEscape(t *testing.T) {
for i := 0; i < 4; i++ {
var x int = 77 + i
var p P = P{p: &x}
f(p)
}
for i, p := range esc {
if got, want := *p, 77+i; got != want {
panic(fmt.Sprintf("entry %d: got %d, want %d", i, got, want))
}
}
}

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