mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
[dev.boringcrypto.go1.17] all: merge go1.17rc2 into dev.boringcrypto.go1.17
Change-Id: I24c7d03fb140d1df8f33b950ee54f4ee51f345d6
This commit is contained in:
commit
ef7be8869c
2
AUTHORS
2
AUTHORS
@ -41,7 +41,7 @@ Aeneas Rekkas (arekkas) <aeneas@ory.am>
|
||||
Afanasev Stanislav <phpprogger@gmail.com>
|
||||
Agis Anastasopoulos <agis.anast@gmail.com>
|
||||
Agniva De Sarker <agnivade@yahoo.co.in>
|
||||
Ahmed Wahed <oneofone@gmail.com>
|
||||
Ahmed W. Mones <oneofone@gmail.com>
|
||||
Ahmet Soormally <ahmet@mangomm.co.uk>
|
||||
Ahmy Yulrizka <yulrizka@gmail.com>
|
||||
Aiden Scandella <ai@uber.com>
|
||||
|
@ -67,7 +67,7 @@ Aeneas Rekkas (arekkas) <aeneas@ory.am>
|
||||
Afanasev Stanislav <phpprogger@gmail.com>
|
||||
Agis Anastasopoulos <agis.anast@gmail.com>
|
||||
Agniva De Sarker <agnivade@yahoo.co.in>
|
||||
Ahmed Wahed <oneofone@gmail.com>
|
||||
Ahmed W. Mones <oneofone@gmail.com>
|
||||
Ahmet Alp Balkan <ahmetb@google.com>
|
||||
Ahmet Soormally <ahmet@mangomm.co.uk>
|
||||
Ahmy Yulrizka <yulrizka@gmail.com>
|
||||
|
195
api/go1.17.txt
Normal file
195
api/go1.17.txt
Normal file
@ -0,0 +1,195 @@
|
||||
pkg archive/zip, method (*File) OpenRaw() (io.Reader, error)
|
||||
pkg archive/zip, method (*Writer) Copy(*File) error
|
||||
pkg archive/zip, method (*Writer) CreateRaw(*FileHeader) (io.Writer, error)
|
||||
pkg compress/lzw, method (*Reader) Close() error
|
||||
pkg compress/lzw, method (*Reader) Read([]uint8) (int, error)
|
||||
pkg compress/lzw, method (*Reader) Reset(io.Reader, Order, int)
|
||||
pkg compress/lzw, method (*Writer) Close() error
|
||||
pkg compress/lzw, method (*Writer) Reset(io.Writer, Order, int)
|
||||
pkg compress/lzw, method (*Writer) Write([]uint8) (int, error)
|
||||
pkg compress/lzw, type Reader struct
|
||||
pkg compress/lzw, type Writer struct
|
||||
pkg crypto/tls, method (*CertificateRequestInfo) Context() context.Context
|
||||
pkg crypto/tls, method (*ClientHelloInfo) Context() context.Context
|
||||
pkg crypto/tls, method (*Conn) HandshakeContext(context.Context) error
|
||||
pkg database/sql, method (*NullByte) Scan(interface{}) error
|
||||
pkg database/sql, method (*NullInt16) Scan(interface{}) error
|
||||
pkg database/sql, method (NullByte) Value() (driver.Value, error)
|
||||
pkg database/sql, method (NullInt16) Value() (driver.Value, error)
|
||||
pkg database/sql, type NullByte struct
|
||||
pkg database/sql, type NullByte struct, Byte uint8
|
||||
pkg database/sql, type NullByte struct, Valid bool
|
||||
pkg database/sql, type NullInt16 struct
|
||||
pkg database/sql, type NullInt16 struct, Int16 int16
|
||||
pkg database/sql, type NullInt16 struct, Valid bool
|
||||
pkg debug/elf, const SHT_MIPS_ABIFLAGS = 1879048234
|
||||
pkg debug/elf, const SHT_MIPS_ABIFLAGS SectionType
|
||||
pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
|
||||
pkg go/build, type Context struct, ToolTags []string
|
||||
pkg go/parser, const SkipObjectResolution = 64
|
||||
pkg go/parser, const SkipObjectResolution Mode
|
||||
pkg image, method (*Alpha) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*Alpha) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*Alpha16) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*Alpha16) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*CMYK) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*CMYK) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*Gray) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*Gray) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*Gray16) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*Gray16) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*NRGBA) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*NRGBA) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*NRGBA64) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*NRGBA64) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*NYCbCrA) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*Paletted) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*Paletted) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*RGBA) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*RGBA) SetRGBA64(int, int, color.RGBA64)
|
||||
pkg image, method (*Uniform) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*YCbCr) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (Rectangle) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At }
|
||||
pkg image, type RGBA64Image interface, At(int, int) color.Color
|
||||
pkg image, type RGBA64Image interface, Bounds() Rectangle
|
||||
pkg image, type RGBA64Image interface, ColorModel() color.Model
|
||||
pkg image, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
|
||||
pkg image/draw, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At, Set, SetRGBA64 }
|
||||
pkg image/draw, type RGBA64Image interface, At(int, int) color.Color
|
||||
pkg image/draw, type RGBA64Image interface, Bounds() image.Rectangle
|
||||
pkg image/draw, type RGBA64Image interface, ColorModel() color.Model
|
||||
pkg image/draw, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
|
||||
pkg image/draw, type RGBA64Image interface, Set(int, int, color.Color)
|
||||
pkg image/draw, type RGBA64Image interface, SetRGBA64(int, int, color.RGBA64)
|
||||
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
|
||||
pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
|
||||
pkg math, const MaxInt = 9223372036854775807
|
||||
pkg math, const MaxInt ideal-int
|
||||
pkg math, const MaxUint = 18446744073709551615
|
||||
pkg math, const MaxUint ideal-int
|
||||
pkg math, const MinInt = -9223372036854775808
|
||||
pkg math, const MinInt ideal-int
|
||||
pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 1/713623846352979940529142984724747568191373312
|
||||
pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784
|
||||
pkg net, method (*ParseError) Temporary() bool
|
||||
pkg net, method (*ParseError) Timeout() bool
|
||||
pkg net, method (IP) IsPrivate() bool
|
||||
pkg net/http, func AllowQuerySemicolons(Handler) Handler
|
||||
pkg net/url, method (Values) Has(string) bool
|
||||
pkg reflect, func VisibleFields(Type) []StructField
|
||||
pkg reflect, method (Method) IsExported() bool
|
||||
pkg reflect, method (StructField) IsExported() bool
|
||||
pkg reflect, method (Value) CanConvert(Type) bool
|
||||
pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (darwin-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (freebsd-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (freebsd-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (freebsd-arm-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (freebsd-arm-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (linux-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (linux-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (linux-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (linux-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (linux-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (linux-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (linux-arm-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (linux-arm-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (linux-arm-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (linux-arm-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-arm-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-arm-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (openbsd-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (openbsd-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), type Handle uintptr
|
||||
pkg strconv, func QuotedPrefix(string) (string, error)
|
||||
pkg sync/atomic, method (*Value) CompareAndSwap(interface{}, interface{}) bool
|
||||
pkg sync/atomic, method (*Value) Swap(interface{}) interface{}
|
||||
pkg syscall (netbsd-386), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-386), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-386), const WEXITED = 32
|
||||
pkg syscall (netbsd-386), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-386-cgo), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-386-cgo), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-386-cgo), const WEXITED = 32
|
||||
pkg syscall (netbsd-386-cgo), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-amd64), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-amd64), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-amd64), const WEXITED = 32
|
||||
pkg syscall (netbsd-amd64), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-amd64-cgo), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-amd64-cgo), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-amd64-cgo), const WEXITED = 32
|
||||
pkg syscall (netbsd-amd64-cgo), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-arm), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-arm), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-arm), const WEXITED = 32
|
||||
pkg syscall (netbsd-arm), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-arm-cgo), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-arm-cgo), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-arm-cgo), const WEXITED = 32
|
||||
pkg syscall (netbsd-arm-cgo), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-arm64), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-arm64), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-arm64), const WEXITED = 32
|
||||
pkg syscall (netbsd-arm64), const WEXITED ideal-int
|
||||
pkg syscall (netbsd-arm64-cgo), const SYS_WAIT6 = 481
|
||||
pkg syscall (netbsd-arm64-cgo), const SYS_WAIT6 ideal-int
|
||||
pkg syscall (netbsd-arm64-cgo), const WEXITED = 32
|
||||
pkg syscall (netbsd-arm64-cgo), const WEXITED ideal-int
|
||||
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (windows-386), type SysProcAttr struct, AdditionalInheritedHandles []Handle
|
||||
pkg syscall (windows-386), type SysProcAttr struct, ParentProcess Handle
|
||||
pkg syscall (windows-amd64), type SysProcAttr struct, AdditionalInheritedHandles []Handle
|
||||
pkg syscall (windows-amd64), type SysProcAttr struct, ParentProcess Handle
|
||||
pkg testing, method (*B) Setenv(string, string)
|
||||
pkg testing, method (*T) Setenv(string, string)
|
||||
pkg testing, type TB interface, Setenv(string, string)
|
||||
pkg text/template/parse, const SkipFuncCheck = 2
|
||||
pkg text/template/parse, const SkipFuncCheck Mode
|
||||
pkg time, const Layout = "01/02 03:04:05PM '06 -0700"
|
||||
pkg time, const Layout ideal-string
|
||||
pkg time, func UnixMicro(int64) Time
|
||||
pkg time, func UnixMilli(int64) Time
|
||||
pkg time, method (Time) GoString() string
|
||||
pkg time, method (Time) IsDST() bool
|
||||
pkg time, method (Time) UnixMicro() int64
|
||||
pkg time, method (Time) UnixMilli() int64
|
99
api/next.txt
99
api/next.txt
@ -1,99 +0,0 @@
|
||||
pkg compress/lzw, method (*Reader) Close() error
|
||||
pkg compress/lzw, method (*Reader) Read([]uint8) (int, error)
|
||||
pkg compress/lzw, method (*Reader) Reset(io.Reader, Order, int)
|
||||
pkg compress/lzw, method (*Writer) Close() error
|
||||
pkg compress/lzw, method (*Writer) Reset(io.Writer, Order, int)
|
||||
pkg compress/lzw, method (*Writer) Write([]uint8) (int, error)
|
||||
pkg compress/lzw, type Reader struct
|
||||
pkg compress/lzw, type Writer struct
|
||||
pkg crypto/tls, method (*CertificateRequestInfo) Context() context.Context
|
||||
pkg crypto/tls, method (*ClientHelloInfo) Context() context.Context
|
||||
pkg crypto/tls, method (*Conn) HandshakeContext(context.Context) error
|
||||
pkg debug/elf, const SHT_MIPS_ABIFLAGS = 1879048234
|
||||
pkg debug/elf, const SHT_MIPS_ABIFLAGS SectionType
|
||||
pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
|
||||
pkg go/ast, method (*FuncDecl) IsMethod() bool
|
||||
pkg go/build, type Context struct, ToolTags []string
|
||||
pkg go/parser, const SkipObjectResolution = 64
|
||||
pkg go/parser, const SkipObjectResolution Mode
|
||||
pkg go/types, type Config struct, GoVersion string
|
||||
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
|
||||
pkg net, method (*ParseError) Temporary() bool
|
||||
pkg net, method (*ParseError) Timeout() bool
|
||||
pkg net, method (IP) IsPrivate() bool
|
||||
pkg reflect, func VisibleFields(Type) []StructField
|
||||
pkg reflect, method (Method) IsExported() bool
|
||||
pkg reflect, method (StructField) IsExported() bool
|
||||
pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (darwin-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (freebsd-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (freebsd-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (freebsd-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (freebsd-arm-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (freebsd-arm-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (linux-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (linux-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (linux-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (linux-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (linux-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (linux-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (linux-arm-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (linux-arm-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (linux-arm-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (linux-arm-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-amd64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-arm-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-arm-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (netbsd-arm64-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (openbsd-386-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (openbsd-386-cgo), type Handle uintptr
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), func NewHandle(interface{}) Handle
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Delete()
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Value() interface{}
|
||||
pkg runtime/cgo (openbsd-amd64-cgo), type Handle uintptr
|
||||
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC = 2048
|
||||
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC ideal-int
|
||||
pkg syscall (windows-386), type SysProcAttr struct, AdditionalInheritedHandles []Handle
|
||||
pkg syscall (windows-386), type SysProcAttr struct, ParentProcess Handle
|
||||
pkg syscall (windows-amd64), type SysProcAttr struct, AdditionalInheritedHandles []Handle
|
||||
pkg syscall (windows-amd64), type SysProcAttr struct, ParentProcess Handle
|
||||
pkg testing, method (*B) Setenv(string, string)
|
||||
pkg testing, method (*T) Setenv(string, string)
|
||||
pkg text/template/parse, const SkipFuncCheck = 2
|
||||
pkg text/template/parse, const SkipFuncCheck Mode
|
||||
pkg time, func UnixMicro(int64) Time
|
||||
pkg time, func UnixMilli(int64) Time
|
||||
pkg time, method (*Time) IsDST() bool
|
||||
pkg time, method (Time) UnixMicro() int64
|
||||
pkg time, method (Time) UnixMilli() int64
|
@ -1,2 +1 @@
|
||||
branch: dev.boringcrypto
|
||||
parent-branch: master
|
||||
branch: dev.boringcrypto.go1.17
|
||||
|
@ -827,10 +827,6 @@ The other codes are <code>-></code> (arithmetic right shift),
|
||||
|
||||
<h3 id="arm64">ARM64</h3>
|
||||
|
||||
<p>
|
||||
The ARM64 port is in an experimental state.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<code>R18</code> is the "platform register", reserved on the Apple platform.
|
||||
To prevent accidental misuse, the register is named <code>R18_PLATFORM</code>.
|
||||
|
872
doc/go1.17.html
872
doc/go1.17.html
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of Apr 28, 2021",
|
||||
"Subtitle": "Version of Jul 26, 2021",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
@ -490,8 +490,8 @@ After a backslash, certain single-character escapes represent special values:
|
||||
\n U+000A line feed or newline
|
||||
\r U+000D carriage return
|
||||
\t U+0009 horizontal tab
|
||||
\v U+000b vertical tab
|
||||
\\ U+005c backslash
|
||||
\v U+000B vertical tab
|
||||
\\ U+005C backslash
|
||||
\' U+0027 single quote (valid escape only within rune literals)
|
||||
\" U+0022 double quote (valid escape only within string literals)
|
||||
</pre>
|
||||
@ -4334,7 +4334,10 @@ s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s)
|
||||
|
||||
var t []string
|
||||
t0 := (*[0]string)(t) // t0 == nil
|
||||
t1 := (*[1]string)(t) // panics: len([1]string) > len(s)
|
||||
t1 := (*[1]string)(t) // panics: len([1]string) > len(t)
|
||||
|
||||
u := make([]byte, 0)
|
||||
u0 = (*[0]byte)(u) // u0 != nil
|
||||
</pre>
|
||||
|
||||
<h3 id="Constant_expressions">Constant expressions</h3>
|
||||
@ -4670,7 +4673,7 @@ The following built-in functions are not permitted in statement context:
|
||||
|
||||
<pre>
|
||||
append cap complex imag len make new real
|
||||
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
|
||||
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
@ -4909,7 +4912,7 @@ if x := f(); x < y {
|
||||
|
||||
<p>
|
||||
"Switch" statements provide multi-way execution.
|
||||
An expression or type specifier is compared to the "cases"
|
||||
An expression or type is compared to the "cases"
|
||||
inside the "switch" to determine which branch
|
||||
to execute.
|
||||
</p>
|
||||
@ -5020,7 +5023,7 @@ floating point, or string constants in case expressions.
|
||||
A type switch compares types rather than values. It is otherwise similar
|
||||
to an expression switch. It is marked by a special switch expression that
|
||||
has the form of a <a href="#Type_assertions">type assertion</a>
|
||||
using the reserved word <code>type</code> rather than an actual type:
|
||||
using the keyword <code>type</code> rather than an actual type:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@ -6782,18 +6785,26 @@ The rules for <a href="/pkg/unsafe#Pointer">valid uses</a> of <code>Pointer</cod
|
||||
|
||||
<p>
|
||||
The function <code>Slice</code> returns a slice whose underlying array starts at <code>ptr</code>
|
||||
and whose length and capacity are <code>len</code>:
|
||||
and whose length and capacity are <code>len</code>.
|
||||
<code>Slice(ptr, len)</code> is equivalent to
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
except that, as a special case, if <code>ptr</code>
|
||||
is <code>nil</code> and <code>len</code> is zero,
|
||||
<code>Slice</code> returns <code>nil</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <code>len</code> argument must be of integer type or an untyped <a href="#Constants">constant</a>.
|
||||
A constant <code>len</code> argument must be non-negative and <a href="#Representability">representable</a> by a value of type <code>int</code>;
|
||||
if it is an untyped constant it is given type <code>int</code>.
|
||||
If <code>ptr</code> is <code>nil</code> or <code>len</code> is negative at run time,
|
||||
At run time, if <code>len</code> is negative,
|
||||
or if <code>ptr</code> is <code>nil</code> and <code>len</code> is not zero,
|
||||
a <a href="#Run_time_panics">run-time panic</a> occurs.
|
||||
</p>
|
||||
|
||||
|
@ -40,7 +40,8 @@ func check(t *testing.T, file string) {
|
||||
if len(frags) == 1 {
|
||||
continue
|
||||
}
|
||||
re, err := regexp.Compile(string(frags[1]))
|
||||
frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1])
|
||||
re, err := regexp.Compile(frag)
|
||||
if err != nil {
|
||||
t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1])
|
||||
continue
|
||||
|
12
misc/cgo/errors/testdata/err2.go
vendored
12
misc/cgo/errors/testdata/err2.go
vendored
@ -40,15 +40,15 @@ func main() {
|
||||
C.foop = x // ERROR HERE
|
||||
|
||||
// issue 13129: used to output error about C.unsignedshort with CC=clang
|
||||
var x C.ushort
|
||||
x = int(0) // ERROR HERE: C\.ushort
|
||||
var x1 C.ushort
|
||||
x1 = int(0) // ERROR HERE: C\.ushort
|
||||
|
||||
// issue 13423
|
||||
_ = C.fopen() // ERROR HERE
|
||||
|
||||
// issue 13467
|
||||
var x rune = '✈'
|
||||
var _ rune = C.transform(x) // ERROR HERE: C\.int
|
||||
var x2 rune = '✈'
|
||||
var _ rune = C.transform(x2) // ERROR HERE: C\.int
|
||||
|
||||
// issue 13635: used to output error about C.unsignedchar.
|
||||
// This test tests all such types.
|
||||
@ -91,10 +91,10 @@ func main() {
|
||||
|
||||
// issue 26745
|
||||
_ = func(i int) int {
|
||||
return C.i + 1 // ERROR HERE: :13
|
||||
return C.i + 1 // ERROR HERE: 14
|
||||
}
|
||||
_ = func(i int) {
|
||||
C.fi(i) // ERROR HERE: :6
|
||||
C.fi(i) // ERROR HERE: 7
|
||||
}
|
||||
|
||||
C.fi = C.fi // ERROR HERE
|
||||
|
@ -9,6 +9,7 @@ package cgotest
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
@ -105,11 +106,23 @@ func compareStatus(filter, expect string) error {
|
||||
// "Pid:\t".
|
||||
}
|
||||
if strings.HasPrefix(line, filter) {
|
||||
if line != expected {
|
||||
return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
|
||||
if line == expected {
|
||||
foundAThread = true
|
||||
break
|
||||
}
|
||||
foundAThread = true
|
||||
break
|
||||
if filter == "Groups:" && strings.HasPrefix(line, "Groups:\t") {
|
||||
// https://github.com/golang/go/issues/46145
|
||||
// Containers don't reliably output this line in sorted order so manually sort and compare that.
|
||||
a := strings.Split(line[8:], " ")
|
||||
sort.Strings(a)
|
||||
got := strings.Join(a, " ")
|
||||
if got == expected[8:] {
|
||||
foundAThread = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,11 +292,60 @@ func createHeaders() error {
|
||||
"-installsuffix", "testcshared",
|
||||
"-o", libgoname,
|
||||
filepath.Join(".", "libgo", "libgo.go")}
|
||||
if GOOS == "windows" && strings.HasSuffix(args[6], ".a") {
|
||||
args[6] = strings.TrimSuffix(args[6], ".a") + ".dll"
|
||||
}
|
||||
cmd = exec.Command(args[0], args[1:]...)
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
|
||||
}
|
||||
if GOOS == "windows" {
|
||||
// We can't simply pass -Wl,--out-implib, because this relies on having imports from multiple packages,
|
||||
// which results in the linkers output implib getting overwritten at each step. So instead build the
|
||||
// import library the traditional way, using a def file.
|
||||
err = os.WriteFile("libgo.def",
|
||||
[]byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"),
|
||||
0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write def file: %v", err)
|
||||
}
|
||||
out, err = exec.Command(cc[0], append(cc[1:], "-print-prog-name=dlltool")...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find dlltool path: %v\n%s\n", err, out)
|
||||
}
|
||||
args := []string{strings.TrimSpace(string(out)), "-D", args[6], "-l", libgoname, "-d", "libgo.def"}
|
||||
|
||||
// This is an unfortunate workaround for https://github.com/mstorsjo/llvm-mingw/issues/205 in which
|
||||
// we basically reimplement the contents of the dlltool.sh wrapper: https://git.io/JZFlU
|
||||
dlltoolContents, err := os.ReadFile(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read dlltool: %v\n", err)
|
||||
}
|
||||
if bytes.HasPrefix(dlltoolContents, []byte("#!/bin/sh")) && bytes.Contains(dlltoolContents, []byte("llvm-dlltool")) {
|
||||
base, name := filepath.Split(args[0])
|
||||
args[0] = filepath.Join(base, "llvm-dlltool")
|
||||
var machine string
|
||||
switch strings.SplitN(name, "-", 2)[0] {
|
||||
case "i686":
|
||||
machine = "i386"
|
||||
case "x86_64":
|
||||
machine = "i386:x86-64"
|
||||
case "armv7":
|
||||
machine = "arm"
|
||||
case "aarch64":
|
||||
machine = "arm64"
|
||||
}
|
||||
if len(machine) > 0 {
|
||||
args = append(args, "-m", machine)
|
||||
}
|
||||
}
|
||||
|
||||
out, err = exec.Command(args[0], args[1:]...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to run dlltool to create import library: %v\n%s\n", err, out)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS != GOOS && GOOS == "android" {
|
||||
args = append(adbCmd(), "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname))
|
||||
@ -400,7 +449,7 @@ func main() {
|
||||
defer f.Close()
|
||||
section := f.Section(".edata")
|
||||
if section == nil {
|
||||
t.Fatalf(".edata section is not present")
|
||||
t.Skip(".edata section is not present")
|
||||
}
|
||||
|
||||
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
|
||||
@ -749,7 +798,12 @@ func TestGo2C2Go(t *testing.T) {
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
lib := filepath.Join(tmpdir, "libtestgo2c2go."+libSuffix)
|
||||
run(t, nil, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
|
||||
var env []string
|
||||
if GOOS == "windows" && strings.HasSuffix(lib, ".a") {
|
||||
env = append(env, "CGO_LDFLAGS=-Wl,--out-implib,"+lib, "CGO_LDFLAGS_ALLOW=.*")
|
||||
lib = strings.TrimSuffix(lib, ".a") + ".dll"
|
||||
}
|
||||
run(t, env, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
|
||||
|
||||
cgoCflags := os.Getenv("CGO_CFLAGS")
|
||||
if cgoCflags != "" {
|
||||
|
@ -263,6 +263,17 @@ func TestIssue25756(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test with main using -buildmode=pie with plugin for issue #43228
|
||||
func TestIssue25756pie(t *testing.T) {
|
||||
if os.Getenv("GO_BUILDER_NAME") == "darwin-arm64-11_0-toothrot" {
|
||||
t.Skip("broken on darwin/arm64 builder in sharded mode; see issue 46239")
|
||||
}
|
||||
|
||||
goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
|
||||
goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
|
||||
run(t, "./issue25756pie.exe")
|
||||
}
|
||||
|
||||
func TestMethod(t *testing.T) {
|
||||
// Exported symbol's method must be live.
|
||||
goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
|
||||
|
@ -401,6 +401,7 @@
|
||||
storeValue(sp + 56, result);
|
||||
this.mem.setUint8(sp + 64, 1);
|
||||
} catch (err) {
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 56, err);
|
||||
this.mem.setUint8(sp + 64, 0);
|
||||
}
|
||||
@ -417,6 +418,7 @@
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
@ -433,6 +435,7 @@
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
|
@ -96,7 +96,15 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
|
||||
return err
|
||||
}
|
||||
z.r = r
|
||||
z.File = make([]*File, 0, end.directoryRecords)
|
||||
// Since the number of directory records is not validated, it is not
|
||||
// safe to preallocate z.File without first checking that the specified
|
||||
// number of files is reasonable, since a malformed archive may
|
||||
// indicate it contains up to 1 << 128 - 1 files. Since each file has a
|
||||
// header which will be _at least_ 30 bytes we can safely preallocate
|
||||
// if (data size / 30) >= end.directoryRecords.
|
||||
if (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
|
||||
z.File = make([]*File, 0, end.directoryRecords)
|
||||
}
|
||||
z.Comment = end.comment
|
||||
rs := io.NewSectionReader(r, 0, size)
|
||||
if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil {
|
||||
|
@ -1325,3 +1325,62 @@ func TestReadDataDescriptor(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCVE202133196(t *testing.T) {
|
||||
// Archive that indicates it has 1 << 128 -1 files,
|
||||
// this would previously cause a panic due to attempting
|
||||
// to allocate a slice with 1 << 128 -1 elements.
|
||||
data := []byte{
|
||||
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
|
||||
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02,
|
||||
0x03, 0x62, 0x61, 0x65, 0x03, 0x04, 0x00, 0x00,
|
||||
0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xbe, 0x20,
|
||||
0x5c, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
|
||||
0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00,
|
||||
0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xbe, 0x20, 0x5c, 0x6c, 0x09, 0x00,
|
||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x02, 0x03, 0x50, 0x4b, 0x06, 0x06, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
|
||||
0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
|
||||
0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x00, 0x00,
|
||||
}
|
||||
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
if err != ErrFormat {
|
||||
t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
|
||||
}
|
||||
|
||||
// Also check that an archive containing a handful of empty
|
||||
// files doesn't cause an issue
|
||||
b := bytes.NewBuffer(nil)
|
||||
w := NewWriter(b)
|
||||
for i := 0; i < 5; i++ {
|
||||
_, err := w.Create("")
|
||||
if err != nil {
|
||||
t.Fatalf("Writer.Create failed: %s", err)
|
||||
}
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatalf("Writer.Close failed: %s", err)
|
||||
}
|
||||
r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
|
||||
if err != nil {
|
||||
t.Fatalf("NewReader failed: %s", err)
|
||||
}
|
||||
if len(r.File) != 5 {
|
||||
t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
|
||||
}
|
||||
}
|
||||
|
@ -1003,7 +1003,8 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
|
||||
p.errorf("unimplemented two-register form")
|
||||
}
|
||||
a.Index = r1
|
||||
if scale != 0 && p.arch.Family == sys.ARM64 {
|
||||
if scale != 0 && scale != 1 && p.arch.Family == sys.ARM64 {
|
||||
// Support (R1)(R2) (no scaling) and (R1)(R2*1).
|
||||
p.errorf("arm64 doesn't support scaled register format")
|
||||
} else {
|
||||
a.Scale = int16(scale)
|
||||
|
5
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
5
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
@ -89,7 +89,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||
CMP R1<<33, R2
|
||||
CMP R22.SXTX, RSP // ffe336eb
|
||||
CMP $0x22220000, RSP // CMP $572653568, RSP // 5b44a4d2ff633beb
|
||||
CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff633b6b
|
||||
CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff433b6b
|
||||
CCMN MI, ZR, R1, $4 // e44341ba
|
||||
// MADD Rn,Rm,Ra,Rd
|
||||
MADD R1, R2, R3, R4 // 6408019b
|
||||
@ -377,6 +377,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||
MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2
|
||||
MOVW $0x10001000, RSP // MOVW $268439552, RSP // ff830432
|
||||
ADDW $0x10001000, R1 // ADDW $268439552, R1 // fb83043221001b0b
|
||||
ADDW $0x22220000, RSP, R3 // ADDW $572653568, RSP, R3 // 5b44a452e3433b0b
|
||||
|
||||
// move a large constant to a Vd.
|
||||
VMOVS $0x80402010, V11 // VMOVS $2151686160, V11
|
||||
@ -547,6 +548,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||
// shifted or extended register offset.
|
||||
MOVD (R2)(R6.SXTW), R4 // 44c866f8
|
||||
MOVD (R3)(R6), R5 // 656866f8
|
||||
MOVD (R3)(R6*1), R5 // 656866f8
|
||||
MOVD (R2)(R6), R4 // 446866f8
|
||||
MOVWU (R19)(R20<<2), R20 // 747a74b8
|
||||
MOVD (R2)(R6<<3), R4 // 447866f8
|
||||
@ -579,6 +581,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||
MOVB R4, (R2)(R6.SXTX) // 44e82638
|
||||
MOVB R8, (R3)(R9.UXTW) // 68482938
|
||||
MOVB R10, (R5)(R8) // aa682838
|
||||
MOVB R10, (R5)(R8*1) // aa682838
|
||||
MOVH R11, (R2)(R7.SXTW<<1) // 4bd82778
|
||||
MOVH R5, (R1)(R2<<1) // 25782278
|
||||
MOVH R7, (R2)(R5.SXTX<<1) // 47f82578
|
||||
|
10
src/cmd/asm/internal/asm/testdata/arm64error.s
vendored
10
src/cmd/asm/internal/asm/testdata/arm64error.s
vendored
@ -52,6 +52,16 @@ TEXT errors(SB),$0
|
||||
NEGSW R7@>2, R5 // ERROR "unsupported shift operator"
|
||||
CINC CS, R2, R3, R4 // ERROR "illegal combination"
|
||||
CSEL LT, R1, R2 // ERROR "illegal combination"
|
||||
CINC AL, R2, R3 // ERROR "invalid condition"
|
||||
CINC NV, R2, R3 // ERROR "invalid condition"
|
||||
CINVW AL, R2, R3 // ERROR "invalid condition"
|
||||
CINV NV, R2, R3 // ERROR "invalid condition"
|
||||
CNEG AL, R2, R3 // ERROR "invalid condition"
|
||||
CNEGW NV, R2, R3 // ERROR "invalid condition"
|
||||
CSET AL, R2 // ERROR "invalid condition"
|
||||
CSET NV, R2 // ERROR "invalid condition"
|
||||
CSETMW AL, R2 // ERROR "invalid condition"
|
||||
CSETM NV, R2 // ERROR "invalid condition"
|
||||
LDP.P 8(R2), (R2, R3) // ERROR "constrained unpredictable behavior"
|
||||
LDP.W 8(R3), (R2, R3) // ERROR "constrained unpredictable behavior"
|
||||
LDP (R1), (R2, R2) // ERROR "constrained unpredictable behavior"
|
||||
|
4
src/cmd/asm/internal/asm/testdata/ppc64.s
vendored
4
src/cmd/asm/internal/asm/testdata/ppc64.s
vendored
@ -41,8 +41,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
|
||||
MOVDBR (R3)(R4), R5 // 7ca41c28
|
||||
MOVWBR (R3)(R4), R5 // 7ca41c2c
|
||||
MOVHBR (R3)(R4), R5 // 7ca41e2c
|
||||
MOVD $foo+4009806848(FP), R5 // 3fe1ef0138bfcc20
|
||||
MOVD $foo(SB), R5 // 3fe0000038bf0000
|
||||
MOVD $foo+4009806848(FP), R5 // 3ca1ef0138a5cc20
|
||||
MOVD $foo(SB), R5 // 3ca0000038a50000
|
||||
|
||||
MOVDU 8(R3), R4 // e8830009
|
||||
MOVDU (R3)(R4), R5 // 7ca4186a
|
||||
|
@ -1638,6 +1638,8 @@ func (p *Package) gccCmd() []string {
|
||||
c = append(c, "-maix64")
|
||||
c = append(c, "-mcmodel=large")
|
||||
}
|
||||
// disable LTO so we get an object whose symbols we can read
|
||||
c = append(c, "-fno-lto")
|
||||
c = append(c, "-") //read input from standard input
|
||||
return c
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Cgo; see gmp.go for an overview.
|
||||
// Cgo; see doc.go for an overview.
|
||||
|
||||
// TODO(rsc):
|
||||
// Emit correct line number annotations.
|
||||
|
@ -168,8 +168,18 @@ func (p *Package) writeDefs() {
|
||||
if *gccgo {
|
||||
fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
|
||||
} else {
|
||||
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
|
||||
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
|
||||
// Force a reference to all symbols so that
|
||||
// the external linker will add DT_NEEDED
|
||||
// entries as needed on ELF systems.
|
||||
// Treat function variables differently
|
||||
// to avoid type confict errors from LTO
|
||||
// (Link Time Optimization).
|
||||
if n.Kind == "fpvar" {
|
||||
fmt.Fprintf(fm, "extern void %s();\n", n.C)
|
||||
} else {
|
||||
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
|
||||
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
|
||||
}
|
||||
fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C)
|
||||
fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C)
|
||||
fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C)
|
||||
@ -1042,7 +1052,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
||||
// This unpacks the argument struct above and calls the Go function.
|
||||
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype)
|
||||
|
||||
fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName)
|
||||
fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
|
||||
|
||||
if gccResult != "void" {
|
||||
// Write results back to frame.
|
||||
|
@ -233,7 +233,7 @@ stack frame is laid out in the following sequence:
|
||||
r1.x uintptr
|
||||
r1.y [2]uintptr
|
||||
a1Spill uint8
|
||||
a2Spill uint8
|
||||
a3Spill uint8
|
||||
_ [6]uint8 // alignment padding
|
||||
|
||||
In the stack frame, only the `a2` field is initialized on entry; the
|
||||
@ -402,7 +402,7 @@ without corrupting arguments or results.
|
||||
Special-purpose registers are as follows:
|
||||
|
||||
| Register | Call meaning | Return meaning | Body meaning |
|
||||
| --- | --- | --- |
|
||||
| --- | --- | --- | --- |
|
||||
| RSP | Stack pointer | Same | Same |
|
||||
| RBP | Frame pointer | Same | Same |
|
||||
| RDX | Closure context pointer | Scratch | Scratch |
|
||||
|
@ -446,35 +446,20 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResul
|
||||
return result
|
||||
}
|
||||
|
||||
// parameterUpdateMu protects the Offset field of function/method parameters (a subset of structure Fields)
|
||||
var parameterUpdateMu sync.Mutex
|
||||
|
||||
// FieldOffsetOf returns a concurency-safe version of f.Offset
|
||||
func FieldOffsetOf(f *types.Field) int64 {
|
||||
parameterUpdateMu.Lock()
|
||||
defer parameterUpdateMu.Unlock()
|
||||
return f.Offset
|
||||
}
|
||||
|
||||
func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn, setNname bool) {
|
||||
// Everything except return values in registers has either a frame home (if not in a register) or a frame spill location.
|
||||
if !isReturn || len(a.Registers) == 0 {
|
||||
// The type frame offset DOES NOT show effects of minimum frame size.
|
||||
// Getting this wrong breaks stackmaps, see liveness/plive.go:WriteFuncMap and typebits/typebits.go:Set
|
||||
parameterUpdateMu.Lock()
|
||||
defer parameterUpdateMu.Unlock()
|
||||
off := a.FrameOffset(result)
|
||||
fOffset := f.Offset
|
||||
if fOffset == types.BOGUS_FUNARG_OFFSET {
|
||||
// Set the Offset the first time. After that, we may recompute it, but it should never change.
|
||||
f.Offset = off
|
||||
if f.Nname != nil {
|
||||
// always set it in this case.
|
||||
if setNname && f.Nname != nil {
|
||||
f.Nname.(*ir.Name).SetFrameOffset(off)
|
||||
f.Nname.(*ir.Name).SetIsOutputParamInRegisters(false)
|
||||
}
|
||||
} else if fOffset != off {
|
||||
base.Fatalf("offset for %s at %s changed from %d to %d", f.Sym.Name, base.FmtPos(f.Pos), fOffset, off)
|
||||
} else {
|
||||
base.Fatalf("field offset for %s at %s has been set to %d", f.Sym.Name, base.FmtPos(f.Pos), fOffset)
|
||||
}
|
||||
} else {
|
||||
if setNname && f.Nname != nil {
|
||||
|
@ -222,9 +222,64 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
|
||||
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
|
||||
}
|
||||
|
||||
// Sort decls and vars.
|
||||
sortDeclsAndVars(fn, decls, vars)
|
||||
|
||||
return decls, vars
|
||||
}
|
||||
|
||||
// sortDeclsAndVars sorts the decl and dwarf var lists according to
|
||||
// parameter declaration order, so as to insure that when a subprogram
|
||||
// DIE is emitted, its parameter children appear in declaration order.
|
||||
// Prior to the advent of the register ABI, sorting by frame offset
|
||||
// would achieve this; with the register we now need to go back to the
|
||||
// original function signature.
|
||||
func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) {
|
||||
paramOrder := make(map[*ir.Name]int)
|
||||
idx := 1
|
||||
for _, selfn := range types.RecvsParamsResults {
|
||||
fsl := selfn(fn.Type()).FieldSlice()
|
||||
for _, f := range fsl {
|
||||
if n, ok := f.Nname.(*ir.Name); ok {
|
||||
paramOrder[n] = idx
|
||||
idx++
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Stable(varsAndDecls{decls, vars, paramOrder})
|
||||
}
|
||||
|
||||
type varsAndDecls struct {
|
||||
decls []*ir.Name
|
||||
vars []*dwarf.Var
|
||||
paramOrder map[*ir.Name]int
|
||||
}
|
||||
|
||||
func (v varsAndDecls) Len() int {
|
||||
return len(v.decls)
|
||||
}
|
||||
|
||||
func (v varsAndDecls) Less(i, j int) bool {
|
||||
nameLT := func(ni, nj *ir.Name) bool {
|
||||
oi, foundi := v.paramOrder[ni]
|
||||
oj, foundj := v.paramOrder[nj]
|
||||
if foundi {
|
||||
if foundj {
|
||||
return oi < oj
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return nameLT(v.decls[i], v.decls[j])
|
||||
}
|
||||
|
||||
func (v varsAndDecls) Swap(i, j int) {
|
||||
v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
|
||||
v.decls[i], v.decls[j] = v.decls[j], v.decls[i]
|
||||
}
|
||||
|
||||
// Given a function that was inlined at some point during the
|
||||
// compilation, return a sorted list of nodes corresponding to the
|
||||
// autos/locals in that function prior to inlining. If this is a
|
||||
@ -476,6 +531,14 @@ func RecordFlags(flags ...string) {
|
||||
fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
|
||||
}
|
||||
|
||||
// Adds flag to producer string singalling whether regabi is turned on or
|
||||
// off.
|
||||
// Once regabi is turned on across the board and the relative GOEXPERIMENT
|
||||
// knobs no longer exist this code should be removed.
|
||||
if buildcfg.Experiment.RegabiArgs {
|
||||
cmd.Write([]byte(" regabi"))
|
||||
}
|
||||
|
||||
if cmd.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -2013,14 +2013,14 @@ func HeapAllocReason(n ir.Node) string {
|
||||
return "too large for stack"
|
||||
}
|
||||
|
||||
if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width >= ir.MaxImplicitStackVarSize {
|
||||
if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width > ir.MaxImplicitStackVarSize {
|
||||
return "too large for stack"
|
||||
}
|
||||
|
||||
if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() >= ir.MaxImplicitStackVarSize {
|
||||
if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
|
||||
return "too large for stack"
|
||||
}
|
||||
if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() >= ir.MaxImplicitStackVarSize {
|
||||
if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
|
||||
return "too large for stack"
|
||||
}
|
||||
|
||||
@ -2033,7 +2033,7 @@ func HeapAllocReason(n ir.Node) string {
|
||||
if !ir.IsSmallIntConst(r) {
|
||||
return "non-constant size"
|
||||
}
|
||||
if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) >= ir.MaxImplicitStackVarSize/t.Elem().Width {
|
||||
if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Width {
|
||||
return "too large for stack"
|
||||
}
|
||||
}
|
||||
|
@ -119,38 +119,51 @@ func compileFunctions() {
|
||||
})
|
||||
}
|
||||
|
||||
// We queue up a goroutine per function that needs to be
|
||||
// compiled, but require them to grab an available worker ID
|
||||
// before doing any substantial work to limit parallelism.
|
||||
workerIDs := make(chan int, base.Flag.LowerC)
|
||||
for i := 0; i < base.Flag.LowerC; i++ {
|
||||
workerIDs <- i
|
||||
// By default, we perform work right away on the current goroutine
|
||||
// as the solo worker.
|
||||
queue := func(work func(int)) {
|
||||
work(0)
|
||||
}
|
||||
|
||||
if nWorkers := base.Flag.LowerC; nWorkers > 1 {
|
||||
// For concurrent builds, we create a goroutine per task, but
|
||||
// require them to hold a unique worker ID while performing work
|
||||
// to limit parallelism.
|
||||
workerIDs := make(chan int, nWorkers)
|
||||
for i := 0; i < nWorkers; i++ {
|
||||
workerIDs <- i
|
||||
}
|
||||
|
||||
queue = func(work func(int)) {
|
||||
go func() {
|
||||
worker := <-workerIDs
|
||||
work(worker)
|
||||
workerIDs <- worker
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var asyncCompile func(*ir.Func)
|
||||
asyncCompile = func(fn *ir.Func) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
worker := <-workerIDs
|
||||
ssagen.Compile(fn, worker)
|
||||
workerIDs <- worker
|
||||
|
||||
// Done compiling fn. Schedule it's closures for compilation.
|
||||
for _, closure := range fn.Closures {
|
||||
asyncCompile(closure)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
var compile func([]*ir.Func)
|
||||
compile = func(fns []*ir.Func) {
|
||||
wg.Add(len(fns))
|
||||
for _, fn := range fns {
|
||||
fn := fn
|
||||
queue(func(worker int) {
|
||||
ssagen.Compile(fn, worker)
|
||||
compile(fn.Closures)
|
||||
wg.Done()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
|
||||
base.Ctxt.InParallel = true
|
||||
for _, fn := range compilequeue {
|
||||
asyncCompile(fn)
|
||||
}
|
||||
|
||||
compile(compilequeue)
|
||||
compilequeue = nil
|
||||
wg.Wait()
|
||||
|
||||
base.Ctxt.InParallel = false
|
||||
types.CalcSizeDisabled = false
|
||||
}
|
||||
|
@ -148,6 +148,7 @@ func dumpdata() {
|
||||
if reflectdata.ZeroSize > 0 {
|
||||
zero := base.PkgLinksym("go.map", "zero", obj.ABI0)
|
||||
objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA)
|
||||
zero.Set(obj.AttrStatic, true)
|
||||
}
|
||||
|
||||
staticdata.WriteFuncSyms()
|
||||
|
@ -1124,6 +1124,10 @@ type inlsubst struct {
|
||||
newclofn *ir.Func
|
||||
|
||||
fn *ir.Func // For debug -- the func that is being inlined
|
||||
|
||||
// If true, then don't update source positions during substitution
|
||||
// (retain old source positions).
|
||||
noPosUpdate bool
|
||||
}
|
||||
|
||||
// list inlines a list of nodes.
|
||||
@ -1219,7 +1223,14 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
|
||||
// closure node.
|
||||
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
||||
m := ir.Copy(n)
|
||||
m.SetPos(subst.updatedPos(m.Pos()))
|
||||
|
||||
// Prior to the subst edit, set a flag in the inlsubst to
|
||||
// indicated that we don't want to update the source positions in
|
||||
// the new closure. If we do this, it will appear that the closure
|
||||
// itself has things inlined into it, which is not the case. See
|
||||
// issue #46234 for more details.
|
||||
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
|
||||
subst.noPosUpdate = true
|
||||
ir.EditChildren(m, subst.edit)
|
||||
|
||||
//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
|
||||
@ -1445,6 +1456,9 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
|
||||
}
|
||||
|
||||
func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
|
||||
if subst.noPosUpdate {
|
||||
return xpos
|
||||
}
|
||||
pos := base.Ctxt.PosTable.Pos(xpos)
|
||||
oldbase := pos.Base() // can be nil
|
||||
newbase := subst.bases[oldbase]
|
||||
|
@ -882,9 +882,6 @@ func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
if _, ok := n.(ir.Ntype); !ok {
|
||||
ir.Dump("NOT NTYPE", n)
|
||||
}
|
||||
return n.(ir.Ntype)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ package reflectdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"sort"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
@ -47,6 +48,11 @@ func eqCanPanic(t *types.Type) bool {
|
||||
func AlgType(t *types.Type) types.AlgKind {
|
||||
a, _ := types.AlgType(t)
|
||||
if a == types.AMEM {
|
||||
if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Width {
|
||||
// For example, we can't treat [2]int16 as an int32 if int32s require
|
||||
// 4-byte alignment. See issue 46283.
|
||||
return a
|
||||
}
|
||||
switch t.Width {
|
||||
case 0:
|
||||
return types.AMEM0
|
||||
@ -769,6 +775,20 @@ func memrun(t *types.Type, start int) (size int64, next int) {
|
||||
if f := t.Field(next); f.Sym.IsBlank() || !isRegularMemory(f.Type) {
|
||||
break
|
||||
}
|
||||
// For issue 46283, don't combine fields if the resulting load would
|
||||
// require a larger alignment than the component fields.
|
||||
if base.Ctxt.Arch.Alignment > 1 {
|
||||
align := t.Alignment()
|
||||
if off := t.Field(start).Offset; off&(align-1) != 0 {
|
||||
// Offset is less aligned than the containing type.
|
||||
// Use offset to determine alignment.
|
||||
align = 1 << uint(bits.TrailingZeros64(uint64(off)))
|
||||
}
|
||||
size := t.Field(next).End() - t.Field(start).Offset
|
||||
if size > align {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return t.Field(next-1).End() - t.Field(start).Offset, next
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package reflectdata
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"internal/buildcfg"
|
||||
"os"
|
||||
@ -473,21 +474,25 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int {
|
||||
|
||||
// dnameData writes the contents of a reflect.name into s at offset ot.
|
||||
func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int {
|
||||
if len(name) > 1<<16-1 {
|
||||
base.Fatalf("name too long: %s", name)
|
||||
if len(name) >= 1<<29 {
|
||||
base.Fatalf("name too long: %d %s...", len(name), name[:1024])
|
||||
}
|
||||
if len(tag) > 1<<16-1 {
|
||||
base.Fatalf("tag too long: %s", tag)
|
||||
if len(tag) >= 1<<29 {
|
||||
base.Fatalf("tag too long: %d %s...", len(tag), tag[:1024])
|
||||
}
|
||||
var nameLen [binary.MaxVarintLen64]byte
|
||||
nameLenLen := binary.PutUvarint(nameLen[:], uint64(len(name)))
|
||||
var tagLen [binary.MaxVarintLen64]byte
|
||||
tagLenLen := binary.PutUvarint(tagLen[:], uint64(len(tag)))
|
||||
|
||||
// Encode name and tag. See reflect/type.go for details.
|
||||
var bits byte
|
||||
l := 1 + 2 + len(name)
|
||||
l := 1 + nameLenLen + len(name)
|
||||
if exported {
|
||||
bits |= 1 << 0
|
||||
}
|
||||
if len(tag) > 0 {
|
||||
l += 2 + len(tag)
|
||||
l += tagLenLen + len(tag)
|
||||
bits |= 1 << 1
|
||||
}
|
||||
if pkg != nil {
|
||||
@ -495,14 +500,12 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b
|
||||
}
|
||||
b := make([]byte, l)
|
||||
b[0] = bits
|
||||
b[1] = uint8(len(name) >> 8)
|
||||
b[2] = uint8(len(name))
|
||||
copy(b[3:], name)
|
||||
copy(b[1:], nameLen[:nameLenLen])
|
||||
copy(b[1+nameLenLen:], name)
|
||||
if len(tag) > 0 {
|
||||
tb := b[3+len(name):]
|
||||
tb[0] = uint8(len(tag) >> 8)
|
||||
tb[1] = uint8(len(tag))
|
||||
copy(tb[2:], tag)
|
||||
tb := b[1+nameLenLen+len(name):]
|
||||
copy(tb, tagLen[:tagLenLen])
|
||||
copy(tb[tagLenLen:], tag)
|
||||
}
|
||||
|
||||
ot = int(s.WriteBytes(base.Ctxt, int64(ot), b))
|
||||
@ -666,7 +669,7 @@ var kinds = []int{
|
||||
// tflag is documented in reflect/type.go.
|
||||
//
|
||||
// tflag values must be kept in sync with copies in:
|
||||
// cmd/compile/internal/gc/reflect.go
|
||||
// cmd/compile/internal/reflectdata/reflect.go
|
||||
// cmd/link/internal/ld/decodesym.go
|
||||
// reflect/type.go
|
||||
// runtime/type.go
|
||||
@ -1109,6 +1112,15 @@ func writeType(t *types.Type) *obj.LSym {
|
||||
}
|
||||
ot = objw.Uint32(lsym, ot, flags)
|
||||
ot = dextratype(lsym, ot, t, 0)
|
||||
if u := t.Underlying(); u != t {
|
||||
// If t is a named map type, also keep the underlying map
|
||||
// type live in the binary. This is important to make sure that
|
||||
// a named map and that same map cast to its underlying type via
|
||||
// reflection, use the same hash function. See issue 37716.
|
||||
r := obj.Addrel(lsym)
|
||||
r.Sym = writeType(u)
|
||||
r.Type = objabi.R_KEEP
|
||||
}
|
||||
|
||||
case types.TPTR:
|
||||
if t.Elem().Kind() == types.TANY {
|
||||
@ -1490,8 +1502,8 @@ func (a typesByString) Less(i, j int) bool {
|
||||
// will be equal for the above checks, but different in DWARF output.
|
||||
// Sort by source position to ensure deterministic order.
|
||||
// See issues 27013 and 30202.
|
||||
if a[i].t.Kind() == types.TINTER && a[i].t.Methods().Len() > 0 {
|
||||
return a[i].t.Methods().Index(0).Pos.Before(a[j].t.Methods().Index(0).Pos)
|
||||
if a[i].t.Kind() == types.TINTER && a[i].t.AllMethods().Len() > 0 {
|
||||
return a[i].t.AllMethods().Index(0).Pos.Before(a[j].t.AllMethods().Index(0).Pos)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -147,13 +147,6 @@ type Frontend interface {
|
||||
|
||||
// Given the name for a compound type, returns the name we should use
|
||||
// for the parts of that compound type.
|
||||
SplitString(LocalSlot) (LocalSlot, LocalSlot)
|
||||
SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
|
||||
SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
|
||||
SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
|
||||
SplitStruct(LocalSlot, int) LocalSlot
|
||||
SplitArray(LocalSlot) LocalSlot // array must be length 1
|
||||
SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
|
||||
SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot
|
||||
|
||||
// DerefItab dereferences an itab function
|
||||
|
@ -26,7 +26,7 @@ func copyelim(f *Func) {
|
||||
|
||||
// Update named values.
|
||||
for _, name := range f.Names {
|
||||
values := f.NamedValues[name]
|
||||
values := f.NamedValues[*name]
|
||||
for i, v := range values {
|
||||
if v.Op == OpCopy {
|
||||
values[i] = v.Args[0]
|
||||
|
@ -223,7 +223,7 @@ func deadcode(f *Func) {
|
||||
for _, name := range f.Names {
|
||||
j := 0
|
||||
s.clear()
|
||||
values := f.NamedValues[name]
|
||||
values := f.NamedValues[*name]
|
||||
for _, v := range values {
|
||||
if live[v.ID] && !s.contains(v.ID) {
|
||||
values[j] = v
|
||||
@ -232,19 +232,19 @@ func deadcode(f *Func) {
|
||||
}
|
||||
}
|
||||
if j == 0 {
|
||||
delete(f.NamedValues, name)
|
||||
delete(f.NamedValues, *name)
|
||||
} else {
|
||||
f.Names[i] = name
|
||||
i++
|
||||
for k := len(values) - 1; k >= j; k-- {
|
||||
values[k] = nil
|
||||
}
|
||||
f.NamedValues[name] = values[:j]
|
||||
f.NamedValues[*name] = values[:j]
|
||||
}
|
||||
}
|
||||
clearNames := f.Names[i:]
|
||||
for j := range clearNames {
|
||||
clearNames[j] = LocalSlot{}
|
||||
clearNames[j] = nil
|
||||
}
|
||||
f.Names = f.Names[:i]
|
||||
|
||||
|
@ -7,10 +7,13 @@ package ssa
|
||||
import (
|
||||
"cmd/compile/internal/abi"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/dwarf"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/src"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"internal/buildcfg"
|
||||
"math/bits"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -335,6 +338,216 @@ func (s *debugState) stateString(state stateAtPC) string {
|
||||
return strings.Join(strs, "")
|
||||
}
|
||||
|
||||
// slotCanonicalizer is a table used to lookup and canonicalize
|
||||
// LocalSlot's in a type insensitive way (e.g. taking into account the
|
||||
// base name, offset, and width of the slot, but ignoring the slot
|
||||
// type).
|
||||
type slotCanonicalizer struct {
|
||||
slmap map[slotKey]SlKeyIdx
|
||||
slkeys []LocalSlot
|
||||
}
|
||||
|
||||
func newSlotCanonicalizer() *slotCanonicalizer {
|
||||
return &slotCanonicalizer{
|
||||
slmap: make(map[slotKey]SlKeyIdx),
|
||||
slkeys: []LocalSlot{LocalSlot{N: nil}},
|
||||
}
|
||||
}
|
||||
|
||||
type SlKeyIdx uint32
|
||||
|
||||
const noSlot = SlKeyIdx(0)
|
||||
|
||||
// slotKey is a type-insensitive encapsulation of a LocalSlot; it
|
||||
// is used to key a map within slotCanonicalizer.
|
||||
type slotKey struct {
|
||||
name *ir.Name
|
||||
offset int64
|
||||
width int64
|
||||
splitOf SlKeyIdx // idx in slkeys slice in slotCanonicalizer
|
||||
splitOffset int64
|
||||
}
|
||||
|
||||
// lookup looks up a LocalSlot in the slot canonicalizer "sc", returning
|
||||
// a canonical index for the slot, and adding it to the table if need
|
||||
// be. Return value is the canonical slot index, and a boolean indicating
|
||||
// whether the slot was found in the table already (TRUE => found).
|
||||
func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) {
|
||||
split := noSlot
|
||||
if ls.SplitOf != nil {
|
||||
split, _ = sc.lookup(*ls.SplitOf)
|
||||
}
|
||||
k := slotKey{
|
||||
name: ls.N, offset: ls.Off, width: ls.Type.Width,
|
||||
splitOf: split, splitOffset: ls.SplitOffset,
|
||||
}
|
||||
if idx, ok := sc.slmap[k]; ok {
|
||||
return idx, true
|
||||
}
|
||||
rv := SlKeyIdx(len(sc.slkeys))
|
||||
sc.slkeys = append(sc.slkeys, ls)
|
||||
sc.slmap[k] = rv
|
||||
return rv, false
|
||||
}
|
||||
|
||||
func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot {
|
||||
return sc.slkeys[idx]
|
||||
}
|
||||
|
||||
// PopulateABIInRegArgOps examines the entry block of the function
|
||||
// and looks for incoming parameters that have missing or partial
|
||||
// OpArg{Int,Float}Reg values, inserting additional values in
|
||||
// cases where they are missing. Example:
|
||||
//
|
||||
// func foo(s string, used int, notused int) int {
|
||||
// return len(s) + used
|
||||
// }
|
||||
//
|
||||
// In the function above, the incoming parameter "used" is fully live,
|
||||
// "notused" is not live, and "s" is partially live (only the length
|
||||
// field of the string is used). At the point where debug value
|
||||
// analysis runs, we might expect to see an entry block with:
|
||||
//
|
||||
// b1:
|
||||
// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
|
||||
// v5 = ArgIntReg <int> {used} [0] : CX
|
||||
//
|
||||
// While this is an accurate picture of the live incoming params,
|
||||
// we also want to have debug locations for non-live params (or
|
||||
// their non-live pieces), e.g. something like
|
||||
//
|
||||
// b1:
|
||||
// v9 = ArgIntReg <*uint8> {s+0} [0] : AX
|
||||
// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
|
||||
// v5 = ArgIntReg <int> {used} [0] : CX
|
||||
// v10 = ArgIntReg <int> {unused} [0] : DI
|
||||
//
|
||||
// This function examines the live OpArg{Int,Float}Reg values and
|
||||
// synthesizes new (dead) values for the non-live params or the
|
||||
// non-live pieces of partially live params.
|
||||
//
|
||||
func PopulateABIInRegArgOps(f *Func) {
|
||||
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())
|
||||
|
||||
// When manufacturing new slots that correspond to splits of
|
||||
// composite parameters, we want to avoid creating a new sub-slot
|
||||
// that differs from some existing sub-slot only by type, since
|
||||
// the debug location analysis will treat that slot as a separate
|
||||
// entity. To achieve this, create a lookup table of existing
|
||||
// slots that is type-insenstitive.
|
||||
sc := newSlotCanonicalizer()
|
||||
for _, sl := range f.Names {
|
||||
sc.lookup(*sl)
|
||||
}
|
||||
|
||||
// Add slot -> value entry to f.NamedValues if not already present.
|
||||
addToNV := func(v *Value, sl LocalSlot) {
|
||||
values, ok := f.NamedValues[sl]
|
||||
if !ok {
|
||||
// Haven't seen this slot yet.
|
||||
sla := f.localSlotAddr(sl)
|
||||
f.Names = append(f.Names, sla)
|
||||
} else {
|
||||
for _, ev := range values {
|
||||
if v == ev {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
values = append(values, v)
|
||||
f.NamedValues[sl] = values
|
||||
}
|
||||
|
||||
newValues := []*Value{}
|
||||
|
||||
abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
|
||||
i := f.ABISelf.FloatIndexFor(reg)
|
||||
if i >= 0 { // float PR
|
||||
return f.Config.floatParamRegs[i]
|
||||
} else {
|
||||
return f.Config.intParamRegs[reg]
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to construct a new OpArg{Float,Int}Reg op value.
|
||||
var pos src.XPos
|
||||
if len(f.Entry.Values) != 0 {
|
||||
pos = f.Entry.Values[0].Pos
|
||||
}
|
||||
synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value {
|
||||
aux := &AuxNameOffset{n, sl.Off}
|
||||
op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf)
|
||||
v := f.newValueNoBlock(op, t, pos)
|
||||
v.AuxInt = auxInt
|
||||
v.Aux = aux
|
||||
v.Args = nil
|
||||
v.Block = f.Entry
|
||||
newValues = append(newValues, v)
|
||||
addToNV(v, sl)
|
||||
f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)])
|
||||
return v
|
||||
}
|
||||
|
||||
// Make a pass through the entry block looking for
|
||||
// OpArg{Int,Float}Reg ops. Record the slots they use in a table
|
||||
// ("sc"). We use a type-insensitive lookup for the slot table,
|
||||
// since the type we get from the ABI analyzer won't always match
|
||||
// what the compiler uses when creating OpArg{Int,Float}Reg ops.
|
||||
for _, v := range f.Entry.Values {
|
||||
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
|
||||
aux := v.Aux.(*AuxNameOffset)
|
||||
sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
|
||||
// install slot in lookup table
|
||||
idx, _ := sc.lookup(sl)
|
||||
// add to f.NamedValues if not already present
|
||||
addToNV(v, sc.canonSlot(idx))
|
||||
} else if v.Op.IsCall() {
|
||||
// if we hit a call, we've gone too far.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Now make a pass through the ABI in-params, looking for params
|
||||
// or pieces of params that we didn't encounter in the loop above.
|
||||
for _, inp := range pri.InParams() {
|
||||
if !isNamedRegParam(inp) {
|
||||
continue
|
||||
}
|
||||
n := inp.Name.(*ir.Name)
|
||||
|
||||
// Param is spread across one or more registers. Walk through
|
||||
// each piece to see whether we've seen an arg reg op for it.
|
||||
types, offsets := inp.RegisterTypesAndOffsets()
|
||||
for k, t := range types {
|
||||
// Note: this recipe for creating a LocalSlot is designed
|
||||
// to be compatible with the one used in expand_calls.go
|
||||
// as opposed to decompose.go. The expand calls code just
|
||||
// takes the base name and creates an offset into it,
|
||||
// without using the SplitOf/SplitOffset fields. The code
|
||||
// in decompose.go does the opposite -- it creates a
|
||||
// LocalSlot object with "Off" set to zero, but with
|
||||
// SplitOf pointing to a parent slot, and SplitOffset
|
||||
// holding the offset into the parent object.
|
||||
pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]}
|
||||
|
||||
// Look up this piece to see if we've seen a reg op
|
||||
// for it. If not, create one.
|
||||
_, found := sc.lookup(pieceSlot)
|
||||
if !found {
|
||||
// This slot doesn't appear in the map, meaning it
|
||||
// corresponds to an in-param that is not live, or
|
||||
// a portion of an in-param that is not live/used.
|
||||
// Add a new dummy OpArg{Int,Float}Reg for it.
|
||||
synthesizeOpIntFloatArg(n, t, inp.Registers[k],
|
||||
pieceSlot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the new values into the head of the block.
|
||||
f.Entry.Values = append(newValues, f.Entry.Values...)
|
||||
}
|
||||
|
||||
// BuildFuncDebug returns debug information for f.
|
||||
// f must be fully processed, so that each Value is where it will be when
|
||||
// machine code is emitted.
|
||||
@ -349,6 +562,10 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
|
||||
state.stackOffset = stackOffset
|
||||
state.ctxt = ctxt
|
||||
|
||||
if buildcfg.Experiment.RegabiArgs {
|
||||
PopulateABIInRegArgOps(f)
|
||||
}
|
||||
|
||||
if state.loggingEnabled {
|
||||
state.logf("Generating location lists for function %q\n", f.Name)
|
||||
}
|
||||
@ -367,12 +584,12 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
|
||||
state.slots = state.slots[:0]
|
||||
state.vars = state.vars[:0]
|
||||
for i, slot := range f.Names {
|
||||
state.slots = append(state.slots, slot)
|
||||
state.slots = append(state.slots, *slot)
|
||||
if ir.IsSynthetic(slot.N) {
|
||||
continue
|
||||
}
|
||||
|
||||
topSlot := &slot
|
||||
topSlot := slot
|
||||
for topSlot.SplitOf != nil {
|
||||
topSlot = topSlot.SplitOf
|
||||
}
|
||||
@ -436,7 +653,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
|
||||
if ir.IsSynthetic(slot.N) {
|
||||
continue
|
||||
}
|
||||
for _, value := range f.NamedValues[slot] {
|
||||
for _, value := range f.NamedValues[*slot] {
|
||||
state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
|
||||
}
|
||||
}
|
||||
@ -898,8 +1115,14 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
|
||||
continue
|
||||
}
|
||||
|
||||
mustBeFirst := func(v *Value) bool {
|
||||
return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() ||
|
||||
v.Op == OpArgIntReg || v.Op == OpArgFloatReg
|
||||
}
|
||||
|
||||
zeroWidthPending := false
|
||||
apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
|
||||
blockPrologComplete := false // set to true at first non-zero-width op
|
||||
apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
|
||||
// expect to see values in pattern (apc)* (zerowidth|real)*
|
||||
for _, v := range b.Values {
|
||||
slots := state.valueNames[v.ID]
|
||||
@ -908,16 +1131,16 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
|
||||
|
||||
if opcodeTable[v.Op].zeroWidth {
|
||||
if changed {
|
||||
if hasAnyArgOp(v) || v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() {
|
||||
if mustBeFirst(v) || v.Op == OpArg {
|
||||
// These ranges begin at true beginning of block, not after first instruction
|
||||
if zeroWidthPending {
|
||||
panic(fmt.Errorf("Unexpected op '%s' mixed with OpArg/OpPhi/OpLoweredGetClosurePtr at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
|
||||
if blockPrologComplete && mustBeFirst(v) {
|
||||
panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
|
||||
}
|
||||
apcChangedSize = len(state.changedVars.contents())
|
||||
// Other zero-width ops must wait on a "real" op.
|
||||
zeroWidthPending = true
|
||||
continue
|
||||
}
|
||||
// Other zero-width ops must wait on a "real" op.
|
||||
zeroWidthPending = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -928,6 +1151,7 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
|
||||
// Not zero-width; i.e., a "real" instruction.
|
||||
|
||||
zeroWidthPending = false
|
||||
blockPrologComplete = true
|
||||
for i, varID := range state.changedVars.contents() {
|
||||
if i < apcChangedSize { // buffered true start-of-block changes
|
||||
state.updateVar(VarID(varID), v.Block, BlockStart)
|
||||
|
@ -36,64 +36,65 @@ func decomposeBuiltIn(f *Func) {
|
||||
// accumulate new LocalSlots in newNames for addition after the iteration. This decomposition is for
|
||||
// builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots.
|
||||
var toDelete []namedVal
|
||||
var newNames []LocalSlot
|
||||
var newNames []*LocalSlot
|
||||
for i, name := range f.Names {
|
||||
t := name.Type
|
||||
switch {
|
||||
case t.IsInteger() && t.Size() > f.Config.RegSize:
|
||||
hiName, loName := f.fe.SplitInt64(name)
|
||||
newNames = append(newNames, hiName, loName)
|
||||
for j, v := range f.NamedValues[name] {
|
||||
hiName, loName := f.SplitInt64(name)
|
||||
newNames = maybeAppend2(f, newNames, hiName, loName)
|
||||
for j, v := range f.NamedValues[*name] {
|
||||
if v.Op != OpInt64Make {
|
||||
continue
|
||||
}
|
||||
f.NamedValues[hiName] = append(f.NamedValues[hiName], v.Args[0])
|
||||
f.NamedValues[loName] = append(f.NamedValues[loName], v.Args[1])
|
||||
f.NamedValues[*hiName] = append(f.NamedValues[*hiName], v.Args[0])
|
||||
f.NamedValues[*loName] = append(f.NamedValues[*loName], v.Args[1])
|
||||
toDelete = append(toDelete, namedVal{i, j})
|
||||
}
|
||||
case t.IsComplex():
|
||||
rName, iName := f.fe.SplitComplex(name)
|
||||
newNames = append(newNames, rName, iName)
|
||||
for j, v := range f.NamedValues[name] {
|
||||
rName, iName := f.SplitComplex(name)
|
||||
newNames = maybeAppend2(f, newNames, rName, iName)
|
||||
for j, v := range f.NamedValues[*name] {
|
||||
if v.Op != OpComplexMake {
|
||||
continue
|
||||
}
|
||||
f.NamedValues[rName] = append(f.NamedValues[rName], v.Args[0])
|
||||
f.NamedValues[iName] = append(f.NamedValues[iName], v.Args[1])
|
||||
f.NamedValues[*rName] = append(f.NamedValues[*rName], v.Args[0])
|
||||
f.NamedValues[*iName] = append(f.NamedValues[*iName], v.Args[1])
|
||||
toDelete = append(toDelete, namedVal{i, j})
|
||||
}
|
||||
case t.IsString():
|
||||
ptrName, lenName := f.fe.SplitString(name)
|
||||
newNames = append(newNames, ptrName, lenName)
|
||||
for j, v := range f.NamedValues[name] {
|
||||
ptrName, lenName := f.SplitString(name)
|
||||
newNames = maybeAppend2(f, newNames, ptrName, lenName)
|
||||
for j, v := range f.NamedValues[*name] {
|
||||
if v.Op != OpStringMake {
|
||||
continue
|
||||
}
|
||||
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0])
|
||||
f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1])
|
||||
f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
|
||||
f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
|
||||
toDelete = append(toDelete, namedVal{i, j})
|
||||
}
|
||||
case t.IsSlice():
|
||||
ptrName, lenName, capName := f.fe.SplitSlice(name)
|
||||
newNames = append(newNames, ptrName, lenName, capName)
|
||||
for j, v := range f.NamedValues[name] {
|
||||
ptrName, lenName, capName := f.SplitSlice(name)
|
||||
newNames = maybeAppend2(f, newNames, ptrName, lenName)
|
||||
newNames = maybeAppend(f, newNames, capName)
|
||||
for j, v := range f.NamedValues[*name] {
|
||||
if v.Op != OpSliceMake {
|
||||
continue
|
||||
}
|
||||
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0])
|
||||
f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1])
|
||||
f.NamedValues[capName] = append(f.NamedValues[capName], v.Args[2])
|
||||
f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
|
||||
f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
|
||||
f.NamedValues[*capName] = append(f.NamedValues[*capName], v.Args[2])
|
||||
toDelete = append(toDelete, namedVal{i, j})
|
||||
}
|
||||
case t.IsInterface():
|
||||
typeName, dataName := f.fe.SplitInterface(name)
|
||||
newNames = append(newNames, typeName, dataName)
|
||||
for j, v := range f.NamedValues[name] {
|
||||
typeName, dataName := f.SplitInterface(name)
|
||||
newNames = maybeAppend2(f, newNames, typeName, dataName)
|
||||
for j, v := range f.NamedValues[*name] {
|
||||
if v.Op != OpIMake {
|
||||
continue
|
||||
}
|
||||
f.NamedValues[typeName] = append(f.NamedValues[typeName], v.Args[0])
|
||||
f.NamedValues[dataName] = append(f.NamedValues[dataName], v.Args[1])
|
||||
f.NamedValues[*typeName] = append(f.NamedValues[*typeName], v.Args[0])
|
||||
f.NamedValues[*dataName] = append(f.NamedValues[*dataName], v.Args[1])
|
||||
toDelete = append(toDelete, namedVal{i, j})
|
||||
}
|
||||
case t.IsFloat():
|
||||
@ -107,6 +108,18 @@ func decomposeBuiltIn(f *Func) {
|
||||
f.Names = append(f.Names, newNames...)
|
||||
}
|
||||
|
||||
func maybeAppend(f *Func, ss []*LocalSlot, s *LocalSlot) []*LocalSlot {
|
||||
if _, ok := f.NamedValues[*s]; !ok {
|
||||
f.NamedValues[*s] = nil
|
||||
return append(ss, s)
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func maybeAppend2(f *Func, ss []*LocalSlot, s1, s2 *LocalSlot) []*LocalSlot {
|
||||
return maybeAppend(f, maybeAppend(f, ss, s1), s2)
|
||||
}
|
||||
|
||||
func decomposeBuiltInPhi(v *Value) {
|
||||
switch {
|
||||
case v.Type.IsInteger() && v.Type.Size() > v.Block.Func.Config.RegSize:
|
||||
@ -230,7 +243,7 @@ func decomposeUser(f *Func) {
|
||||
}
|
||||
// Split up named values into their components.
|
||||
i := 0
|
||||
var newNames []LocalSlot
|
||||
var newNames []*LocalSlot
|
||||
for _, name := range f.Names {
|
||||
t := name.Type
|
||||
switch {
|
||||
@ -250,7 +263,7 @@ func decomposeUser(f *Func) {
|
||||
// decomposeUserArrayInto creates names for the element(s) of arrays referenced
|
||||
// by name where possible, and appends those new names to slots, which is then
|
||||
// returned.
|
||||
func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalSlot {
|
||||
func decomposeUserArrayInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
|
||||
t := name.Type
|
||||
if t.NumElem() == 0 {
|
||||
// TODO(khr): Not sure what to do here. Probably nothing.
|
||||
@ -261,20 +274,20 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS
|
||||
// shouldn't get here due to CanSSA
|
||||
f.Fatalf("array not of size 1")
|
||||
}
|
||||
elemName := f.fe.SplitArray(name)
|
||||
elemName := f.SplitArray(name)
|
||||
var keep []*Value
|
||||
for _, v := range f.NamedValues[name] {
|
||||
for _, v := range f.NamedValues[*name] {
|
||||
if v.Op != OpArrayMake1 {
|
||||
keep = append(keep, v)
|
||||
continue
|
||||
}
|
||||
f.NamedValues[elemName] = append(f.NamedValues[elemName], v.Args[0])
|
||||
f.NamedValues[*elemName] = append(f.NamedValues[*elemName], v.Args[0])
|
||||
}
|
||||
if len(keep) == 0 {
|
||||
// delete the name for the array as a whole
|
||||
delete(f.NamedValues, name)
|
||||
delete(f.NamedValues, *name)
|
||||
} else {
|
||||
f.NamedValues[name] = keep
|
||||
f.NamedValues[*name] = keep
|
||||
}
|
||||
|
||||
if t.Elem().IsArray() {
|
||||
@ -289,38 +302,38 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS
|
||||
// decomposeUserStructInto creates names for the fields(s) of structs referenced
|
||||
// by name where possible, and appends those new names to slots, which is then
|
||||
// returned.
|
||||
func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalSlot {
|
||||
fnames := []LocalSlot{} // slots for struct in name
|
||||
func decomposeUserStructInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
|
||||
fnames := []*LocalSlot{} // slots for struct in name
|
||||
t := name.Type
|
||||
n := t.NumFields()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
fs := f.fe.SplitStruct(name, i)
|
||||
fs := f.SplitStruct(name, i)
|
||||
fnames = append(fnames, fs)
|
||||
// arrays and structs will be decomposed further, so
|
||||
// there's no need to record a name
|
||||
if !fs.Type.IsArray() && !fs.Type.IsStruct() {
|
||||
slots = append(slots, fs)
|
||||
slots = maybeAppend(f, slots, fs)
|
||||
}
|
||||
}
|
||||
|
||||
makeOp := StructMakeOp(n)
|
||||
var keep []*Value
|
||||
// create named values for each struct field
|
||||
for _, v := range f.NamedValues[name] {
|
||||
for _, v := range f.NamedValues[*name] {
|
||||
if v.Op != makeOp {
|
||||
keep = append(keep, v)
|
||||
continue
|
||||
}
|
||||
for i := 0; i < len(fnames); i++ {
|
||||
f.NamedValues[fnames[i]] = append(f.NamedValues[fnames[i]], v.Args[i])
|
||||
f.NamedValues[*fnames[i]] = append(f.NamedValues[*fnames[i]], v.Args[i])
|
||||
}
|
||||
}
|
||||
if len(keep) == 0 {
|
||||
// delete the name for the struct as a whole
|
||||
delete(f.NamedValues, name)
|
||||
delete(f.NamedValues, *name)
|
||||
} else {
|
||||
f.NamedValues[name] = keep
|
||||
f.NamedValues[*name] = keep
|
||||
}
|
||||
|
||||
// now that this f.NamedValues contains values for the struct
|
||||
@ -328,10 +341,10 @@ func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []Local
|
||||
for i := 0; i < n; i++ {
|
||||
if name.Type.FieldType(i).IsStruct() {
|
||||
slots = decomposeUserStructInto(f, fnames[i], slots)
|
||||
delete(f.NamedValues, fnames[i])
|
||||
delete(f.NamedValues, *fnames[i])
|
||||
} else if name.Type.FieldType(i).IsArray() {
|
||||
slots = decomposeUserArrayInto(f, fnames[i], slots)
|
||||
delete(f.NamedValues, fnames[i])
|
||||
delete(f.NamedValues, *fnames[i])
|
||||
}
|
||||
}
|
||||
return slots
|
||||
@ -416,9 +429,10 @@ type namedVal struct {
|
||||
locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key
|
||||
}
|
||||
|
||||
// deleteNamedVals removes particular values with debugger names from f's naming data structures
|
||||
// deleteNamedVals removes particular values with debugger names from f's naming data structures,
|
||||
// removes all values with OpInvalid, and re-sorts the list of Names.
|
||||
func deleteNamedVals(f *Func, toDelete []namedVal) {
|
||||
// Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalid pending indices.
|
||||
// Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalidate pending indices.
|
||||
sort.Slice(toDelete, func(i, j int) bool {
|
||||
if toDelete[i].locIndex != toDelete[j].locIndex {
|
||||
return toDelete[i].locIndex > toDelete[j].locIndex
|
||||
@ -430,16 +444,36 @@ func deleteNamedVals(f *Func, toDelete []namedVal) {
|
||||
// Get rid of obsolete names
|
||||
for _, d := range toDelete {
|
||||
loc := f.Names[d.locIndex]
|
||||
vals := f.NamedValues[loc]
|
||||
vals := f.NamedValues[*loc]
|
||||
l := len(vals) - 1
|
||||
if l > 0 {
|
||||
vals[d.valIndex] = vals[l]
|
||||
f.NamedValues[loc] = vals[:l]
|
||||
} else {
|
||||
delete(f.NamedValues, loc)
|
||||
l = len(f.Names) - 1
|
||||
f.Names[d.locIndex] = f.Names[l]
|
||||
f.Names = f.Names[:l]
|
||||
}
|
||||
vals[l] = nil
|
||||
f.NamedValues[*loc] = vals[:l]
|
||||
}
|
||||
// Delete locations with no values attached.
|
||||
end := len(f.Names)
|
||||
for i := len(f.Names) - 1; i >= 0; i-- {
|
||||
loc := f.Names[i]
|
||||
vals := f.NamedValues[*loc]
|
||||
last := len(vals)
|
||||
for j := len(vals) - 1; j >= 0; j-- {
|
||||
if vals[j].Op == OpInvalid {
|
||||
last--
|
||||
vals[j] = vals[last]
|
||||
vals[last] = nil
|
||||
}
|
||||
}
|
||||
if last < len(vals) {
|
||||
f.NamedValues[*loc] = vals[:last]
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
delete(f.NamedValues, *loc)
|
||||
end--
|
||||
f.Names[i] = f.Names[end]
|
||||
f.Names[end] = nil
|
||||
}
|
||||
}
|
||||
f.Names = f.Names[:end]
|
||||
}
|
||||
|
@ -243,10 +243,10 @@ func (x *expandState) offsetFrom(b *Block, from *Value, offset int64, pt *types.
|
||||
}
|
||||
|
||||
// splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates.
|
||||
func (x *expandState) splitSlots(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot {
|
||||
var locs []LocalSlot
|
||||
func (x *expandState) splitSlots(ls []*LocalSlot, sfx string, offset int64, ty *types.Type) []*LocalSlot {
|
||||
var locs []*LocalSlot
|
||||
for i := range ls {
|
||||
locs = append(locs, x.f.fe.SplitSlot(&ls[i], sfx, offset, ty))
|
||||
locs = append(locs, x.f.SplitSlot(ls[i], sfx, offset, ty))
|
||||
}
|
||||
return locs
|
||||
}
|
||||
@ -301,13 +301,13 @@ func (x *expandState) Printf(format string, a ...interface{}) (n int, err error)
|
||||
// It emits the code necessary to implement the leaf select operation that leads to the root.
|
||||
//
|
||||
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
|
||||
func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []LocalSlot {
|
||||
func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot {
|
||||
if x.debug {
|
||||
x.indent(3)
|
||||
defer x.indent(-3)
|
||||
x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
|
||||
}
|
||||
var locs []LocalSlot
|
||||
var locs []*LocalSlot
|
||||
leafType := leaf.Type
|
||||
if len(selector.Args) > 0 {
|
||||
w := selector.Args[0]
|
||||
@ -477,7 +477,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
|
||||
|
||||
case OpStructSelect:
|
||||
w := selector.Args[0]
|
||||
var ls []LocalSlot
|
||||
var ls []*LocalSlot
|
||||
if w.Type.Kind() != types.TSTRUCT { // IData artifact
|
||||
ls = x.rewriteSelect(leaf, w, offset, regOffset)
|
||||
} else {
|
||||
@ -485,7 +485,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
|
||||
ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(fldi), regOffset+x.regOffset(w.Type, fldi))
|
||||
if w.Op != OpIData {
|
||||
for _, l := range ls {
|
||||
locs = append(locs, x.f.fe.SplitStruct(l, int(selector.AuxInt)))
|
||||
locs = append(locs, x.f.SplitStruct(l, int(selector.AuxInt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -662,7 +662,7 @@ outer:
|
||||
func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
|
||||
|
||||
pa := x.prAssignForArg(source)
|
||||
var locs []LocalSlot
|
||||
var locs []*LocalSlot
|
||||
for _, s := range x.namedSelects[source] {
|
||||
locs = append(locs, x.f.Names[s.locIndex])
|
||||
}
|
||||
@ -756,12 +756,15 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *expandState) splitSlotsIntoNames(locs []LocalSlot, suffix string, off int64, rt *types.Type, w *Value) {
|
||||
func (x *expandState) splitSlotsIntoNames(locs []*LocalSlot, suffix string, off int64, rt *types.Type, w *Value) {
|
||||
wlocs := x.splitSlots(locs, suffix, off, rt)
|
||||
for _, l := range wlocs {
|
||||
x.f.NamedValues[l] = append(x.f.NamedValues[l], w)
|
||||
old, ok := x.f.NamedValues[*l]
|
||||
x.f.NamedValues[*l] = append(old, w)
|
||||
if !ok {
|
||||
x.f.Names = append(x.f.Names, l)
|
||||
}
|
||||
}
|
||||
x.f.Names = append(x.f.Names, wlocs...)
|
||||
}
|
||||
|
||||
// decomposeLoad is a helper for storeArgOrLoad.
|
||||
@ -826,7 +829,7 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value,
|
||||
// storeOneArg creates a decomposed (one step) arg that is then stored.
|
||||
// pos and b locate the store instruction, source is the "base" of the value input,
|
||||
// mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
|
||||
func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
|
||||
func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
|
||||
if x.debug {
|
||||
x.indent(3)
|
||||
defer x.indent(-3)
|
||||
@ -848,7 +851,7 @@ func storeOneLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t
|
||||
return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc)
|
||||
}
|
||||
|
||||
func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
|
||||
func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
|
||||
mem = storeOneArg(x, pos, b, locs, suffix1, source, mem, t1, offArg, offStore, loadRegOffset, storeRc.next(t1))
|
||||
pos = pos.WithNotStmt()
|
||||
t1Size := t1.Size()
|
||||
@ -1168,7 +1171,7 @@ func expandCalls(f *Func) {
|
||||
for i, name := range f.Names {
|
||||
t := name.Type
|
||||
if x.isAlreadyExpandedAggregateType(t) {
|
||||
for j, v := range f.NamedValues[name] {
|
||||
for j, v := range f.NamedValues[*name] {
|
||||
if v.Op == OpSelectN || v.Op == OpArg && x.isAlreadyExpandedAggregateType(v.Type) {
|
||||
ns := x.namedSelects[v]
|
||||
x.namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j})
|
||||
@ -1477,10 +1480,10 @@ func expandCalls(f *Func) {
|
||||
// Leaf types may have debug locations
|
||||
if !x.isAlreadyExpandedAggregateType(v.Type) {
|
||||
for _, l := range locs {
|
||||
if _, ok := f.NamedValues[l]; !ok {
|
||||
if _, ok := f.NamedValues[*l]; !ok {
|
||||
f.Names = append(f.Names, l)
|
||||
}
|
||||
f.NamedValues[l] = append(f.NamedValues[l], v)
|
||||
f.NamedValues[*l] = append(f.NamedValues[*l], v)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -1553,7 +1556,7 @@ func expandCalls(f *Func) {
|
||||
// Step 6: elide any copies introduced.
|
||||
// Update named values.
|
||||
for _, name := range f.Names {
|
||||
values := f.NamedValues[name]
|
||||
values := f.NamedValues[*name]
|
||||
for i, v := range values {
|
||||
if v.Op == OpCopy {
|
||||
a := v.Args[0]
|
||||
@ -1714,21 +1717,6 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
|
||||
} else {
|
||||
w = baseArg.Block.NewValue0IA(pos, op, t, auxInt, aux)
|
||||
}
|
||||
// If we are creating an OpArgIntReg/OpArgFloatReg that
|
||||
// corresponds to an in-param that fits entirely in a register,
|
||||
// then enter it into the name/value table. The LocalSlot
|
||||
// is somewhat fictitious, since there is no incoming live
|
||||
// memory version of the parameter, but we need an entry in
|
||||
// NamedValues in order for ssa debug tracking to include
|
||||
// the value in the tracking analysis.
|
||||
if len(pa.Registers) == 1 {
|
||||
loc := LocalSlot{N: aux.Name, Type: t, Off: 0}
|
||||
values, ok := x.f.NamedValues[loc]
|
||||
if !ok {
|
||||
x.f.Names = append(x.f.Names, loc)
|
||||
}
|
||||
x.f.NamedValues[loc] = append(values, w)
|
||||
}
|
||||
x.commonArgs[key] = w
|
||||
if toReplace != nil {
|
||||
toReplace.copyOf(w)
|
||||
|
@ -73,36 +73,6 @@ func (TestFrontend) Auto(pos src.XPos, t *types.Type) *ir.Name {
|
||||
n.Class = ir.PAUTO
|
||||
return n
|
||||
}
|
||||
func (d TestFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8}
|
||||
}
|
||||
func (d TestFrontend) SplitInterface(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off + 8}
|
||||
}
|
||||
func (d TestFrontend) SplitSlice(s LocalSlot) (LocalSlot, LocalSlot, LocalSlot) {
|
||||
return LocalSlot{N: s.N, Type: s.Type.Elem().PtrTo(), Off: s.Off},
|
||||
LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8},
|
||||
LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 16}
|
||||
}
|
||||
func (d TestFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
if s.Type.Size() == 16 {
|
||||
return LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off + 8}
|
||||
}
|
||||
return LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off + 4}
|
||||
}
|
||||
func (d TestFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
if s.Type.IsSigned() {
|
||||
return LocalSlot{N: s.N, Type: testTypes.Int32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
|
||||
}
|
||||
return LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
|
||||
}
|
||||
func (d TestFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
|
||||
return LocalSlot{N: s.N, Type: s.Type.FieldType(i), Off: s.Off + s.Type.FieldOff(i)}
|
||||
}
|
||||
func (d TestFrontend) SplitArray(s LocalSlot) LocalSlot {
|
||||
return LocalSlot{N: s.N, Type: s.Type.Elem(), Off: s.Off}
|
||||
}
|
||||
|
||||
func (d TestFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot {
|
||||
return LocalSlot{N: parent.N, Type: t, Off: offset}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24
|
||||
|
@ -6,6 +6,7 @@ package ssa
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/abi"
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
"crypto/sha1"
|
||||
@ -61,7 +62,11 @@ type Func struct {
|
||||
NamedValues map[LocalSlot][]*Value
|
||||
// Names is a copy of NamedValues.Keys. We keep a separate list
|
||||
// of keys to make iteration order deterministic.
|
||||
Names []LocalSlot
|
||||
Names []*LocalSlot
|
||||
// Canonicalize root/top-level local slots, and canonicalize their pieces.
|
||||
// Because LocalSlot pieces refer to their parents with a pointer, this ensures that equivalent slots really are equal.
|
||||
CanonicalLocalSlots map[LocalSlot]*LocalSlot
|
||||
CanonicalLocalSplits map[LocalSlotSplitKey]*LocalSlot
|
||||
|
||||
// RegArgs is a slice of register-memory pairs that must be spilled and unspilled in the uncommon path of function entry.
|
||||
RegArgs []Spill
|
||||
@ -87,10 +92,16 @@ type Func struct {
|
||||
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
|
||||
}
|
||||
|
||||
type LocalSlotSplitKey struct {
|
||||
parent *LocalSlot
|
||||
Off int64 // offset of slot in N
|
||||
Type *types.Type // type of slot
|
||||
}
|
||||
|
||||
// NewFunc returns a new, empty function object.
|
||||
// Caller must set f.Config and f.Cache before using f.
|
||||
func NewFunc(fe Frontend) *Func {
|
||||
return &Func{fe: fe, NamedValues: make(map[LocalSlot][]*Value)}
|
||||
return &Func{fe: fe, NamedValues: make(map[LocalSlot][]*Value), CanonicalLocalSlots: make(map[LocalSlot]*LocalSlot), CanonicalLocalSplits: make(map[LocalSlotSplitKey]*LocalSlot)}
|
||||
}
|
||||
|
||||
// NumBlocks returns an integer larger than the id of any Block in the Func.
|
||||
@ -193,6 +204,101 @@ func (f *Func) retDeadcodeLiveOrderStmts(liveOrderStmts []*Value) {
|
||||
f.Cache.deadcode.liveOrderStmts = liveOrderStmts
|
||||
}
|
||||
|
||||
func (f *Func) localSlotAddr(slot LocalSlot) *LocalSlot {
|
||||
a, ok := f.CanonicalLocalSlots[slot]
|
||||
if !ok {
|
||||
a = new(LocalSlot)
|
||||
*a = slot // don't escape slot
|
||||
f.CanonicalLocalSlots[slot] = a
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (f *Func) SplitString(name *LocalSlot) (*LocalSlot, *LocalSlot) {
|
||||
ptrType := types.NewPtr(types.Types[types.TUINT8])
|
||||
lenType := types.Types[types.TINT]
|
||||
// Split this string up into two separate variables.
|
||||
p := f.SplitSlot(name, ".ptr", 0, ptrType)
|
||||
l := f.SplitSlot(name, ".len", ptrType.Size(), lenType)
|
||||
return p, l
|
||||
}
|
||||
|
||||
func (f *Func) SplitInterface(name *LocalSlot) (*LocalSlot, *LocalSlot) {
|
||||
n := name.N
|
||||
u := types.Types[types.TUINTPTR]
|
||||
t := types.NewPtr(types.Types[types.TUINT8])
|
||||
// Split this interface up into two separate variables.
|
||||
sfx := ".itab"
|
||||
if n.Type().IsEmptyInterface() {
|
||||
sfx = ".type"
|
||||
}
|
||||
c := f.SplitSlot(name, sfx, 0, u) // see comment in typebits.Set
|
||||
d := f.SplitSlot(name, ".data", u.Size(), t)
|
||||
return c, d
|
||||
}
|
||||
|
||||
func (f *Func) SplitSlice(name *LocalSlot) (*LocalSlot, *LocalSlot, *LocalSlot) {
|
||||
ptrType := types.NewPtr(name.Type.Elem())
|
||||
lenType := types.Types[types.TINT]
|
||||
p := f.SplitSlot(name, ".ptr", 0, ptrType)
|
||||
l := f.SplitSlot(name, ".len", ptrType.Size(), lenType)
|
||||
c := f.SplitSlot(name, ".cap", ptrType.Size()+lenType.Size(), lenType)
|
||||
return p, l, c
|
||||
}
|
||||
|
||||
func (f *Func) SplitComplex(name *LocalSlot) (*LocalSlot, *LocalSlot) {
|
||||
s := name.Type.Size() / 2
|
||||
var t *types.Type
|
||||
if s == 8 {
|
||||
t = types.Types[types.TFLOAT64]
|
||||
} else {
|
||||
t = types.Types[types.TFLOAT32]
|
||||
}
|
||||
r := f.SplitSlot(name, ".real", 0, t)
|
||||
i := f.SplitSlot(name, ".imag", t.Size(), t)
|
||||
return r, i
|
||||
}
|
||||
|
||||
func (f *Func) SplitInt64(name *LocalSlot) (*LocalSlot, *LocalSlot) {
|
||||
var t *types.Type
|
||||
if name.Type.IsSigned() {
|
||||
t = types.Types[types.TINT32]
|
||||
} else {
|
||||
t = types.Types[types.TUINT32]
|
||||
}
|
||||
if f.Config.BigEndian {
|
||||
return f.SplitSlot(name, ".hi", 0, t), f.SplitSlot(name, ".lo", t.Size(), types.Types[types.TUINT32])
|
||||
}
|
||||
return f.SplitSlot(name, ".hi", t.Size(), t), f.SplitSlot(name, ".lo", 0, types.Types[types.TUINT32])
|
||||
}
|
||||
|
||||
func (f *Func) SplitStruct(name *LocalSlot, i int) *LocalSlot {
|
||||
st := name.Type
|
||||
return f.SplitSlot(name, st.FieldName(i), st.FieldOff(i), st.FieldType(i))
|
||||
}
|
||||
func (f *Func) SplitArray(name *LocalSlot) *LocalSlot {
|
||||
n := name.N
|
||||
at := name.Type
|
||||
if at.NumElem() != 1 {
|
||||
base.FatalfAt(n.Pos(), "bad array size")
|
||||
}
|
||||
et := at.Elem()
|
||||
return f.SplitSlot(name, "[0]", 0, et)
|
||||
}
|
||||
|
||||
func (f *Func) SplitSlot(name *LocalSlot, sfx string, offset int64, t *types.Type) *LocalSlot {
|
||||
lssk := LocalSlotSplitKey{name, offset, t}
|
||||
if als, ok := f.CanonicalLocalSplits[lssk]; ok {
|
||||
return als
|
||||
}
|
||||
// Note: the _ field may appear several times. But
|
||||
// have no fear, identically-named but distinct Autos are
|
||||
// ok, albeit maybe confusing for a debugger.
|
||||
ls := f.fe.SplitSlot(name, sfx, offset, t)
|
||||
f.CanonicalLocalSplits[lssk] = &ls
|
||||
return &ls
|
||||
}
|
||||
|
||||
// newValue allocates a new Value with the given fields and places it at the end of b.Values.
|
||||
func (f *Func) newValue(op Op, t *types.Type, b *Block, pos src.XPos) *Value {
|
||||
var v *Value
|
||||
|
@ -2216,3 +2216,22 @@
|
||||
(MOVOstore [dstOff] {dstSym} ptr (MOVOload [srcOff] {srcSym} (SB) _) mem) && symIsRO(srcSym) =>
|
||||
(MOVQstore [dstOff+8] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff)+8, config.ctxt.Arch.ByteOrder))])
|
||||
(MOVQstore [dstOff] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff), config.ctxt.Arch.ByteOrder))]) mem))
|
||||
|
||||
// Arch-specific inlining for small or disjoint runtime.memmove
|
||||
// Match post-lowering calls, memory version.
|
||||
(SelectN [0] call:(CALLstatic {sym} s1:(MOVQstoreconst _ [sc] s2:(MOVQstore _ src s3:(MOVQstore _ dst mem)))))
|
||||
&& sc.Val64() >= 0
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, sc.Val64(), config)
|
||||
&& clobber(s1, s2, s3, call)
|
||||
=> (Move [sc.Val64()] dst src mem)
|
||||
|
||||
// Match post-lowering calls, register version.
|
||||
(SelectN [0] call:(CALLstatic {sym} dst src (MOVQconst [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& call.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, sz, config)
|
||||
&& clobber(call)
|
||||
=> (Move [sz] dst src mem)
|
||||
|
@ -2859,3 +2859,12 @@
|
||||
(MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
|
||||
(MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
|
||||
(MOVDload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))])
|
||||
|
||||
// Arch-specific inlining for small or disjoint runtime.memmove
|
||||
(SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
|
||||
&& sz >= 0
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, sz, config)
|
||||
&& clobber(s1, s2, s3, call)
|
||||
=> (Move [sz] dst src mem)
|
||||
|
@ -2065,7 +2065,7 @@
|
||||
(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
&& sz >= 0
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& t.IsPtr() // avoids TUINTPTR, see issue 30061
|
||||
&& t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(s1, s2, s3, call)
|
||||
@ -2076,7 +2076,7 @@
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& dst.Type.IsPtr() // avoids TUINTPTR, see issue 30061
|
||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(call)
|
||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
@ -2086,7 +2086,7 @@
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& dst.Type.IsPtr() // avoids TUINTPTR, see issue 30061
|
||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(call)
|
||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
|
@ -12,26 +12,10 @@ func layout(f *Func) {
|
||||
}
|
||||
|
||||
// Register allocation may use a different order which has constraints
|
||||
// imposed by the linear-scan algorithm. Note that f.pass here is
|
||||
// regalloc, so the switch is conditional on -d=ssa/regalloc/test=N
|
||||
// imposed by the linear-scan algorithm.
|
||||
func layoutRegallocOrder(f *Func) []*Block {
|
||||
|
||||
switch f.pass.test {
|
||||
case 0: // layout order
|
||||
return layoutOrder(f)
|
||||
case 1: // existing block order
|
||||
return f.Blocks
|
||||
case 2: // reverse of postorder; legal, but usually not good.
|
||||
po := f.postorder()
|
||||
visitOrder := make([]*Block, len(po))
|
||||
for i, b := range po {
|
||||
j := len(po) - i - 1
|
||||
visitOrder[j] = b
|
||||
}
|
||||
return visitOrder
|
||||
}
|
||||
|
||||
return nil
|
||||
// remnant of an experiment; perhaps there will be another.
|
||||
return layoutOrder(f)
|
||||
}
|
||||
|
||||
func layoutOrder(f *Func) []*Block {
|
||||
|
@ -154,6 +154,6 @@ func fprintFunc(p funcPrinter, f *Func) {
|
||||
p.endBlock(b)
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
p.named(name, f.NamedValues[name])
|
||||
p.named(*name, f.NamedValues[*name])
|
||||
}
|
||||
}
|
||||
|
@ -1882,6 +1882,10 @@ func (s *regAllocState) placeSpills() {
|
||||
phiRegs[b.ID] = m
|
||||
}
|
||||
|
||||
mustBeFirst := func(op Op) bool {
|
||||
return op.isLoweredGetClosurePtr() || op == OpPhi || op == OpArgIntReg || op == OpArgFloatReg
|
||||
}
|
||||
|
||||
// Start maps block IDs to the list of spills
|
||||
// that go at the start of the block (but after any phis).
|
||||
start := map[ID][]*Value{}
|
||||
@ -1971,7 +1975,7 @@ func (s *regAllocState) placeSpills() {
|
||||
// Put the spill in the best block we found.
|
||||
spill.Block = best
|
||||
spill.AddArg(bestArg)
|
||||
if best == v.Block && v.Op != OpPhi {
|
||||
if best == v.Block && !mustBeFirst(v.Op) {
|
||||
// Place immediately after v.
|
||||
after[v.ID] = append(after[v.ID], spill)
|
||||
} else {
|
||||
@ -1983,15 +1987,15 @@ func (s *regAllocState) placeSpills() {
|
||||
// Insert spill instructions into the block schedules.
|
||||
var oldSched []*Value
|
||||
for _, b := range s.visitOrder {
|
||||
nphi := 0
|
||||
nfirst := 0
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi {
|
||||
if !mustBeFirst(v.Op) {
|
||||
break
|
||||
}
|
||||
nphi++
|
||||
nfirst++
|
||||
}
|
||||
oldSched = append(oldSched[:0], b.Values[nphi:]...)
|
||||
b.Values = b.Values[:nphi]
|
||||
oldSched = append(oldSched[:0], b.Values[nfirst:]...)
|
||||
b.Values = b.Values[:nfirst]
|
||||
b.Values = append(b.Values, start[b.ID]...)
|
||||
for _, v := range oldSched {
|
||||
b.Values = append(b.Values, v)
|
||||
|
@ -1038,6 +1038,8 @@ func rewriteValueAMD64(v *Value) bool {
|
||||
return rewriteValueAMD64_OpSelect0(v)
|
||||
case OpSelect1:
|
||||
return rewriteValueAMD64_OpSelect1(v)
|
||||
case OpSelectN:
|
||||
return rewriteValueAMD64_OpSelectN(v)
|
||||
case OpSignExt16to32:
|
||||
v.Op = OpAMD64MOVWQSX
|
||||
return true
|
||||
@ -32981,6 +32983,78 @@ func rewriteValueAMD64_OpSelect1(v *Value) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueAMD64_OpSelectN(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVQstoreconst _ [sc] s2:(MOVQstore _ src s3:(MOVQstore _ dst mem)))))
|
||||
// cond: sc.Val64() >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sc.Val64(), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move [sc.Val64()] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
call := v_0
|
||||
if call.Op != OpAMD64CALLstatic || len(call.Args) != 1 {
|
||||
break
|
||||
}
|
||||
sym := auxToCall(call.Aux)
|
||||
s1 := call.Args[0]
|
||||
if s1.Op != OpAMD64MOVQstoreconst {
|
||||
break
|
||||
}
|
||||
sc := auxIntToValAndOff(s1.AuxInt)
|
||||
_ = s1.Args[1]
|
||||
s2 := s1.Args[1]
|
||||
if s2.Op != OpAMD64MOVQstore {
|
||||
break
|
||||
}
|
||||
_ = s2.Args[2]
|
||||
src := s2.Args[1]
|
||||
s3 := s2.Args[2]
|
||||
if s3.Op != OpAMD64MOVQstore {
|
||||
break
|
||||
}
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sc.Val64() >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sc.Val64(), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(sc.Val64())
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVQconst [sz]) mem))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)
|
||||
// result: (Move [sz] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
call := v_0
|
||||
if call.Op != OpAMD64CALLstatic || len(call.Args) != 4 {
|
||||
break
|
||||
}
|
||||
sym := auxToCall(call.Aux)
|
||||
mem := call.Args[3]
|
||||
dst := call.Args[0]
|
||||
src := call.Args[1]
|
||||
call_2 := call.Args[2]
|
||||
if call_2.Op != OpAMD64MOVQconst {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(sz)
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueAMD64_OpSlicemask(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
|
@ -984,6 +984,8 @@ func rewriteValueARM64(v *Value) bool {
|
||||
return rewriteValueARM64_OpSelect0(v)
|
||||
case OpSelect1:
|
||||
return rewriteValueARM64_OpSelect1(v)
|
||||
case OpSelectN:
|
||||
return rewriteValueARM64_OpSelectN(v)
|
||||
case OpSignExt16to32:
|
||||
v.Op = OpARM64MOVHreg
|
||||
return true
|
||||
@ -25983,6 +25985,54 @@ func rewriteValueARM64_OpSelect1(v *Value) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueARM64_OpSelectN(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move [sz] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
call := v_0
|
||||
if call.Op != OpARM64CALLstatic {
|
||||
break
|
||||
}
|
||||
sym := auxToCall(call.Aux)
|
||||
s1 := call.Args[0]
|
||||
if s1.Op != OpARM64MOVDstore {
|
||||
break
|
||||
}
|
||||
_ = s1.Args[2]
|
||||
s1_1 := s1.Args[1]
|
||||
if s1_1.Op != OpARM64MOVDconst {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(s1_1.AuxInt)
|
||||
s2 := s1.Args[2]
|
||||
if s2.Op != OpARM64MOVDstore {
|
||||
break
|
||||
}
|
||||
_ = s2.Args[2]
|
||||
src := s2.Args[1]
|
||||
s3 := s2.Args[2]
|
||||
if s3.Op != OpARM64MOVDstore {
|
||||
break
|
||||
}
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(sz)
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueARM64_OpSlicemask(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
|
@ -141,10 +141,11 @@ func (s *stackAllocState) stackalloc() {
|
||||
s.names = make([]LocalSlot, n)
|
||||
}
|
||||
names := s.names
|
||||
empty := LocalSlot{}
|
||||
for _, name := range f.Names {
|
||||
// Note: not "range f.NamedValues" above, because
|
||||
// that would be nondeterministic.
|
||||
for _, v := range f.NamedValues[name] {
|
||||
for _, v := range f.NamedValues[*name] {
|
||||
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
|
||||
aux := v.Aux.(*AuxNameOffset)
|
||||
// Never let an arg be bound to a differently named thing.
|
||||
@ -162,10 +163,12 @@ func (s *stackAllocState) stackalloc() {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.pass.debug > stackDebug {
|
||||
fmt.Printf("stackalloc value %s to name %s\n", v, name)
|
||||
if names[v.ID] == empty {
|
||||
if f.pass.debug > stackDebug {
|
||||
fmt.Printf("stackalloc value %s to name %s\n", v, *name)
|
||||
}
|
||||
names[v.ID] = *name
|
||||
}
|
||||
names[v.ID] = name
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"cmd/compile/internal/abi"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"html"
|
||||
@ -1297,7 +1296,7 @@ func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrument
|
||||
if f.Sym.IsBlank() {
|
||||
continue
|
||||
}
|
||||
offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), abi.FieldOffsetOf(f), addr)
|
||||
offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr)
|
||||
s.instrumentFields(f.Type, offptr, kind)
|
||||
}
|
||||
}
|
||||
@ -3175,7 +3174,7 @@ func (s *state) expr(n ir.Node) *ssa.Value {
|
||||
arrlen := s.constInt(types.Types[types.TINT], n.Type().Elem().NumElem())
|
||||
cap := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], v)
|
||||
s.boundsCheck(arrlen, cap, ssa.BoundsConvert, false)
|
||||
return s.newValue1(ssa.OpSlicePtrUnchecked, types.Types[types.TINT], v)
|
||||
return s.newValue1(ssa.OpSlicePtrUnchecked, n.Type(), v)
|
||||
|
||||
case ir.OCALLFUNC:
|
||||
n := n.(*ir.CallExpr)
|
||||
@ -5054,19 +5053,23 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||
ft := fn.Type()
|
||||
off := t.FieldOff(12) // TODO register args: be sure this isn't a hardcoded param stack offset.
|
||||
args := n.Args
|
||||
i0 := 0
|
||||
|
||||
// Set receiver (for interface calls). Always a pointer.
|
||||
if rcvr != nil {
|
||||
p := s.newValue1I(ssa.OpOffPtr, ft.Recv().Type.PtrTo(), off, addr)
|
||||
s.store(types.Types[types.TUINTPTR], p, rcvr)
|
||||
i0 = 1
|
||||
}
|
||||
// Set receiver (for method calls).
|
||||
if n.Op() == ir.OCALLMETH {
|
||||
base.Fatalf("OCALLMETH missed by walkCall")
|
||||
}
|
||||
// Set other args.
|
||||
for _, f := range ft.Params().Fields().Slice() {
|
||||
s.storeArgWithBase(args[0], f.Type, addr, off+abi.FieldOffsetOf(f))
|
||||
// This code is only used when RegabiDefer is not enabled, and arguments are always
|
||||
// passed on stack.
|
||||
for i, f := range ft.Params().Fields().Slice() {
|
||||
s.storeArgWithBase(args[0], f.Type, addr, off+params.InParam(i+i0).FrameOffset(params))
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
@ -5079,7 +5082,6 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||
if stksize < int64(types.PtrSize) {
|
||||
// We need room for both the call to deferprocStack and the call to
|
||||
// the deferred function.
|
||||
// TODO(register args) Revisit this if/when we pass args in registers.
|
||||
stksize = int64(types.PtrSize)
|
||||
}
|
||||
call.AuxInt = stksize
|
||||
@ -6463,7 +6465,8 @@ func (s *state) addNamedValue(n *ir.Name, v *ssa.Value) {
|
||||
loc := ssa.LocalSlot{N: n, Type: n.Type(), Off: 0}
|
||||
values, ok := s.f.NamedValues[loc]
|
||||
if !ok {
|
||||
s.f.Names = append(s.f.Names, loc)
|
||||
s.f.Names = append(s.f.Names, &loc)
|
||||
s.f.CanonicalLocalSlots[loc] = &loc
|
||||
}
|
||||
s.f.NamedValues[loc] = append(values, v)
|
||||
}
|
||||
@ -6598,6 +6601,7 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
|
||||
x := base.Ctxt.Lookup(fmt.Sprintf("%s.arginfo%d", f.LSym.Name, f.ABI))
|
||||
|
||||
PtrSize := int64(types.PtrSize)
|
||||
uintptrTyp := types.Types[types.TUINTPTR]
|
||||
|
||||
isAggregate := func(t *types.Type) bool {
|
||||
return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice()
|
||||
@ -6641,12 +6645,8 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
|
||||
n := 0
|
||||
writebyte := func(o uint8) { wOff = objw.Uint8(x, wOff, o) }
|
||||
|
||||
// Write one non-aggrgate arg/field/element if there is room.
|
||||
// Returns whether to continue.
|
||||
write1 := func(sz, offset int64) bool {
|
||||
if n >= limit {
|
||||
return false
|
||||
}
|
||||
// Write one non-aggrgate arg/field/element.
|
||||
write1 := func(sz, offset int64) {
|
||||
if offset >= _special {
|
||||
writebyte(_offsetTooLarge)
|
||||
} else {
|
||||
@ -6654,7 +6654,6 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
|
||||
writebyte(uint8(sz))
|
||||
}
|
||||
n++
|
||||
return true
|
||||
}
|
||||
|
||||
// Visit t recursively and write it out.
|
||||
@ -6662,10 +6661,12 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
|
||||
var visitType func(baseOffset int64, t *types.Type, depth int) bool
|
||||
visitType = func(baseOffset int64, t *types.Type, depth int) bool {
|
||||
if n >= limit {
|
||||
writebyte(_dotdotdot)
|
||||
return false
|
||||
}
|
||||
if !isAggregate(t) {
|
||||
return write1(t.Size(), baseOffset)
|
||||
write1(t.Size(), baseOffset)
|
||||
return true
|
||||
}
|
||||
writebyte(_startAgg)
|
||||
depth++
|
||||
@ -6675,58 +6676,47 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
|
||||
n++
|
||||
return true
|
||||
}
|
||||
var r bool
|
||||
switch {
|
||||
case t.IsInterface(), t.IsString():
|
||||
r = write1(PtrSize, baseOffset) &&
|
||||
write1(PtrSize, baseOffset+PtrSize)
|
||||
_ = visitType(baseOffset, uintptrTyp, depth) &&
|
||||
visitType(baseOffset+PtrSize, uintptrTyp, depth)
|
||||
case t.IsSlice():
|
||||
r = write1(PtrSize, baseOffset) &&
|
||||
write1(PtrSize, baseOffset+PtrSize) &&
|
||||
write1(PtrSize, baseOffset+PtrSize*2)
|
||||
_ = visitType(baseOffset, uintptrTyp, depth) &&
|
||||
visitType(baseOffset+PtrSize, uintptrTyp, depth) &&
|
||||
visitType(baseOffset+PtrSize*2, uintptrTyp, depth)
|
||||
case t.IsComplex():
|
||||
r = write1(t.Size()/2, baseOffset) &&
|
||||
write1(t.Size()/2, baseOffset+t.Size()/2)
|
||||
_ = visitType(baseOffset, types.FloatForComplex(t), depth) &&
|
||||
visitType(baseOffset+t.Size()/2, types.FloatForComplex(t), depth)
|
||||
case t.IsArray():
|
||||
r = true
|
||||
if t.NumElem() == 0 {
|
||||
n++ // {} counts as a component
|
||||
break
|
||||
}
|
||||
for i := int64(0); i < t.NumElem(); i++ {
|
||||
if !visitType(baseOffset, t.Elem(), depth) {
|
||||
r = false
|
||||
break
|
||||
}
|
||||
baseOffset += t.Elem().Size()
|
||||
}
|
||||
case t.IsStruct():
|
||||
r = true
|
||||
if t.NumFields() == 0 {
|
||||
n++ // {} counts as a component
|
||||
break
|
||||
}
|
||||
for _, field := range t.Fields().Slice() {
|
||||
if !visitType(baseOffset+field.Offset, field.Type, depth) {
|
||||
r = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !r {
|
||||
writebyte(_dotdotdot)
|
||||
}
|
||||
writebyte(_endAgg)
|
||||
return r
|
||||
return true
|
||||
}
|
||||
|
||||
c := true
|
||||
for _, a := range abiInfo.InParams() {
|
||||
if !c {
|
||||
writebyte(_dotdotdot)
|
||||
if !visitType(a.FrameOffset(abiInfo), a.Type, 0) {
|
||||
break
|
||||
}
|
||||
c = visitType(a.FrameOffset(abiInfo), a.Type, 0)
|
||||
}
|
||||
writebyte(_endSeq)
|
||||
if wOff > maxLen {
|
||||
@ -7552,82 +7542,6 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name {
|
||||
return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
|
||||
ptrType := types.NewPtr(types.Types[types.TUINT8])
|
||||
lenType := types.Types[types.TINT]
|
||||
// Split this string up into two separate variables.
|
||||
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
|
||||
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
|
||||
return p, l
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
|
||||
n := name.N
|
||||
u := types.Types[types.TUINTPTR]
|
||||
t := types.NewPtr(types.Types[types.TUINT8])
|
||||
// Split this interface up into two separate variables.
|
||||
f := ".itab"
|
||||
if n.Type().IsEmptyInterface() {
|
||||
f = ".type"
|
||||
}
|
||||
c := e.SplitSlot(&name, f, 0, u) // see comment in typebits.Set
|
||||
d := e.SplitSlot(&name, ".data", u.Size(), t)
|
||||
return c, d
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
|
||||
ptrType := types.NewPtr(name.Type.Elem())
|
||||
lenType := types.Types[types.TINT]
|
||||
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
|
||||
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
|
||||
c := e.SplitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
|
||||
return p, l, c
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
|
||||
s := name.Type.Size() / 2
|
||||
var t *types.Type
|
||||
if s == 8 {
|
||||
t = types.Types[types.TFLOAT64]
|
||||
} else {
|
||||
t = types.Types[types.TFLOAT32]
|
||||
}
|
||||
r := e.SplitSlot(&name, ".real", 0, t)
|
||||
i := e.SplitSlot(&name, ".imag", t.Size(), t)
|
||||
return r, i
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
|
||||
var t *types.Type
|
||||
if name.Type.IsSigned() {
|
||||
t = types.Types[types.TINT32]
|
||||
} else {
|
||||
t = types.Types[types.TUINT32]
|
||||
}
|
||||
if Arch.LinkArch.ByteOrder == binary.BigEndian {
|
||||
return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[types.TUINT32])
|
||||
}
|
||||
return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[types.TUINT32])
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
|
||||
st := name.Type
|
||||
// Note: the _ field may appear several times. But
|
||||
// have no fear, identically-named but distinct Autos are
|
||||
// ok, albeit maybe confusing for a debugger.
|
||||
return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i))
|
||||
}
|
||||
|
||||
func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
|
||||
n := name.N
|
||||
at := name.Type
|
||||
if at.NumElem() != 1 {
|
||||
e.Fatalf(n.Pos(), "bad array size")
|
||||
}
|
||||
et := at.Elem()
|
||||
return e.SplitSlot(&name, "[0]", 0, et)
|
||||
}
|
||||
|
||||
func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
|
||||
return reflectdata.ITabSym(it, offset)
|
||||
}
|
||||
|
96
src/cmd/compile/internal/test/align_test.go
Normal file
96
src/cmd/compile/internal/test/align_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
// Test to make sure that equality functions (and hash
|
||||
// functions) don't do unaligned reads on architectures
|
||||
// that can't do unaligned reads. See issue 46283.
|
||||
|
||||
package test
|
||||
|
||||
import "testing"
|
||||
|
||||
type T1 struct {
|
||||
x float32
|
||||
a, b, c, d int16 // memequal64
|
||||
}
|
||||
type T2 struct {
|
||||
x float32
|
||||
a, b, c, d int32 // memequal128
|
||||
}
|
||||
|
||||
type A2 [2]byte // eq uses a 2-byte load
|
||||
type A4 [4]byte // eq uses a 4-byte load
|
||||
type A8 [8]byte // eq uses an 8-byte load
|
||||
|
||||
//go:noinline
|
||||
func cmpT1(p, q *T1) {
|
||||
if *p != *q {
|
||||
panic("comparison test wrong")
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func cmpT2(p, q *T2) {
|
||||
if *p != *q {
|
||||
panic("comparison test wrong")
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func cmpA2(p, q *A2) {
|
||||
if *p != *q {
|
||||
panic("comparison test wrong")
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func cmpA4(p, q *A4) {
|
||||
if *p != *q {
|
||||
panic("comparison test wrong")
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func cmpA8(p, q *A8) {
|
||||
if *p != *q {
|
||||
panic("comparison test wrong")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlignEqual(t *testing.T) {
|
||||
cmpT1(&T1{}, &T1{})
|
||||
cmpT2(&T2{}, &T2{})
|
||||
|
||||
m1 := map[T1]bool{}
|
||||
m1[T1{}] = true
|
||||
m1[T1{}] = false
|
||||
if len(m1) != 1 {
|
||||
t.Fatalf("len(m1)=%d, want 1", len(m1))
|
||||
}
|
||||
m2 := map[T2]bool{}
|
||||
m2[T2{}] = true
|
||||
m2[T2{}] = false
|
||||
if len(m2) != 1 {
|
||||
t.Fatalf("len(m2)=%d, want 1", len(m2))
|
||||
}
|
||||
|
||||
type X2 struct {
|
||||
y byte
|
||||
z A2
|
||||
}
|
||||
var x2 X2
|
||||
cmpA2(&x2.z, &A2{})
|
||||
type X4 struct {
|
||||
y byte
|
||||
z A4
|
||||
}
|
||||
var x4 X4
|
||||
cmpA4(&x4.z, &A4{})
|
||||
type X8 struct {
|
||||
y byte
|
||||
z A8
|
||||
}
|
||||
var x8 X8
|
||||
cmpA8(&x8.z, &A8{})
|
||||
}
|
@ -138,6 +138,7 @@ var runtimeDecls = [...]struct {
|
||||
{"growslice", funcTag, 116},
|
||||
{"unsafeslice", funcTag, 117},
|
||||
{"unsafeslice64", funcTag, 118},
|
||||
{"unsafeslicecheckptr", funcTag, 118},
|
||||
{"memmove", funcTag, 119},
|
||||
{"memclrNoHeapPointers", funcTag, 120},
|
||||
{"memclrHasPointers", funcTag, 120},
|
||||
@ -341,8 +342,8 @@ func runtimeTypes() []*types.Type {
|
||||
typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
|
||||
typs[115] = types.NewSlice(typs[2])
|
||||
typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115]))
|
||||
typs[117] = newSig(params(typs[1], typs[15]), nil)
|
||||
typs[118] = newSig(params(typs[1], typs[22]), nil)
|
||||
typs[117] = newSig(params(typs[1], typs[7], typs[15]), nil)
|
||||
typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
|
||||
typs[119] = newSig(params(typs[3], typs[3], typs[5]), nil)
|
||||
typs[120] = newSig(params(typs[7], typs[5]), nil)
|
||||
typs[121] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
|
||||
|
@ -183,8 +183,9 @@ func makeslice(typ *byte, len int, cap int) unsafe.Pointer
|
||||
func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer
|
||||
func makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
|
||||
func growslice(typ *byte, old []any, cap int) (ary []any)
|
||||
func unsafeslice(typ *byte, len int)
|
||||
func unsafeslice64(typ *byte, len int64)
|
||||
func unsafeslice(typ *byte, ptr unsafe.Pointer, len int)
|
||||
func unsafeslice64(typ *byte, ptr unsafe.Pointer, len int64)
|
||||
func unsafeslicecheckptr(typ *byte, ptr unsafe.Pointer, len int64)
|
||||
|
||||
func memmove(to *any, frm *any, length uintptr)
|
||||
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||
|
@ -633,6 +633,17 @@ func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
|
||||
if l.Type() == nil || r.Type() == nil {
|
||||
return l, r
|
||||
}
|
||||
|
||||
if !l.Type().IsInterface() && !r.Type().IsInterface() {
|
||||
// Can't mix bool with non-bool, string with non-string.
|
||||
if l.Type().IsBoolean() != r.Type().IsBoolean() {
|
||||
return l, r
|
||||
}
|
||||
if l.Type().IsString() != r.Type().IsString() {
|
||||
return l, r
|
||||
}
|
||||
}
|
||||
|
||||
if !l.Type().IsUntyped() {
|
||||
r = convlit(r, l.Type())
|
||||
return l, r
|
||||
@ -647,17 +658,10 @@ func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
|
||||
return l, r
|
||||
}
|
||||
|
||||
// Can't mix bool with non-bool, string with non-string, or nil with anything (untyped).
|
||||
if l.Type().IsBoolean() != r.Type().IsBoolean() {
|
||||
return l, r
|
||||
}
|
||||
if l.Type().IsString() != r.Type().IsString() {
|
||||
return l, r
|
||||
}
|
||||
// Can't mix nil with anything untyped.
|
||||
if ir.IsNil(l) || ir.IsNil(r) {
|
||||
return l, r
|
||||
}
|
||||
|
||||
t := defaultType(mixUntyped(l.Type(), r.Type()))
|
||||
l = convlit(l, t)
|
||||
r = convlit(r, t)
|
||||
|
@ -106,7 +106,17 @@ func Export(n *ir.Name) {
|
||||
// Redeclared emits a diagnostic about symbol s being redeclared at pos.
|
||||
func Redeclared(pos src.XPos, s *types.Sym, where string) {
|
||||
if !s.Lastlineno.IsKnown() {
|
||||
pkgName := DotImportRefs[s.Def.(*ir.Ident)]
|
||||
var pkgName *ir.PkgName
|
||||
if s.Def == nil {
|
||||
for id, pkg := range DotImportRefs {
|
||||
if id.Sym().Name == s.Name {
|
||||
pkgName = pkg
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pkgName = DotImportRefs[s.Def.(*ir.Ident)]
|
||||
}
|
||||
base.ErrorfAt(pos, "%v redeclared %s\n"+
|
||||
"\t%v: previous declaration during import %q", s, where, base.FmtPos(pkgName.Pos()), pkgName.Pkg.Path)
|
||||
} else {
|
||||
|
@ -981,6 +981,12 @@ func tcRecover(n *ir.CallExpr) ir.Node {
|
||||
|
||||
// tcUnsafeAdd typechecks an OUNSAFEADD node.
|
||||
func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
|
||||
if !types.AllowsGoVersion(curpkg(), 1, 17) {
|
||||
base.ErrorfVers("go1.17", "unsafe.Add")
|
||||
n.SetType(nil)
|
||||
return n
|
||||
}
|
||||
|
||||
n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add")
|
||||
n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT])
|
||||
if n.X.Type() == nil || n.Y.Type() == nil {
|
||||
@ -997,6 +1003,12 @@ func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
|
||||
|
||||
// tcUnsafeSlice typechecks an OUNSAFESLICE node.
|
||||
func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
|
||||
if !types.AllowsGoVersion(curpkg(), 1, 17) {
|
||||
base.ErrorfVers("go1.17", "unsafe.Slice")
|
||||
n.SetType(nil)
|
||||
return n
|
||||
}
|
||||
|
||||
n.X = Expr(n.X)
|
||||
n.Y = Expr(n.Y)
|
||||
if n.X.Type() == nil || n.Y.Type() == nil {
|
||||
@ -1006,7 +1018,14 @@ func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
|
||||
t := n.X.Type()
|
||||
if !t.IsPtr() {
|
||||
base.Errorf("first argument to unsafe.Slice must be pointer; have %L", t)
|
||||
} else if t.Elem().NotInHeap() {
|
||||
// TODO(mdempsky): This can be relaxed, but should only affect the
|
||||
// Go runtime itself. End users should only see //go:notinheap
|
||||
// types due to incomplete C structs in cgo, and those types don't
|
||||
// have a meaningful size anyway.
|
||||
base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed")
|
||||
}
|
||||
|
||||
if !checkunsafeslice(&n.Y) {
|
||||
n.SetType(nil)
|
||||
return n
|
||||
|
@ -1540,7 +1540,7 @@ func (r *importReader) exprsOrNil() (a, b ir.Node) {
|
||||
func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
|
||||
if go117ExportTypes {
|
||||
// These should all be encoded as direct ops, not OCALL.
|
||||
base.Fatalf("builtinCall should not be invoked when types are included in inport/export")
|
||||
base.Fatalf("builtinCall should not be invoked when types are included in import/export")
|
||||
}
|
||||
return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
|
||||
}
|
||||
|
@ -204,8 +204,20 @@ assignOK:
|
||||
r.Use = ir.CallUseList
|
||||
rtyp := r.Type()
|
||||
|
||||
mismatched := false
|
||||
failed := false
|
||||
for i := range lhs {
|
||||
assignType(i, rtyp.Field(i).Type)
|
||||
result := rtyp.Field(i).Type
|
||||
assignType(i, result)
|
||||
|
||||
if lhs[i].Type() == nil || result == nil {
|
||||
failed = true
|
||||
} else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) {
|
||||
mismatched = true
|
||||
}
|
||||
}
|
||||
if mismatched && !failed {
|
||||
rewriteMultiValueCall(stmt, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -945,16 +945,18 @@ func typecheckargs(n ir.InitNode) {
|
||||
return
|
||||
}
|
||||
|
||||
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
|
||||
|
||||
// Save n as n.Orig for fmt.go.
|
||||
if ir.Orig(n) == n {
|
||||
n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
|
||||
}
|
||||
|
||||
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
|
||||
as.Rhs.Append(list...)
|
||||
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
|
||||
rewriteMultiValueCall(n, list[0])
|
||||
}
|
||||
|
||||
// rewriteMultiValueCall rewrites multi-valued f() to use temporaries,
|
||||
// so the backend wouldn't need to worry about tuple-valued expressions.
|
||||
func rewriteMultiValueCall(n ir.InitNode, call ir.Node) {
|
||||
// If we're outside of function context, then this call will
|
||||
// be executed during the generated init function. However,
|
||||
// init.go hasn't yet created it. Instead, associate the
|
||||
@ -964,25 +966,40 @@ func typecheckargs(n ir.InitNode) {
|
||||
if static {
|
||||
ir.CurFunc = InitTodoFunc
|
||||
}
|
||||
list = nil
|
||||
for _, f := range t.FieldSlice() {
|
||||
t := Temp(f.Type)
|
||||
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
|
||||
as.Lhs.Append(t)
|
||||
list = append(list, t)
|
||||
|
||||
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, []ir.Node{call})
|
||||
results := call.Type().FieldSlice()
|
||||
list := make([]ir.Node, len(results))
|
||||
for i, result := range results {
|
||||
tmp := Temp(result.Type)
|
||||
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, tmp))
|
||||
as.Lhs.Append(tmp)
|
||||
list[i] = tmp
|
||||
}
|
||||
if static {
|
||||
ir.CurFunc = nil
|
||||
}
|
||||
|
||||
n.PtrInit().Append(Stmt(as))
|
||||
|
||||
switch n := n.(type) {
|
||||
default:
|
||||
base.Fatalf("rewriteMultiValueCall %+v", n.Op())
|
||||
case *ir.CallExpr:
|
||||
n.Args = list
|
||||
case *ir.ReturnStmt:
|
||||
n.Results = list
|
||||
case *ir.AssignListStmt:
|
||||
if n.Op() != ir.OAS2FUNC {
|
||||
base.Fatalf("rewriteMultiValueCall: invalid op %v", n.Op())
|
||||
}
|
||||
as.SetOp(ir.OAS2FUNC)
|
||||
n.SetOp(ir.OAS2)
|
||||
n.Rhs = make([]ir.Node, len(list))
|
||||
for i, tmp := range list {
|
||||
n.Rhs[i] = AssignConv(tmp, n.Lhs[i].Type(), "assignment")
|
||||
}
|
||||
}
|
||||
|
||||
n.PtrInit().Append(Stmt(as))
|
||||
}
|
||||
|
||||
func checksliceindex(l ir.Node, r ir.Node, tp *types.Type) bool {
|
||||
@ -1443,15 +1460,22 @@ toomany:
|
||||
}
|
||||
|
||||
func errorDetails(nl ir.Nodes, tstruct *types.Type, isddd bool) string {
|
||||
// If we don't know any type at a call site, let's suppress any return
|
||||
// message signatures. See Issue https://golang.org/issues/19012.
|
||||
// Suppress any return message signatures if:
|
||||
//
|
||||
// (1) We don't know any type at a call site (see #19012).
|
||||
// (2) Any node has an unknown type.
|
||||
// (3) Invalid type for variadic parameter (see #46957).
|
||||
if tstruct == nil {
|
||||
return ""
|
||||
return "" // case 1
|
||||
}
|
||||
// If any node has an unknown type, suppress it as well
|
||||
|
||||
if isddd && !nl[len(nl)-1].Type().IsSlice() {
|
||||
return "" // case 3
|
||||
}
|
||||
|
||||
for _, n := range nl {
|
||||
if n.Type() == nil {
|
||||
return ""
|
||||
return "" // case 2
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("\n\thave %s\n\twant %v", fmtSignature(nl, isddd), tstruct)
|
||||
|
@ -109,14 +109,18 @@ func sconv(s *Sym, verb rune, mode fmtMode) string {
|
||||
return "<S>"
|
||||
}
|
||||
|
||||
if s.Name == "_" {
|
||||
return "_"
|
||||
q := pkgqual(s.Pkg, verb, mode)
|
||||
if q == "" {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
buf := fmtBufferPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
defer fmtBufferPool.Put(buf)
|
||||
|
||||
symfmt(buf, s, verb, mode)
|
||||
buf.WriteString(q)
|
||||
buf.WriteByte('.')
|
||||
buf.WriteString(s.Name)
|
||||
return InternString(buf.Bytes())
|
||||
}
|
||||
|
||||
@ -128,56 +132,49 @@ func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
|
||||
b.WriteString("<S>")
|
||||
return
|
||||
}
|
||||
if s.Name == "_" {
|
||||
b.WriteString("_")
|
||||
return
|
||||
}
|
||||
|
||||
symfmt(b, s, verb, mode)
|
||||
}
|
||||
|
||||
func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
|
||||
if q := pkgqual(s.Pkg, verb, mode); q != "" {
|
||||
b.WriteString(q)
|
||||
b.WriteByte('.')
|
||||
}
|
||||
b.WriteString(s.Name)
|
||||
}
|
||||
|
||||
// pkgqual returns the qualifier that should be used for printing
|
||||
// symbols from the given package in the given mode.
|
||||
// If it returns the empty string, no qualification is needed.
|
||||
func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
|
||||
if verb != 'S' {
|
||||
switch mode {
|
||||
case fmtGo: // This is for the user
|
||||
if s.Pkg == BuiltinPkg || s.Pkg == LocalPkg {
|
||||
b.WriteString(s.Name)
|
||||
return
|
||||
if pkg == BuiltinPkg || pkg == LocalPkg {
|
||||
return ""
|
||||
}
|
||||
|
||||
// If the name was used by multiple packages, display the full path,
|
||||
if s.Pkg.Name != "" && NumImport[s.Pkg.Name] > 1 {
|
||||
fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name)
|
||||
return
|
||||
if pkg.Name != "" && NumImport[pkg.Name] > 1 {
|
||||
return strconv.Quote(pkg.Path)
|
||||
}
|
||||
b.WriteString(s.Pkg.Name)
|
||||
b.WriteByte('.')
|
||||
b.WriteString(s.Name)
|
||||
return
|
||||
return pkg.Name
|
||||
|
||||
case fmtDebug:
|
||||
b.WriteString(s.Pkg.Name)
|
||||
b.WriteByte('.')
|
||||
b.WriteString(s.Name)
|
||||
return
|
||||
return pkg.Name
|
||||
|
||||
case fmtTypeIDName:
|
||||
// dcommontype, typehash
|
||||
b.WriteString(s.Pkg.Name)
|
||||
b.WriteByte('.')
|
||||
b.WriteString(s.Name)
|
||||
return
|
||||
return pkg.Name
|
||||
|
||||
case fmtTypeID:
|
||||
// (methodsym), typesym, weaksym
|
||||
b.WriteString(s.Pkg.Prefix)
|
||||
b.WriteByte('.')
|
||||
b.WriteString(s.Name)
|
||||
return
|
||||
return pkg.Prefix
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(s.Name)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Type
|
||||
|
@ -579,6 +579,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
case _Add:
|
||||
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
|
||||
if !check.allowVersion(check.pkg, 1, 17) {
|
||||
check.error(call.Fun, "unsafe.Add requires go1.17 or later")
|
||||
return
|
||||
}
|
||||
|
||||
check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add")
|
||||
if x.mode == invalid {
|
||||
return
|
||||
@ -675,6 +680,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||
|
||||
case _Slice:
|
||||
// unsafe.Slice(ptr *T, len IntegerType) []T
|
||||
if !check.allowVersion(check.pkg, 1, 17) {
|
||||
check.error(call.Fun, "unsafe.Slice requires go1.17 or later")
|
||||
return
|
||||
}
|
||||
|
||||
typ := asPointer(x.typ)
|
||||
if typ == nil {
|
||||
check.errorf(x, invalidArg+"%s is not a pointer", x)
|
||||
|
@ -489,7 +489,7 @@ func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
|
||||
base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type().Elem())
|
||||
}
|
||||
if n.Esc() == ir.EscNone {
|
||||
if t.Size() >= ir.MaxImplicitStackVarSize {
|
||||
if t.Size() > ir.MaxImplicitStackVarSize {
|
||||
base.Fatalf("large ONEW with EscNone: %v", n)
|
||||
}
|
||||
return stackTempAddr(init, t)
|
||||
@ -654,36 +654,28 @@ func walkRecover(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
|
||||
}
|
||||
|
||||
func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
|
||||
ptr := safeExpr(n.X, init)
|
||||
len := safeExpr(n.Y, init)
|
||||
|
||||
fnname := "unsafeslice64"
|
||||
argtype := types.Types[types.TINT64]
|
||||
lenType := types.Types[types.TINT64]
|
||||
|
||||
// Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
|
||||
// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
|
||||
// will be handled by the negative range checks in unsafeslice during runtime.
|
||||
if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
|
||||
if ir.ShouldCheckPtr(ir.CurFunc, 1) {
|
||||
fnname = "unsafeslicecheckptr"
|
||||
// for simplicity, unsafeslicecheckptr always uses int64
|
||||
} else if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
|
||||
fnname = "unsafeslice"
|
||||
argtype = types.Types[types.TINT]
|
||||
lenType = types.Types[types.TINT]
|
||||
}
|
||||
|
||||
t := n.Type()
|
||||
|
||||
// Call runtime.unsafeslice[64] to check that the length argument is
|
||||
// non-negative and smaller than the max length allowed for the
|
||||
// element type.
|
||||
// Call runtime.unsafeslice{,64,checkptr} to check ptr and len.
|
||||
fn := typecheck.LookupRuntime(fnname)
|
||||
init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype)))
|
||||
|
||||
ptr := walkExpr(n.X, init)
|
||||
|
||||
c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, ptr)
|
||||
c.SetTypecheck(1)
|
||||
init.Append(c)
|
||||
|
||||
// TODO(mdempsky): checkptr instrumentation. Maybe merge into length
|
||||
// check above, along with nil check? Need to be careful about
|
||||
// notinheap pointers though: can't pass them as unsafe.Pointer.
|
||||
init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), typecheck.Conv(len, lenType)))
|
||||
|
||||
h := ir.NewSliceHeaderExpr(n.Pos(), t,
|
||||
typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
|
||||
|
@ -313,7 +313,7 @@ func mayCall(n ir.Node) bool {
|
||||
return true
|
||||
|
||||
case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
|
||||
ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD:
|
||||
ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD, ir.OSLICE2ARRPTR:
|
||||
// These ops might panic, make sure they are done
|
||||
// before we start marshaling args for a call. See issue 16760.
|
||||
return true
|
||||
|
16
src/cmd/dist/build.go
vendored
16
src/cmd/dist/build.go
vendored
@ -1607,6 +1607,18 @@ var incomplete = map[string]bool{
|
||||
"linux/sparc64": true,
|
||||
}
|
||||
|
||||
// List of platforms which are first class ports. See golang.org/issue/38874.
|
||||
var firstClass = map[string]bool{
|
||||
"darwin/amd64": true,
|
||||
"darwin/arm64": true,
|
||||
"linux/386": true,
|
||||
"linux/amd64": true,
|
||||
"linux/arm": true,
|
||||
"linux/arm64": true,
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
}
|
||||
|
||||
func needCC() bool {
|
||||
switch os.Getenv("CGO_ENABLED") {
|
||||
case "1":
|
||||
@ -1743,6 +1755,7 @@ func cmdlist() {
|
||||
GOOS string
|
||||
GOARCH string
|
||||
CgoSupported bool
|
||||
FirstClass bool
|
||||
}
|
||||
var results []jsonResult
|
||||
for _, p := range plats {
|
||||
@ -1750,7 +1763,8 @@ func cmdlist() {
|
||||
results = append(results, jsonResult{
|
||||
GOOS: fields[0],
|
||||
GOARCH: fields[1],
|
||||
CgoSupported: cgoEnabled[p]})
|
||||
CgoSupported: cgoEnabled[p],
|
||||
FirstClass: firstClass[p]})
|
||||
}
|
||||
out, err := json.MarshalIndent(results, "", "\t")
|
||||
if err != nil {
|
||||
|
33
src/cmd/dist/test.go
vendored
33
src/cmd/dist/test.go
vendored
@ -722,14 +722,29 @@ func (t *tester) registerTests() {
|
||||
},
|
||||
})
|
||||
if t.hasCxx() {
|
||||
t.tests = append(t.tests, distTest{
|
||||
name: "swig_callback",
|
||||
heading: "../misc/swig/callback",
|
||||
fn: func(dt *distTest) error {
|
||||
t.addCmd(dt, "misc/swig/callback", t.goTest())
|
||||
return nil
|
||||
t.tests = append(t.tests,
|
||||
distTest{
|
||||
name: "swig_callback",
|
||||
heading: "../misc/swig/callback",
|
||||
fn: func(dt *distTest) error {
|
||||
t.addCmd(dt, "misc/swig/callback", t.goTest())
|
||||
return nil
|
||||
},
|
||||
},
|
||||
})
|
||||
distTest{
|
||||
name: "swig_callback_lto",
|
||||
heading: "../misc/swig/callback",
|
||||
fn: func(dt *distTest) error {
|
||||
cmd := t.addCmd(dt, "misc/swig/callback", t.goTest())
|
||||
cmd.Env = append(os.Environ(),
|
||||
"CGO_CFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
|
||||
"CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
|
||||
"CGO_LDFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
|
||||
)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -766,7 +781,7 @@ func (t *tester) registerTests() {
|
||||
t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", ".")
|
||||
}
|
||||
if goos == "linux" && goarch != "ppc64le" {
|
||||
// because syscall.SysProcAttri struct used in misc/cgo/testsanitizers is only built on linux.
|
||||
// because syscall.SysProcAttr struct used in misc/cgo/testsanitizers is only built on linux.
|
||||
// Some inconsistent failures happen on ppc64le so disable for now.
|
||||
t.registerHostTest("testsanitizers", "../misc/cgo/testsanitizers", "misc/cgo/testsanitizers", ".")
|
||||
}
|
||||
@ -1042,7 +1057,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
|
||||
"darwin-amd64", "darwin-arm64",
|
||||
"freebsd-amd64",
|
||||
"android-arm", "android-arm64", "android-386",
|
||||
"windows-amd64", "windows-386":
|
||||
"windows-amd64", "windows-386", "windows-arm64":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
3
src/cmd/dist/vfp_arm.s
vendored
3
src/cmd/dist/vfp_arm.s
vendored
@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gc,arm
|
||||
//go:build gc
|
||||
// +build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
3
src/cmd/dist/vfp_default.s
vendored
3
src/cmd/dist/vfp_default.s
vendored
@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm,gc
|
||||
//go:build gc && !arm
|
||||
// +build gc,!arm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
@ -3,13 +3,13 @@ module cmd
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5
|
||||
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
|
||||
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e
|
||||
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect
|
||||
golang.org/x/mod v0.4.3-0.20210504181020-67f1c1edc27a
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
|
||||
golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a
|
||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56
|
||||
golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5
|
||||
golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
)
|
||||
|
@ -1,22 +1,45 @@
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5 h1:zIaiqGYDQwa4HVx5wGRTXbx38Pqxjemn4BP98wpzpXo=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a h1:jmAp/2PZAScNd62lTD3Mcb0Ey9FvIIJtLohPhtxZJ+Q=
|
||||
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e h1:pv3V0NlNSh5Q6AX/StwGLBjcLS7UN4m4Gq+V+uSecqM=
|
||||
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI=
|
||||
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/mod v0.4.3-0.20210504181020-67f1c1edc27a h1:wbpC/7Wbo5WFVox32n+KjhRRLmTLq8YW/wRlL2iVAhk=
|
||||
golang.org/x/mod v0.4.3-0.20210504181020-67f1c1edc27a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a h1:e8qnjKz4EE6OjRki9wTadWSIogINvq10sMcuBRORxMY=
|
||||
golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q=
|
||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5 h1:ImcI7RFHWLu2QWpFDXaReu0j+sQAHIy65vUFZImXiqY=
|
||||
golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5/go.mod h1:sH/Eidr0EddymY8HZSakBo32zU3fG5ovDq874hJLjVg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 h1:2XlR/j4I4xz5GQZI7zBjqTfezYyRIE2jD5IMousB2rg=
|
||||
golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
@ -174,8 +174,8 @@
|
||||
// a build will run as if the disk file path exists with the contents
|
||||
// given by the backing file paths, or as if the disk file path does not
|
||||
// exist if its backing file path is empty. Support for the -overlay flag
|
||||
// has some limitations:importantly, cgo files included from outside the
|
||||
// include path must be in the same directory as the Go package they are
|
||||
// has some limitations: importantly, cgo files included from outside the
|
||||
// include path must be in the same directory as the Go package they are
|
||||
// included from, and overlays will not appear when binaries and tests are
|
||||
// run through go run and go test respectively.
|
||||
// -pkgdir dir
|
||||
@ -293,7 +293,7 @@
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]
|
||||
// go doc [doc flags] [package|[package.]symbol[.methodOrField]]
|
||||
//
|
||||
// Doc prints the documentation comments associated with the item identified by its
|
||||
// arguments (a package, const, func, type, var, method, or struct field)
|
||||
@ -1078,7 +1078,7 @@
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go mod edit [editing flags] [go.mod]
|
||||
// go mod edit [editing flags] [-fmt|-print|-json] [go.mod]
|
||||
//
|
||||
// Edit provides a command-line interface for editing go.mod,
|
||||
// for use primarily by tools or scripts. It reads only go.mod;
|
||||
@ -1186,13 +1186,17 @@
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go mod graph
|
||||
// go mod graph [-go=version]
|
||||
//
|
||||
// Graph prints the module requirement graph (with replacements applied)
|
||||
// in text form. Each line in the output has two space-separated fields: a module
|
||||
// and one of its requirements. Each module is identified as a string of the form
|
||||
// path@version, except for the main module, which has no @version suffix.
|
||||
//
|
||||
// The -go flag causes graph to report the module graph as loaded by the
|
||||
// given Go version, instead of the version indicated by the 'go' directive
|
||||
// in the go.mod file.
|
||||
//
|
||||
// See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.
|
||||
//
|
||||
//
|
||||
@ -1200,7 +1204,7 @@
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go mod init [module]
|
||||
// go mod init [module-path]
|
||||
//
|
||||
// Init initializes and writes a new go.mod file in the current directory, in
|
||||
// effect creating a new module rooted at the current directory. The go.mod file
|
||||
@ -1221,7 +1225,7 @@
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go mod tidy [-e] [-v] [-go=version]
|
||||
// go mod tidy [-e] [-v] [-go=version] [-compat=version]
|
||||
//
|
||||
// Tidy makes sure go.mod matches the source code in the module.
|
||||
// It adds any missing modules necessary to build the current module's
|
||||
@ -1241,6 +1245,14 @@
|
||||
// (Go versions 1.17 and higher retain more requirements in order to
|
||||
// support lazy module loading.)
|
||||
//
|
||||
// The -compat flag preserves any additional checksums needed for the
|
||||
// 'go' command from the indicated major Go release to successfully load
|
||||
// the module graph, and causes tidy to error out if that version of the
|
||||
// 'go' command would load any imported package from a different module
|
||||
// version. By default, tidy acts as if the -compat flag were set to the
|
||||
// version prior to the one indicated by the 'go' directive in the go.mod
|
||||
// file.
|
||||
//
|
||||
// See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
|
||||
//
|
||||
//
|
||||
@ -1560,7 +1572,7 @@
|
||||
//
|
||||
// A build constraint, also known as a build tag, is a line comment that begins
|
||||
//
|
||||
// // +build
|
||||
// //go:build
|
||||
//
|
||||
// that lists the conditions under which a file should be included in the package.
|
||||
// Constraints may appear in any kind of source file (not just Go), but
|
||||
@ -1568,30 +1580,20 @@
|
||||
// only by blank lines and other line comments. These rules mean that in Go
|
||||
// files a build constraint must appear before the package clause.
|
||||
//
|
||||
// To distinguish build constraints from package documentation, a series of
|
||||
// build constraints must be followed by a blank line.
|
||||
// To distinguish build constraints from package documentation,
|
||||
// a build constraint should be followed by a blank line.
|
||||
//
|
||||
// A build constraint is evaluated as the OR of space-separated options.
|
||||
// Each option evaluates as the AND of its comma-separated terms.
|
||||
// Each term consists of letters, digits, underscores, and dots.
|
||||
// A term may be negated with a preceding !.
|
||||
// For example, the build constraint:
|
||||
// A build constraint is evaluated as an expression containing options
|
||||
// combined by ||, &&, and ! operators and parentheses. Operators have
|
||||
// the same meaning as in Go.
|
||||
//
|
||||
// // +build linux,386 darwin,!cgo
|
||||
// For example, the following build constraint constrains a file to
|
||||
// build when the "linux" and "386" constraints are satisfied, or when
|
||||
// "darwin" is satisfied and "cgo" is not:
|
||||
//
|
||||
// corresponds to the boolean formula:
|
||||
// //go:build (linux && 386) || (darwin && !cgo)
|
||||
//
|
||||
// (linux AND 386) OR (darwin AND (NOT cgo))
|
||||
//
|
||||
// A file may have multiple build constraints. The overall constraint is the AND
|
||||
// of the individual constraints. That is, the build constraints:
|
||||
//
|
||||
// // +build linux darwin
|
||||
// // +build amd64
|
||||
//
|
||||
// corresponds to the boolean formula:
|
||||
//
|
||||
// (linux OR darwin) AND amd64
|
||||
// It is an error for a file to have more than one //go:build line.
|
||||
//
|
||||
// During a particular build, the following words are satisfied:
|
||||
//
|
||||
@ -1629,24 +1631,28 @@
|
||||
//
|
||||
// To keep a file from being considered for the build:
|
||||
//
|
||||
// // +build ignore
|
||||
// //go:build ignore
|
||||
//
|
||||
// (any other unsatisfied word will work as well, but "ignore" is conventional.)
|
||||
//
|
||||
// To build a file only when using cgo, and only on Linux and OS X:
|
||||
//
|
||||
// // +build linux,cgo darwin,cgo
|
||||
// //go:build cgo && (linux || darwin)
|
||||
//
|
||||
// Such a file is usually paired with another file implementing the
|
||||
// default functionality for other systems, which in this case would
|
||||
// carry the constraint:
|
||||
//
|
||||
// // +build !linux,!darwin !cgo
|
||||
// //go:build !(cgo && (linux || darwin))
|
||||
//
|
||||
// Naming a file dns_windows.go will cause it to be included only when
|
||||
// building the package for Windows; similarly, math_386.s will be included
|
||||
// only when building the package for 32-bit x86.
|
||||
//
|
||||
// Go versions 1.16 and earlier used a different syntax for build constraints,
|
||||
// with a "// +build" prefix. The gofmt command will add an equivalent //go:build
|
||||
// constraint when encountering the older syntax.
|
||||
//
|
||||
//
|
||||
// Build modes
|
||||
//
|
||||
@ -1885,6 +1891,9 @@
|
||||
// GOMIPS64
|
||||
// For GOARCH=mips64{,le}, whether to use floating point instructions.
|
||||
// Valid values are hardfloat (default), softfloat.
|
||||
// GOPPC64
|
||||
// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
|
||||
// Valid values are power8 (default), power9.
|
||||
// GOWASM
|
||||
// For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
|
||||
// Valid values are satconv, signext.
|
||||
@ -1894,6 +1903,12 @@
|
||||
// GCCGOTOOLDIR
|
||||
// If set, where to find gccgo tools, such as cgo.
|
||||
// The default is based on how gccgo was configured.
|
||||
// GOEXPERIMENT
|
||||
// Comma-separated list of toolchain experiments to enable or disable.
|
||||
// The list of available experiments may change arbitrarily over time.
|
||||
// See src/internal/goexperiment/flags.go for currently valid values.
|
||||
// Warning: This variable is provided for the development and testing
|
||||
// of the Go toolchain itself. Use beyond that purpose is unsupported.
|
||||
// GOROOT_FINAL
|
||||
// The root of the installed Go tree, when it is
|
||||
// installed in a location other than where it is built.
|
||||
|
@ -72,7 +72,6 @@ func tooSlow(t *testing.T) {
|
||||
// (temp) directory.
|
||||
var testGOROOT string
|
||||
|
||||
var testCC string
|
||||
var testGOCACHE string
|
||||
|
||||
var testGo string
|
||||
@ -179,13 +178,6 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
out, err = exec.Command(gotool, "env", "CC").CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "could not find testing CC: %v\n%s", err, out)
|
||||
os.Exit(2)
|
||||
}
|
||||
testCC = strings.TrimSpace(string(out))
|
||||
|
||||
cmd := exec.Command(testGo, "env", "CGO_ENABLED")
|
||||
cmd.Stderr = new(strings.Builder)
|
||||
if out, err := cmd.Output(); err != nil {
|
||||
@ -2193,7 +2185,7 @@ func testBuildmodePIE(t *testing.T, useCgo, setBuildmodeToPIE bool) {
|
||||
// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
|
||||
section := f.Section(".edata")
|
||||
if section == nil {
|
||||
t.Fatalf(".edata section is not present")
|
||||
t.Skip(".edata section is not present")
|
||||
}
|
||||
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
|
||||
type IMAGE_EXPORT_DIRECTORY struct {
|
||||
@ -2864,3 +2856,35 @@ func TestExecInDeletedDir(t *testing.T) {
|
||||
// `go version` should not fail
|
||||
tg.run("version")
|
||||
}
|
||||
|
||||
// A missing C compiler should not force the net package to be stale.
|
||||
// Issue 47215.
|
||||
func TestMissingCC(t *testing.T) {
|
||||
if !canCgo {
|
||||
t.Skip("test is only meaningful on systems with cgo")
|
||||
}
|
||||
cc := os.Getenv("CC")
|
||||
if cc == "" {
|
||||
cc = "gcc"
|
||||
}
|
||||
if filepath.IsAbs(cc) {
|
||||
t.Skipf(`"CC" (%s) is an absolute path`, cc)
|
||||
}
|
||||
_, err := exec.LookPath(cc)
|
||||
if err != nil {
|
||||
t.Skipf(`"CC" (%s) not on PATH`, cc)
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
netStale, _ := tg.isStale("net")
|
||||
if netStale {
|
||||
t.Skip(`skipping test because "net" package is currently stale`)
|
||||
}
|
||||
|
||||
tg.setenv("PATH", "") // No C compiler on PATH.
|
||||
netStale, _ = tg.isStale("net")
|
||||
if netStale {
|
||||
t.Error(`clearing "PATH" causes "net" to be stale`)
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,14 @@ func defaultContext() build.Context {
|
||||
ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
|
||||
ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
|
||||
|
||||
// The experiments flags are based on GOARCH, so they may
|
||||
// need to change. TODO: This should be cleaned up.
|
||||
buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT))
|
||||
ctxt.ToolTags = nil
|
||||
for _, exp := range buildcfg.EnabledExperiments() {
|
||||
ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
|
||||
}
|
||||
|
||||
// The go/build rule for whether cgo is enabled is:
|
||||
// 1. If $CGO_ENABLED is set, respect it.
|
||||
// 2. Otherwise, if this is a cross-compile, disable cgo.
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
|
||||
var CmdDoc = &base.Command{
|
||||
Run: runDoc,
|
||||
UsageLine: "go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
|
||||
UsageLine: "go doc [doc flags] [package|[package.]symbol[.methodOrField]]",
|
||||
CustomFlags: true,
|
||||
Short: "show documentation for package or symbol",
|
||||
Long: `
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"internal/buildcfg"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -72,6 +73,7 @@ func MkEnv() []cfg.EnvVar {
|
||||
{Name: "GOCACHE", Value: cache.DefaultDir()},
|
||||
{Name: "GOENV", Value: envFile},
|
||||
{Name: "GOEXE", Value: cfg.ExeSuffix},
|
||||
{Name: "GOEXPERIMENT", Value: buildcfg.GOEXPERIMENT()},
|
||||
{Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
|
||||
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
|
||||
{Name: "GOHOSTOS", Value: runtime.GOOS},
|
||||
@ -197,6 +199,21 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if *envU && *envW {
|
||||
base.Fatalf("go env: cannot use -u with -w")
|
||||
}
|
||||
|
||||
// Handle 'go env -w' and 'go env -u' before calling buildcfg.Check,
|
||||
// so they can be used to recover from an invalid configuration.
|
||||
if *envW {
|
||||
runEnvW(args)
|
||||
return
|
||||
}
|
||||
|
||||
if *envU {
|
||||
runEnvU(args)
|
||||
return
|
||||
}
|
||||
|
||||
buildcfg.Check()
|
||||
|
||||
env := cfg.CmdEnv
|
||||
env = append(env, ExtraEnvVars()...)
|
||||
|
||||
@ -206,14 +223,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
||||
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
|
||||
needCostly := false
|
||||
if *envU || *envW {
|
||||
// We're overwriting or removing default settings,
|
||||
// so it doesn't really matter what the existing settings are.
|
||||
//
|
||||
// Moreover, we haven't validated the new settings yet, so it is
|
||||
// important that we NOT perform any actions based on them,
|
||||
// such as initializing the builder to compute other variables.
|
||||
} else if len(args) == 0 {
|
||||
if len(args) == 0 {
|
||||
// We're listing all environment variables ("go env"),
|
||||
// including the expensive ones.
|
||||
needCostly = true
|
||||
@ -238,95 +248,6 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
||||
env = append(env, ExtraEnvVarsCostly()...)
|
||||
}
|
||||
|
||||
if *envW {
|
||||
// Process and sanity-check command line.
|
||||
if len(args) == 0 {
|
||||
base.Fatalf("go env -w: no KEY=VALUE arguments given")
|
||||
}
|
||||
osEnv := make(map[string]string)
|
||||
for _, e := range cfg.OrigEnv {
|
||||
if i := strings.Index(e, "="); i >= 0 {
|
||||
osEnv[e[:i]] = e[i+1:]
|
||||
}
|
||||
}
|
||||
add := make(map[string]string)
|
||||
for _, arg := range args {
|
||||
i := strings.Index(arg, "=")
|
||||
if i < 0 {
|
||||
base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
|
||||
}
|
||||
key, val := arg[:i], arg[i+1:]
|
||||
if err := checkEnvWrite(key, val); err != nil {
|
||||
base.Fatalf("go env -w: %v", err)
|
||||
}
|
||||
if _, ok := add[key]; ok {
|
||||
base.Fatalf("go env -w: multiple values for key: %s", key)
|
||||
}
|
||||
add[key] = val
|
||||
if osVal := osEnv[key]; osVal != "" && osVal != val {
|
||||
fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
|
||||
}
|
||||
}
|
||||
|
||||
goos, okGOOS := add["GOOS"]
|
||||
goarch, okGOARCH := add["GOARCH"]
|
||||
if okGOOS || okGOARCH {
|
||||
if !okGOOS {
|
||||
goos = cfg.Goos
|
||||
}
|
||||
if !okGOARCH {
|
||||
goarch = cfg.Goarch
|
||||
}
|
||||
if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
|
||||
base.Fatalf("go env -w: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
gotmp, okGOTMP := add["GOTMPDIR"]
|
||||
if okGOTMP {
|
||||
if !filepath.IsAbs(gotmp) && gotmp != "" {
|
||||
base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
|
||||
}
|
||||
}
|
||||
|
||||
updateEnvFile(add, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if *envU {
|
||||
// Process and sanity-check command line.
|
||||
if len(args) == 0 {
|
||||
base.Fatalf("go env -u: no arguments given")
|
||||
}
|
||||
del := make(map[string]bool)
|
||||
for _, arg := range args {
|
||||
if err := checkEnvWrite(arg, ""); err != nil {
|
||||
base.Fatalf("go env -u: %v", err)
|
||||
}
|
||||
del[arg] = true
|
||||
}
|
||||
if del["GOOS"] || del["GOARCH"] {
|
||||
goos, goarch := cfg.Goos, cfg.Goarch
|
||||
if del["GOOS"] {
|
||||
goos = getOrigEnv("GOOS")
|
||||
if goos == "" {
|
||||
goos = build.Default.GOOS
|
||||
}
|
||||
}
|
||||
if del["GOARCH"] {
|
||||
goarch = getOrigEnv("GOARCH")
|
||||
if goarch == "" {
|
||||
goarch = build.Default.GOARCH
|
||||
}
|
||||
}
|
||||
if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
|
||||
base.Fatalf("go env -u: %v", err)
|
||||
}
|
||||
}
|
||||
updateEnvFile(nil, del)
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
if *envJson {
|
||||
var es []cfg.EnvVar
|
||||
@ -351,6 +272,109 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
||||
PrintEnv(os.Stdout, env)
|
||||
}
|
||||
|
||||
func runEnvW(args []string) {
|
||||
// Process and sanity-check command line.
|
||||
if len(args) == 0 {
|
||||
base.Fatalf("go env -w: no KEY=VALUE arguments given")
|
||||
}
|
||||
osEnv := make(map[string]string)
|
||||
for _, e := range cfg.OrigEnv {
|
||||
if i := strings.Index(e, "="); i >= 0 {
|
||||
osEnv[e[:i]] = e[i+1:]
|
||||
}
|
||||
}
|
||||
add := make(map[string]string)
|
||||
for _, arg := range args {
|
||||
i := strings.Index(arg, "=")
|
||||
if i < 0 {
|
||||
base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
|
||||
}
|
||||
key, val := arg[:i], arg[i+1:]
|
||||
if err := checkEnvWrite(key, val); err != nil {
|
||||
base.Fatalf("go env -w: %v", err)
|
||||
}
|
||||
if _, ok := add[key]; ok {
|
||||
base.Fatalf("go env -w: multiple values for key: %s", key)
|
||||
}
|
||||
add[key] = val
|
||||
if osVal := osEnv[key]; osVal != "" && osVal != val {
|
||||
fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkBuildConfig(add, nil); err != nil {
|
||||
base.Fatalf("go env -w: %v", err)
|
||||
}
|
||||
|
||||
gotmp, okGOTMP := add["GOTMPDIR"]
|
||||
if okGOTMP {
|
||||
if !filepath.IsAbs(gotmp) && gotmp != "" {
|
||||
base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
|
||||
}
|
||||
}
|
||||
|
||||
updateEnvFile(add, nil)
|
||||
}
|
||||
|
||||
func runEnvU(args []string) {
|
||||
// Process and sanity-check command line.
|
||||
if len(args) == 0 {
|
||||
base.Fatalf("go env -u: no arguments given")
|
||||
}
|
||||
del := make(map[string]bool)
|
||||
for _, arg := range args {
|
||||
if err := checkEnvWrite(arg, ""); err != nil {
|
||||
base.Fatalf("go env -u: %v", err)
|
||||
}
|
||||
del[arg] = true
|
||||
}
|
||||
|
||||
if err := checkBuildConfig(nil, del); err != nil {
|
||||
base.Fatalf("go env -u: %v", err)
|
||||
}
|
||||
|
||||
updateEnvFile(nil, del)
|
||||
}
|
||||
|
||||
// checkBuildConfig checks whether the build configuration is valid
|
||||
// after the specified configuration environment changes are applied.
|
||||
func checkBuildConfig(add map[string]string, del map[string]bool) error {
|
||||
// get returns the value for key after applying add and del and
|
||||
// reports whether it changed. cur should be the current value
|
||||
// (i.e., before applying changes) and def should be the default
|
||||
// value (i.e., when no environment variables are provided at all).
|
||||
get := func(key, cur, def string) (string, bool) {
|
||||
if val, ok := add[key]; ok {
|
||||
return val, true
|
||||
}
|
||||
if del[key] {
|
||||
val := getOrigEnv(key)
|
||||
if val == "" {
|
||||
val = def
|
||||
}
|
||||
return val, true
|
||||
}
|
||||
return cur, false
|
||||
}
|
||||
|
||||
goos, okGOOS := get("GOOS", cfg.Goos, build.Default.GOOS)
|
||||
goarch, okGOARCH := get("GOARCH", cfg.Goarch, build.Default.GOARCH)
|
||||
if okGOOS || okGOARCH {
|
||||
if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", buildcfg.GOEXPERIMENT(), "")
|
||||
if okGOEXPERIMENT {
|
||||
if _, _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintEnv prints the environment variables to w.
|
||||
func PrintEnv(w io.Writer, env []cfg.EnvVar) {
|
||||
for _, e := range env {
|
||||
|
@ -598,6 +598,9 @@ Architecture-specific environment variables:
|
||||
GOMIPS64
|
||||
For GOARCH=mips64{,le}, whether to use floating point instructions.
|
||||
Valid values are hardfloat (default), softfloat.
|
||||
GOPPC64
|
||||
For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
|
||||
Valid values are power8 (default), power9.
|
||||
GOWASM
|
||||
For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
|
||||
Valid values are satconv, signext.
|
||||
@ -607,6 +610,12 @@ Special-purpose environment variables:
|
||||
GCCGOTOOLDIR
|
||||
If set, where to find gccgo tools, such as cgo.
|
||||
The default is based on how gccgo was configured.
|
||||
GOEXPERIMENT
|
||||
Comma-separated list of toolchain experiments to enable or disable.
|
||||
The list of available experiments may change arbitrarily over time.
|
||||
See src/internal/goexperiment/flags.go for currently valid values.
|
||||
Warning: This variable is provided for the development and testing
|
||||
of the Go toolchain itself. Use beyond that purpose is unsupported.
|
||||
GOROOT_FINAL
|
||||
The root of the installed Go tree, when it is
|
||||
installed in a location other than where it is built.
|
||||
@ -784,7 +793,7 @@ var HelpBuildConstraint = &base.Command{
|
||||
Long: `
|
||||
A build constraint, also known as a build tag, is a line comment that begins
|
||||
|
||||
// +build
|
||||
//go:build
|
||||
|
||||
that lists the conditions under which a file should be included in the package.
|
||||
Constraints may appear in any kind of source file (not just Go), but
|
||||
@ -792,30 +801,20 @@ they must appear near the top of the file, preceded
|
||||
only by blank lines and other line comments. These rules mean that in Go
|
||||
files a build constraint must appear before the package clause.
|
||||
|
||||
To distinguish build constraints from package documentation, a series of
|
||||
build constraints must be followed by a blank line.
|
||||
To distinguish build constraints from package documentation,
|
||||
a build constraint should be followed by a blank line.
|
||||
|
||||
A build constraint is evaluated as the OR of space-separated options.
|
||||
Each option evaluates as the AND of its comma-separated terms.
|
||||
Each term consists of letters, digits, underscores, and dots.
|
||||
A term may be negated with a preceding !.
|
||||
For example, the build constraint:
|
||||
A build constraint is evaluated as an expression containing options
|
||||
combined by ||, &&, and ! operators and parentheses. Operators have
|
||||
the same meaning as in Go.
|
||||
|
||||
// +build linux,386 darwin,!cgo
|
||||
For example, the following build constraint constrains a file to
|
||||
build when the "linux" and "386" constraints are satisfied, or when
|
||||
"darwin" is satisfied and "cgo" is not:
|
||||
|
||||
corresponds to the boolean formula:
|
||||
//go:build (linux && 386) || (darwin && !cgo)
|
||||
|
||||
(linux AND 386) OR (darwin AND (NOT cgo))
|
||||
|
||||
A file may have multiple build constraints. The overall constraint is the AND
|
||||
of the individual constraints. That is, the build constraints:
|
||||
|
||||
// +build linux darwin
|
||||
// +build amd64
|
||||
|
||||
corresponds to the boolean formula:
|
||||
|
||||
(linux OR darwin) AND amd64
|
||||
It is an error for a file to have more than one //go:build line.
|
||||
|
||||
During a particular build, the following words are satisfied:
|
||||
|
||||
@ -853,22 +852,26 @@ in addition to ios tags and files.
|
||||
|
||||
To keep a file from being considered for the build:
|
||||
|
||||
// +build ignore
|
||||
//go:build ignore
|
||||
|
||||
(any other unsatisfied word will work as well, but "ignore" is conventional.)
|
||||
|
||||
To build a file only when using cgo, and only on Linux and OS X:
|
||||
|
||||
// +build linux,cgo darwin,cgo
|
||||
//go:build cgo && (linux || darwin)
|
||||
|
||||
Such a file is usually paired with another file implementing the
|
||||
default functionality for other systems, which in this case would
|
||||
carry the constraint:
|
||||
|
||||
// +build !linux,!darwin !cgo
|
||||
//go:build !(cgo && (linux || darwin))
|
||||
|
||||
Naming a file dns_windows.go will cause it to be included only when
|
||||
building the package for Windows; similarly, math_386.s will be included
|
||||
only when building the package for 32-bit x86.
|
||||
|
||||
Go versions 1.16 and earlier used a different syntax for build constraints,
|
||||
with a "// +build" prefix. The gofmt command will add an equivalent //go:build
|
||||
constraint when encountering the older syntax.
|
||||
`,
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package imports
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
@ -22,6 +23,19 @@ type importReader struct {
|
||||
nerr int
|
||||
}
|
||||
|
||||
var bom = []byte{0xef, 0xbb, 0xbf}
|
||||
|
||||
func newImportReader(b *bufio.Reader) *importReader {
|
||||
// Remove leading UTF-8 BOM.
|
||||
// Per https://golang.org/ref/spec#Source_code_representation:
|
||||
// a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF)
|
||||
// if it is the first Unicode code point in the source text.
|
||||
if leadingBytes, err := b.Peek(3); err == nil && bytes.Equal(leadingBytes, bom) {
|
||||
b.Discard(3)
|
||||
}
|
||||
return &importReader{b: b}
|
||||
}
|
||||
|
||||
func isIdent(c byte) bool {
|
||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
|
||||
}
|
||||
@ -201,7 +215,7 @@ func (r *importReader) readImport(imports *[]string) {
|
||||
// ReadComments is like io.ReadAll, except that it only reads the leading
|
||||
// block of comments in the file.
|
||||
func ReadComments(f io.Reader) ([]byte, error) {
|
||||
r := &importReader{b: bufio.NewReader(f)}
|
||||
r := newImportReader(bufio.NewReader(f))
|
||||
r.peekByte(true)
|
||||
if r.err == nil && !r.eof {
|
||||
// Didn't reach EOF, so must have found a non-space byte. Remove it.
|
||||
@ -213,7 +227,7 @@ func ReadComments(f io.Reader) ([]byte, error) {
|
||||
// ReadImports is like io.ReadAll, except that it expects a Go file as input
|
||||
// and stops reading the input once the imports have completed.
|
||||
func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
|
||||
r := &importReader{b: bufio.NewReader(f)}
|
||||
r := newImportReader(bufio.NewReader(f))
|
||||
|
||||
r.readKeyword("package")
|
||||
r.readIdent()
|
||||
|
@ -66,6 +66,10 @@ var readImportsTests = []readTest{
|
||||
`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"\ufeff𝔻" + `package p; import "x";ℙvar x = 1`,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
var readCommentsTests = []readTest{
|
||||
@ -81,6 +85,10 @@ var readCommentsTests = []readTest{
|
||||
`ℙpackage p; import . "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"\ufeff𝔻" + `ℙpackage p; import . "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`// foo
|
||||
|
||||
@ -90,6 +98,19 @@ var readCommentsTests = []readTest{
|
||||
|
||||
/*/ zot */
|
||||
|
||||
// asdf
|
||||
ℙHello, world`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"\ufeff𝔻" + `// foo
|
||||
|
||||
/* bar */
|
||||
|
||||
/* quux */ // baz
|
||||
|
||||
/*/ zot */
|
||||
|
||||
// asdf
|
||||
ℙHello, world`,
|
||||
"",
|
||||
@ -107,6 +128,11 @@ func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, erro
|
||||
in = tt.in[:j] + tt.in[j+len("ℙ"):]
|
||||
testOut = tt.in[:j]
|
||||
}
|
||||
d := strings.Index(tt.in, "𝔻")
|
||||
if d >= 0 {
|
||||
in = in[:d] + in[d+len("𝔻"):]
|
||||
testOut = testOut[d+len("𝔻"):]
|
||||
}
|
||||
r := strings.NewReader(in)
|
||||
buf, err := read(r)
|
||||
if err != nil {
|
||||
|
@ -724,8 +724,18 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
||||
// Record non-identity import mappings in p.ImportMap.
|
||||
for _, p := range pkgs {
|
||||
for i, srcPath := range p.Internal.RawImports {
|
||||
path := p.Imports[i]
|
||||
nRaw := len(p.Internal.RawImports)
|
||||
for i, path := range p.Imports {
|
||||
var srcPath string
|
||||
if i < nRaw {
|
||||
srcPath = p.Internal.RawImports[i]
|
||||
} else {
|
||||
// This path is not within the raw imports, so it must be an import
|
||||
// found only within CompiledGoFiles. Those paths are found in
|
||||
// CompiledImports.
|
||||
srcPath = p.Internal.CompiledImports[i-nRaw]
|
||||
}
|
||||
|
||||
if path != srcPath {
|
||||
if p.ImportMap == nil {
|
||||
p.ImportMap = make(map[string]string)
|
||||
|
@ -87,6 +87,7 @@ type PackagePublic struct {
|
||||
CgoFiles []string `json:",omitempty"` // .go source files that import "C"
|
||||
CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles
|
||||
IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints
|
||||
InvalidGoFiles []string `json:",omitempty"` // .go source files with detected problems (parse error, wrong package name, and so on)
|
||||
IgnoredOtherFiles []string `json:",omitempty"` // non-.go source files ignored due to build constraints
|
||||
CFiles []string `json:",omitempty"` // .c source files
|
||||
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
|
||||
@ -144,6 +145,7 @@ func (p *Package) AllFiles() []string {
|
||||
p.CgoFiles,
|
||||
// no p.CompiledGoFiles, because they are from GoFiles or generated by us
|
||||
p.IgnoredGoFiles,
|
||||
// no p.InvalidGoFiles, because they are from GoFiles
|
||||
p.IgnoredOtherFiles,
|
||||
p.CFiles,
|
||||
p.CXXFiles,
|
||||
@ -192,8 +194,8 @@ type PackageInternal struct {
|
||||
// Unexported fields are not part of the public API.
|
||||
Build *build.Package
|
||||
Imports []*Package // this package's direct imports
|
||||
CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library)
|
||||
RawImports []string // this package's original imports as they appear in the text of the program
|
||||
CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports
|
||||
RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports
|
||||
ForceLibrary bool // this package is a library (even if named "main")
|
||||
CmdlineFiles bool // package built from files listed on command line
|
||||
CmdlinePkg bool // package listed on command line
|
||||
@ -371,6 +373,7 @@ func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) {
|
||||
p.GoFiles = pp.GoFiles
|
||||
p.CgoFiles = pp.CgoFiles
|
||||
p.IgnoredGoFiles = pp.IgnoredGoFiles
|
||||
p.InvalidGoFiles = pp.InvalidGoFiles
|
||||
p.IgnoredOtherFiles = pp.IgnoredOtherFiles
|
||||
p.CFiles = pp.CFiles
|
||||
p.CXXFiles = pp.CXXFiles
|
||||
@ -852,7 +855,9 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
|
||||
buildMode = build.ImportComment
|
||||
}
|
||||
data.p, data.err = cfg.BuildContext.ImportDir(r.dir, buildMode)
|
||||
if data.p.Root == "" && cfg.ModulesEnabled {
|
||||
if cfg.ModulesEnabled {
|
||||
// Override data.p.Root, since ImportDir sets it to $GOPATH, if
|
||||
// the module is inside $GOPATH/src.
|
||||
if info := modload.PackageModuleInfo(ctx, path); info != nil {
|
||||
data.p.Root = info.Dir
|
||||
}
|
||||
@ -1800,35 +1805,37 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
|
||||
}
|
||||
}
|
||||
|
||||
// Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall",
|
||||
// except for certain packages, to avoid circular dependencies.
|
||||
if p.UsesCgo() {
|
||||
addImport("unsafe", true)
|
||||
}
|
||||
if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" {
|
||||
addImport("runtime/cgo", true)
|
||||
}
|
||||
if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
|
||||
addImport("syscall", true)
|
||||
}
|
||||
|
||||
// SWIG adds imports of some standard packages.
|
||||
if p.UsesSwig() {
|
||||
addImport("unsafe", true)
|
||||
if cfg.BuildContext.Compiler != "gccgo" {
|
||||
if !opts.IgnoreImports {
|
||||
// Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall",
|
||||
// except for certain packages, to avoid circular dependencies.
|
||||
if p.UsesCgo() {
|
||||
addImport("unsafe", true)
|
||||
}
|
||||
if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" {
|
||||
addImport("runtime/cgo", true)
|
||||
}
|
||||
addImport("syscall", true)
|
||||
addImport("sync", true)
|
||||
if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
|
||||
addImport("syscall", true)
|
||||
}
|
||||
|
||||
// TODO: The .swig and .swigcxx files can use
|
||||
// %go_import directives to import other packages.
|
||||
}
|
||||
// SWIG adds imports of some standard packages.
|
||||
if p.UsesSwig() {
|
||||
addImport("unsafe", true)
|
||||
if cfg.BuildContext.Compiler != "gccgo" {
|
||||
addImport("runtime/cgo", true)
|
||||
}
|
||||
addImport("syscall", true)
|
||||
addImport("sync", true)
|
||||
|
||||
// The linker loads implicit dependencies.
|
||||
if p.Name == "main" && !p.Internal.ForceLibrary {
|
||||
for _, dep := range LinkerDeps(p) {
|
||||
addImport(dep, false)
|
||||
// TODO: The .swig and .swigcxx files can use
|
||||
// %go_import directives to import other packages.
|
||||
}
|
||||
|
||||
// The linker loads implicit dependencies.
|
||||
if p.Name == "main" && !p.Internal.ForceLibrary {
|
||||
for _, dep := range LinkerDeps(p) {
|
||||
addImport(dep, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2390,7 +2397,9 @@ func LoadImportWithFlags(path, srcDir string, parent *Package, stk *ImportStack,
|
||||
// PackageOpts control the behavior of PackagesAndErrors and other package
|
||||
// loading functions.
|
||||
type PackageOpts struct {
|
||||
// IgnoreImports controls whether we ignore imports when loading packages.
|
||||
// IgnoreImports controls whether we ignore explicit and implicit imports
|
||||
// when loading packages. Implicit imports are added when supporting Cgo
|
||||
// or SWIG and when linking main packages.
|
||||
IgnoreImports bool
|
||||
|
||||
// ModResolveTests indicates whether calls to the module loader should also
|
||||
@ -2499,7 +2508,7 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string)
|
||||
}
|
||||
|
||||
if opts.MainOnly {
|
||||
pkgs = mainPackagesOnly(pkgs, patterns)
|
||||
pkgs = mainPackagesOnly(pkgs, matches)
|
||||
}
|
||||
|
||||
// Now that CmdlinePkg is set correctly,
|
||||
@ -2553,50 +2562,63 @@ func CheckPackageErrors(pkgs []*Package) {
|
||||
// mainPackagesOnly filters out non-main packages matched only by arguments
|
||||
// containing "..." and returns the remaining main packages.
|
||||
//
|
||||
// Packages with missing, invalid, or ambiguous names may be treated as
|
||||
// possibly-main packages.
|
||||
//
|
||||
// mainPackagesOnly sets a non-main package's Error field and returns it if it
|
||||
// is named by a literal argument.
|
||||
//
|
||||
// mainPackagesOnly prints warnings for non-literal arguments that only match
|
||||
// non-main packages.
|
||||
func mainPackagesOnly(pkgs []*Package, patterns []string) []*Package {
|
||||
matchers := make([]func(string) bool, len(patterns))
|
||||
for i, p := range patterns {
|
||||
if strings.Contains(p, "...") {
|
||||
matchers[i] = search.MatchPattern(p)
|
||||
func mainPackagesOnly(pkgs []*Package, matches []*search.Match) []*Package {
|
||||
treatAsMain := map[string]bool{}
|
||||
for _, m := range matches {
|
||||
if m.IsLiteral() {
|
||||
for _, path := range m.Pkgs {
|
||||
treatAsMain[path] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matchedPkgs := make([]*Package, 0, len(pkgs))
|
||||
mainCount := make([]int, len(patterns))
|
||||
nonMainCount := make([]int, len(patterns))
|
||||
var mains []*Package
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Name == "main" || (pkg.Incomplete && pkg.Name == "") {
|
||||
matchedPkgs = append(matchedPkgs, pkg)
|
||||
for i := range patterns {
|
||||
if matchers[i] != nil && matchers[i](pkg.ImportPath) {
|
||||
mainCount[i]++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range patterns {
|
||||
if matchers[i] == nil && patterns[i] == pkg.ImportPath {
|
||||
if pkg.Error == nil {
|
||||
pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}}
|
||||
}
|
||||
matchedPkgs = append(matchedPkgs, pkg)
|
||||
} else if matchers[i] != nil && matchers[i](pkg.ImportPath) {
|
||||
nonMainCount[i]++
|
||||
}
|
||||
}
|
||||
if pkg.Name == "main" {
|
||||
treatAsMain[pkg.ImportPath] = true
|
||||
mains = append(mains, pkg)
|
||||
continue
|
||||
}
|
||||
}
|
||||
for i, p := range patterns {
|
||||
if matchers[i] != nil && mainCount[i] == 0 && nonMainCount[i] > 0 {
|
||||
fmt.Fprintf(os.Stderr, "go: warning: %q matched no main packages\n", p)
|
||||
|
||||
if len(pkg.InvalidGoFiles) > 0 { // TODO(#45999): && pkg.Name == "", but currently go/build sets pkg.Name arbitrarily if it is ambiguous.
|
||||
// The package has (or may have) conflicting names, and we can't easily
|
||||
// tell whether one of them is "main". So assume that it could be, and
|
||||
// report an error for the package.
|
||||
treatAsMain[pkg.ImportPath] = true
|
||||
}
|
||||
if treatAsMain[pkg.ImportPath] {
|
||||
if pkg.Error == nil {
|
||||
pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}}
|
||||
}
|
||||
mains = append(mains, pkg)
|
||||
}
|
||||
}
|
||||
|
||||
return matchedPkgs
|
||||
for _, m := range matches {
|
||||
if m.IsLiteral() || len(m.Pkgs) == 0 {
|
||||
continue
|
||||
}
|
||||
foundMain := false
|
||||
for _, path := range m.Pkgs {
|
||||
if treatAsMain[path] {
|
||||
foundMain = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundMain {
|
||||
fmt.Fprintf(os.Stderr, "go: warning: %q matched only non-main packages\n", m.Pattern())
|
||||
}
|
||||
}
|
||||
|
||||
return mains
|
||||
}
|
||||
|
||||
type mainPackageError struct {
|
||||
|
@ -116,7 +116,7 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
|
||||
// Can't change that code, because that code is only for loading the
|
||||
// non-test copy of a package.
|
||||
ptestErr = &PackageError{
|
||||
ImportStack: testImportStack(stk[0], p1, p.ImportPath),
|
||||
ImportStack: importCycleStack(p1, p.ImportPath),
|
||||
Err: errors.New("import cycle not allowed in test"),
|
||||
IsImportCycle: true,
|
||||
}
|
||||
@ -375,22 +375,44 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
|
||||
return pmain, ptest, pxtest
|
||||
}
|
||||
|
||||
func testImportStack(top string, p *Package, target string) []string {
|
||||
stk := []string{top, p.ImportPath}
|
||||
Search:
|
||||
for p.ImportPath != target {
|
||||
for _, p1 := range p.Internal.Imports {
|
||||
if p1.ImportPath == target || str.Contains(p1.Deps, target) {
|
||||
stk = append(stk, p1.ImportPath)
|
||||
p = p1
|
||||
continue Search
|
||||
// importCycleStack returns an import stack from p to the package whose import
|
||||
// path is target.
|
||||
func importCycleStack(p *Package, target string) []string {
|
||||
// importerOf maps each import path to its importer nearest to p.
|
||||
importerOf := map[string]string{p.ImportPath: ""}
|
||||
|
||||
// q is a breadth-first queue of packages to search for target.
|
||||
// Every package added to q has a corresponding entry in pathTo.
|
||||
//
|
||||
// We search breadth-first for two reasons:
|
||||
//
|
||||
// 1. We want to report the shortest cycle.
|
||||
//
|
||||
// 2. If p contains multiple cycles, the first cycle we encounter might not
|
||||
// contain target. To ensure termination, we have to break all cycles
|
||||
// other than the first.
|
||||
q := []*Package{p}
|
||||
|
||||
for len(q) > 0 {
|
||||
p := q[0]
|
||||
q = q[1:]
|
||||
if path := p.ImportPath; path == target {
|
||||
var stk []string
|
||||
for path != "" {
|
||||
stk = append(stk, path)
|
||||
path = importerOf[path]
|
||||
}
|
||||
return stk
|
||||
}
|
||||
for _, dep := range p.Internal.Imports {
|
||||
if _, ok := importerOf[dep.ImportPath]; !ok {
|
||||
importerOf[dep.ImportPath] = p.ImportPath
|
||||
q = append(q, dep)
|
||||
}
|
||||
}
|
||||
// Can't happen, but in case it does...
|
||||
stk = append(stk, "<lost path to cycle>")
|
||||
break
|
||||
}
|
||||
return stk
|
||||
|
||||
panic("lost path to cycle")
|
||||
}
|
||||
|
||||
// recompileForTest copies and replaces certain packages in pmain's dependency
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/fsys"
|
||||
"cmd/go/internal/lockedfile/internal/filelock"
|
||||
)
|
||||
|
||||
@ -21,7 +20,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
// calls for Linux and Windows anyway, so it's simpler to use that approach
|
||||
// consistently.
|
||||
|
||||
f, err := fsys.OpenFile(name, flag&^os.O_TRUNC, perm)
|
||||
f, err := os.OpenFile(name, flag&^os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmd/go/internal/fsys"
|
||||
)
|
||||
|
||||
// Opening an exclusive-use file returns an error.
|
||||
@ -59,7 +57,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
// If the file was unpacked or created by some other program, it might not
|
||||
// have the ModeExclusive bit set. Set it before we call OpenFile, so that we
|
||||
// can be confident that a successful OpenFile implies exclusive use.
|
||||
if fi, err := fsys.Stat(name); err == nil {
|
||||
if fi, err := os.Stat(name); err == nil {
|
||||
if fi.Mode()&fs.ModeExclusive == 0 {
|
||||
if err := os.Chmod(name, fi.Mode()|fs.ModeExclusive); err != nil {
|
||||
return nil, err
|
||||
@ -72,7 +70,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
nextSleep := 1 * time.Millisecond
|
||||
const maxSleep = 500 * time.Millisecond
|
||||
for {
|
||||
f, err := fsys.OpenFile(name, flag, perm|fs.ModeExclusive)
|
||||
f, err := os.OpenFile(name, flag, perm|fs.ModeExclusive)
|
||||
if err == nil {
|
||||
return f, nil
|
||||
}
|
||||
|
@ -86,9 +86,11 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if !modload.HasModRoot() && len(args) == 0 {
|
||||
base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
haveExplicitArgs := len(args) > 0
|
||||
if !haveExplicitArgs {
|
||||
args = []string{"all"}
|
||||
} else if modload.HasModRoot() {
|
||||
}
|
||||
if modload.HasModRoot() {
|
||||
modload.LoadModFile(ctx) // to fill Target
|
||||
targetAtUpgrade := modload.Target.Path + "@upgrade"
|
||||
targetAtPatch := modload.Target.Path + "@patch"
|
||||
@ -135,6 +137,18 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
|
||||
type token struct{}
|
||||
sem := make(chan token, runtime.GOMAXPROCS(0))
|
||||
infos, infosErr := modload.ListModules(ctx, args, 0)
|
||||
if !haveExplicitArgs {
|
||||
// 'go mod download' is sometimes run without arguments to pre-populate the
|
||||
// module cache. It may fetch modules that aren't needed to build packages
|
||||
// in the main mdoule. This is usually not intended, so don't save sums for
|
||||
// downloaded modules (golang.org/issue/45332).
|
||||
// TODO(golang.org/issue/45551): For now, in ListModules, save sums needed
|
||||
// to load the build list (same as 1.15 behavior). In the future, report an
|
||||
// error if go.mod or go.sum need to be updated after loading the build
|
||||
// list.
|
||||
modload.DisallowWriteGoMod()
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
if info.Replace != nil {
|
||||
info = info.Replace
|
||||
@ -185,8 +199,15 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
|
||||
base.ExitIfErrors()
|
||||
}
|
||||
|
||||
// Update go.mod and especially go.sum if needed.
|
||||
modload.WriteGoMod(ctx)
|
||||
// If there were explicit arguments, update go.mod and especially go.sum.
|
||||
// 'go mod download mod@version' is a useful way to add a sum without using
|
||||
// 'go get mod@version', which may have other side effects. We print this in
|
||||
// some error message hints.
|
||||
//
|
||||
// Don't save sums for 'go mod download' without arguments; see comment above.
|
||||
if haveExplicitArgs {
|
||||
modload.WriteGoMod(ctx)
|
||||
}
|
||||
|
||||
// If there was an error matching some of the requested packages, emit it now
|
||||
// (after we've written the checksums for the modules that were downloaded
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
var cmdEdit = &base.Command{
|
||||
UsageLine: "go mod edit [editing flags] [go.mod]",
|
||||
UsageLine: "go mod edit [editing flags] [-fmt|-print|-json] [go.mod]",
|
||||
Short: "edit go.mod from tools or scripts",
|
||||
Long: `
|
||||
Edit provides a command-line interface for editing go.mod,
|
||||
@ -196,7 +196,7 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
||||
if *editGo != "" {
|
||||
if !modfile.GoVersionRE.MatchString(*editGo) {
|
||||
base.Fatalf(`go mod: invalid -go option; expecting something like "-go 1.12"`)
|
||||
base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var cmdGraph = &base.Command{
|
||||
UsageLine: "go mod graph",
|
||||
UsageLine: "go mod graph [-go=version]",
|
||||
Short: "print module requirement graph",
|
||||
Long: `
|
||||
Graph prints the module requirement graph (with replacements applied)
|
||||
@ -26,12 +26,21 @@ in text form. Each line in the output has two space-separated fields: a module
|
||||
and one of its requirements. Each module is identified as a string of the form
|
||||
path@version, except for the main module, which has no @version suffix.
|
||||
|
||||
The -go flag causes graph to report the module graph as loaded by the
|
||||
given Go version, instead of the version indicated by the 'go' directive
|
||||
in the go.mod file.
|
||||
|
||||
See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.
|
||||
`,
|
||||
Run: runGraph,
|
||||
}
|
||||
|
||||
var (
|
||||
graphGo goVersionFlag
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdGraph.Flag.Var(&graphGo, "go", "")
|
||||
base.AddModCommonFlags(&cmdGraph.Flag)
|
||||
}
|
||||
|
||||
@ -41,7 +50,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
modload.ForceUseModules = true
|
||||
modload.RootMode = modload.NeedRoot
|
||||
mg := modload.LoadModGraph(ctx)
|
||||
mg := modload.LoadModGraph(ctx, graphGo.String())
|
||||
|
||||
w := bufio.NewWriter(os.Stdout)
|
||||
defer w.Flush()
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
var cmdInit = &base.Command{
|
||||
UsageLine: "go mod init [module]",
|
||||
UsageLine: "go mod init [module-path]",
|
||||
Short: "initialize new module in current directory",
|
||||
Long: `
|
||||
Init initializes and writes a new go.mod file in the current directory, in
|
||||
|
@ -12,12 +12,14 @@ import (
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/modload"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var cmdTidy = &base.Command{
|
||||
UsageLine: "go mod tidy [-e] [-v] [-go=version]",
|
||||
UsageLine: "go mod tidy [-e] [-v] [-go=version] [-compat=version]",
|
||||
Short: "add missing and remove unused modules",
|
||||
Long: `
|
||||
Tidy makes sure go.mod matches the source code in the module.
|
||||
@ -38,34 +40,64 @@ are retained as explicit requirements in the go.mod file.
|
||||
(Go versions 1.17 and higher retain more requirements in order to
|
||||
support lazy module loading.)
|
||||
|
||||
The -compat flag preserves any additional checksums needed for the
|
||||
'go' command from the indicated major Go release to successfully load
|
||||
the module graph, and causes tidy to error out if that version of the
|
||||
'go' command would load any imported package from a different module
|
||||
version. By default, tidy acts as if the -compat flag were set to the
|
||||
version prior to the one indicated by the 'go' directive in the go.mod
|
||||
file.
|
||||
|
||||
See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
|
||||
`,
|
||||
Run: runTidy,
|
||||
}
|
||||
|
||||
var (
|
||||
tidyE bool // if true, report errors but proceed anyway.
|
||||
tidyGo string // go version to write to the tidied go.mod file (toggles lazy loading)
|
||||
tidyE bool // if true, report errors but proceed anyway.
|
||||
tidyGo goVersionFlag // go version to write to the tidied go.mod file (toggles lazy loading)
|
||||
tidyCompat goVersionFlag // go version for which the tidied go.mod and go.sum files should be “compatible”
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
||||
cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
|
||||
cmdTidy.Flag.StringVar(&tidyGo, "go", "", "")
|
||||
cmdTidy.Flag.Var(&tidyGo, "go", "")
|
||||
cmdTidy.Flag.Var(&tidyCompat, "compat", "")
|
||||
base.AddModCommonFlags(&cmdTidy.Flag)
|
||||
}
|
||||
|
||||
// A goVersionFlag is a flag.Value representing a supported Go version.
|
||||
//
|
||||
// (Note that the -go argument to 'go mod edit' is *not* a goVersionFlag.
|
||||
// It intentionally allows newer-than-supported versions as arguments.)
|
||||
type goVersionFlag struct {
|
||||
v string
|
||||
}
|
||||
|
||||
func (f *goVersionFlag) String() string { return f.v }
|
||||
func (f *goVersionFlag) Get() interface{} { return f.v }
|
||||
|
||||
func (f *goVersionFlag) Set(s string) error {
|
||||
if s != "" {
|
||||
latest := modload.LatestGoVersion()
|
||||
if !modfile.GoVersionRE.MatchString(s) {
|
||||
return fmt.Errorf("expecting a Go version like %q", latest)
|
||||
}
|
||||
if semver.Compare("v"+s, "v"+latest) > 0 {
|
||||
return fmt.Errorf("maximum supported Go version is %s", latest)
|
||||
}
|
||||
}
|
||||
|
||||
f.v = s
|
||||
return nil
|
||||
}
|
||||
|
||||
func runTidy(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
base.Fatalf("go mod tidy: no arguments allowed")
|
||||
}
|
||||
|
||||
if tidyGo != "" {
|
||||
if !modfile.GoVersionRE.MatchString(tidyGo) {
|
||||
base.Fatalf(`go mod: invalid -go option %q; expecting something like "-go 1.17"`, tidyGo)
|
||||
}
|
||||
}
|
||||
|
||||
// Tidy aims to make 'go test' reproducible for any package in 'all', so we
|
||||
// need to include test dependencies. For modules that specify go 1.15 or
|
||||
// earlier this is a no-op (because 'all' saturates transitive test
|
||||
@ -80,9 +112,10 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.RootMode = modload.NeedRoot
|
||||
|
||||
modload.LoadPackages(ctx, modload.PackageOpts{
|
||||
GoVersion: tidyGo,
|
||||
GoVersion: tidyGo.String(),
|
||||
Tags: imports.AnyTags(),
|
||||
Tidy: true,
|
||||
TidyCompatibleVersion: tidyCompat.String(),
|
||||
VendorModulesInGOROOTSrc: true,
|
||||
ResolveMissingImports: true,
|
||||
LoadTests: true,
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -299,7 +300,7 @@ func copyMetadata(modPath, pkg, dst, src string, copiedFiles map[string]bool) {
|
||||
if modPath == pkg {
|
||||
break
|
||||
}
|
||||
pkg = filepath.Dir(pkg)
|
||||
pkg = path.Dir(pkg)
|
||||
dst = filepath.Dir(dst)
|
||||
src = filepath.Dir(src)
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) {
|
||||
sem := make(chan token, runtime.GOMAXPROCS(0))
|
||||
|
||||
// Use a slice of result channels, so that the output is deterministic.
|
||||
mods := modload.LoadModGraph(ctx).BuildList()[1:]
|
||||
const defaultGoVersion = ""
|
||||
mods := modload.LoadModGraph(ctx, defaultGoVersion).BuildList()[1:]
|
||||
errsChans := make([]<-chan []error, len(mods))
|
||||
|
||||
for i, mod := range mods {
|
||||
|
@ -152,7 +152,7 @@ func lockVersion(mod module.Version) (unlock func(), err error) {
|
||||
// If err is nil, the caller MUST eventually call the unlock function.
|
||||
func SideLock() (unlock func(), err error) {
|
||||
if err := checkCacheDir(); err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := filepath.Join(cfg.GOMODCACHE, "cache", "lock")
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"io/fs"
|
||||
@ -47,12 +46,6 @@ var altRepos = []string{
|
||||
var localGitRepo string
|
||||
|
||||
func testMain(m *testing.M) int {
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "skipping because git binary not found")
|
||||
fmt.Println("PASS")
|
||||
return 0
|
||||
}
|
||||
|
||||
dir, err := os.MkdirTemp("", "gitrepo-test-")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -60,23 +53,25 @@ func testMain(m *testing.M) int {
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
if testenv.HasExternalNetwork() && testenv.HasExec() {
|
||||
// Clone gitrepo1 into a local directory.
|
||||
// If we use a file:// URL to access the local directory,
|
||||
// then git starts up all the usual protocol machinery,
|
||||
// which will let us test remote git archive invocations.
|
||||
localGitRepo = filepath.Join(dir, "gitrepo2")
|
||||
if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
|
||||
log.Fatal(err)
|
||||
if _, err := exec.LookPath("git"); err == nil {
|
||||
// Clone gitrepo1 into a local directory.
|
||||
// If we use a file:// URL to access the local directory,
|
||||
// then git starts up all the usual protocol machinery,
|
||||
// which will let us test remote git archive invocations.
|
||||
localGitRepo = filepath.Join(dir, "gitrepo2")
|
||||
if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
func testRepo(remote string) (Repo, error) {
|
||||
func testRepo(t *testing.T, remote string) (Repo, error) {
|
||||
if remote == "localGitRepo" {
|
||||
// Convert absolute path to file URL. LocalGitRepo will not accept
|
||||
// Windows absolute paths because they look like a host:path remote.
|
||||
@ -87,15 +82,17 @@ func testRepo(remote string) (Repo, error) {
|
||||
} else {
|
||||
url = "file:///" + filepath.ToSlash(localGitRepo)
|
||||
}
|
||||
testenv.MustHaveExecPath(t, "git")
|
||||
return LocalGitRepo(url)
|
||||
}
|
||||
kind := "git"
|
||||
vcs := "git"
|
||||
for _, k := range []string{"hg"} {
|
||||
if strings.Contains(remote, "/"+k+"/") {
|
||||
kind = k
|
||||
vcs = k
|
||||
}
|
||||
}
|
||||
return NewRepo(kind, remote)
|
||||
testenv.MustHaveExecPath(t, vcs)
|
||||
return NewRepo(vcs, remote)
|
||||
}
|
||||
|
||||
var tagsTests = []struct {
|
||||
@ -116,7 +113,7 @@ func TestTags(t *testing.T) {
|
||||
|
||||
for _, tt := range tagsTests {
|
||||
f := func(t *testing.T) {
|
||||
r, err := testRepo(tt.repo)
|
||||
r, err := testRepo(t, tt.repo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -168,7 +165,7 @@ func TestLatest(t *testing.T) {
|
||||
|
||||
for _, tt := range latestTests {
|
||||
f := func(t *testing.T) {
|
||||
r, err := testRepo(tt.repo)
|
||||
r, err := testRepo(t, tt.repo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -221,7 +218,7 @@ func TestReadFile(t *testing.T) {
|
||||
|
||||
for _, tt := range readFileTests {
|
||||
f := func(t *testing.T) {
|
||||
r, err := testRepo(tt.repo)
|
||||
r, err := testRepo(t, tt.repo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -412,7 +409,7 @@ func TestReadZip(t *testing.T) {
|
||||
|
||||
for _, tt := range readZipTests {
|
||||
f := func(t *testing.T) {
|
||||
r, err := testRepo(tt.repo)
|
||||
r, err := testRepo(t, tt.repo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -581,7 +578,7 @@ func TestStat(t *testing.T) {
|
||||
|
||||
for _, tt := range statTests {
|
||||
f := func(t *testing.T) {
|
||||
r, err := testRepo(tt.repo)
|
||||
r, err := testRepo(t, tt.repo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -864,22 +864,25 @@ func (r *codeRepo) GoMod(version string) (data []byte, err error) {
|
||||
data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return r.legacyGoMod(rev, dir), nil
|
||||
return LegacyGoMod(r.modPath), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (r *codeRepo) legacyGoMod(rev, dir string) []byte {
|
||||
// We used to try to build a go.mod reflecting pre-existing
|
||||
// package management metadata files, but the conversion
|
||||
// was inherently imperfect (because those files don't have
|
||||
// exactly the same semantics as go.mod) and, when done
|
||||
// for dependencies in the middle of a build, impossible to
|
||||
// correct. So we stopped.
|
||||
// Return a fake go.mod that simply declares the module path.
|
||||
return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(r.modPath)))
|
||||
// LegacyGoMod generates a fake go.mod file for a module that doesn't have one.
|
||||
// The go.mod file contains a module directive and nothing else: no go version,
|
||||
// no requirements.
|
||||
//
|
||||
// We used to try to build a go.mod reflecting pre-existing
|
||||
// package management metadata files, but the conversion
|
||||
// was inherently imperfect (because those files don't have
|
||||
// exactly the same semantics as go.mod) and, when done
|
||||
// for dependencies in the middle of a build, impossible to
|
||||
// correct. So we stopped.
|
||||
func LegacyGoMod(modPath string) []byte {
|
||||
return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(modPath)))
|
||||
}
|
||||
|
||||
func (r *codeRepo) modPrefix(rev string) string {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/fsys"
|
||||
"cmd/go/internal/lockedfile"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/robustio"
|
||||
@ -416,7 +417,18 @@ func initGoSum() (bool, error) {
|
||||
|
||||
goSum.m = make(map[module.Version][]string)
|
||||
goSum.status = make(map[modSum]modSumStatus)
|
||||
data, err := lockedfile.Read(GoSumFile)
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok {
|
||||
// Don't lock go.sum if it's part of the overlay.
|
||||
// On Plan 9, locking requires chmod, and we don't want to modify any file
|
||||
// in the overlay. See #44700.
|
||||
data, err = os.ReadFile(actualSumFile)
|
||||
} else {
|
||||
data, err = lockedfile.Read(GoSumFile)
|
||||
}
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return false, err
|
||||
}
|
||||
@ -716,6 +728,9 @@ Outer:
|
||||
if cfg.BuildMod == "readonly" {
|
||||
base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly")
|
||||
}
|
||||
if _, ok := fsys.OverlayPath(GoSumFile); ok {
|
||||
base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
|
||||
}
|
||||
|
||||
// Make a best-effort attempt to acquire the side lock, only to exclude
|
||||
// previous versions of the 'go' command from making simultaneous edits.
|
||||
|
@ -38,6 +38,7 @@ import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/imports"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/search"
|
||||
@ -386,14 +387,14 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
|
||||
}
|
||||
load.CheckPackageErrors(pkgs)
|
||||
|
||||
haveExe := false
|
||||
haveExternalExe := false
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Name == "main" {
|
||||
haveExe = true
|
||||
if pkg.Name == "main" && pkg.Module != nil && pkg.Module.Path != modload.Target.Path {
|
||||
haveExternalExe = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if haveExe {
|
||||
if haveExternalExe {
|
||||
fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.")
|
||||
var altMsg string
|
||||
if modload.HasModRoot() {
|
||||
@ -505,7 +506,8 @@ type versionReason struct {
|
||||
func newResolver(ctx context.Context, queries []*query) *resolver {
|
||||
// LoadModGraph also sets modload.Target, which is needed by various resolver
|
||||
// methods.
|
||||
mg := modload.LoadModGraph(ctx)
|
||||
const defaultGoVersion = ""
|
||||
mg := modload.LoadModGraph(ctx, defaultGoVersion)
|
||||
|
||||
buildList := mg.BuildList()
|
||||
initialVersion := make(map[string]string, len(buildList))
|
||||
@ -1152,6 +1154,7 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack
|
||||
Tags: imports.AnyTags(),
|
||||
VendorModulesInGOROOTSrc: true,
|
||||
LoadTests: *getT,
|
||||
AssumeRootsImported: true, // After 'go get foo', imports of foo should build.
|
||||
SilencePackageErrors: true, // May be fixed by subsequent upgrades or downgrades.
|
||||
}
|
||||
|
||||
@ -1466,6 +1469,8 @@ func (r *resolver) chooseArbitrarily(cs pathSet) (isPackage bool, m module.Versi
|
||||
// checkPackageProblems reloads packages for the given patterns and reports
|
||||
// missing and ambiguous package errors. It also reports retractions and
|
||||
// deprecations for resolved modules and modules needed to build named packages.
|
||||
// It also adds a sum for each updated module in the build list if we had one
|
||||
// before and didn't get one while loading packages.
|
||||
//
|
||||
// We skip missing-package errors earlier in the process, since we want to
|
||||
// resolve pathSets ourselves, but at that point, we don't have enough context
|
||||
@ -1593,12 +1598,55 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
|
||||
})
|
||||
}
|
||||
|
||||
// Load sums for updated modules that had sums before. When we update a
|
||||
// module, we may update another module in the build list that provides a
|
||||
// package in 'all' that wasn't loaded as part of this 'go get' command.
|
||||
// If we don't add a sum for that module, builds may fail later.
|
||||
// Note that an incidentally updated package could still import packages
|
||||
// from unknown modules or from modules in the build list that we didn't
|
||||
// need previously. We can't handle that case without loading 'all'.
|
||||
sumErrs := make([]error, len(r.buildList))
|
||||
for i := range r.buildList {
|
||||
i := i
|
||||
m := r.buildList[i]
|
||||
mActual := m
|
||||
if mRepl := modload.Replacement(m); mRepl.Path != "" {
|
||||
mActual = mRepl
|
||||
}
|
||||
old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}
|
||||
if old.Version == "" {
|
||||
continue
|
||||
}
|
||||
oldActual := old
|
||||
if oldRepl := modload.Replacement(old); oldRepl.Path != "" {
|
||||
oldActual = oldRepl
|
||||
}
|
||||
if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(oldActual) {
|
||||
continue
|
||||
}
|
||||
r.work.Add(func() {
|
||||
if _, err := modfetch.DownloadZip(ctx, mActual); err != nil {
|
||||
verb := "upgraded"
|
||||
if semver.Compare(m.Version, old.Version) < 0 {
|
||||
verb = "downgraded"
|
||||
}
|
||||
replaced := ""
|
||||
if mActual != m {
|
||||
replaced = fmt.Sprintf(" (replaced by %s)", mActual)
|
||||
}
|
||||
err = fmt.Errorf("%s %s %s => %s%s: error finding sum for %s: %v", verb, m.Path, old.Version, m.Version, replaced, mActual, err)
|
||||
sumErrs[i] = err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
<-r.work.Idle()
|
||||
|
||||
// Report deprecations, then retractions.
|
||||
// Report deprecations, then retractions, then errors fetching sums.
|
||||
// Only errors fetching sums are hard errors.
|
||||
for _, mm := range deprecations {
|
||||
if mm.message != "" {
|
||||
fmt.Fprintf(os.Stderr, "go: warning: module %s is deprecated: %s\n", mm.m.Path, mm.message)
|
||||
fmt.Fprintf(os.Stderr, "go: module %s is deprecated: %s\n", mm.m.Path, mm.message)
|
||||
}
|
||||
}
|
||||
var retractPath string
|
||||
@ -1615,6 +1663,12 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
|
||||
if retractPath != "" {
|
||||
fmt.Fprintf(os.Stderr, "go: to switch to the latest unretracted version, run:\n\tgo get %s@latest\n", retractPath)
|
||||
}
|
||||
for _, err := range sumErrs {
|
||||
if err != nil {
|
||||
base.Errorf("go: %v", err)
|
||||
}
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
}
|
||||
|
||||
// reportChanges logs version changes to os.Stderr.
|
||||
@ -1750,7 +1804,8 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
|
||||
return false
|
||||
}
|
||||
|
||||
r.buildList = modload.LoadModGraph(ctx).BuildList()
|
||||
const defaultGoVersion = ""
|
||||
r.buildList = modload.LoadModGraph(ctx, defaultGoVersion).BuildList()
|
||||
r.buildListVersion = make(map[string]string, len(r.buildList))
|
||||
for _, m := range r.buildList {
|
||||
r.buildListVersion[m.Path] = m.Version
|
||||
|
@ -403,11 +403,33 @@ func (mg *ModuleGraph) allRootsSelected() bool {
|
||||
// LoadModGraph loads and returns the graph of module dependencies of the main module,
|
||||
// without loading any packages.
|
||||
//
|
||||
// If the goVersion string is non-empty, the returned graph is the graph
|
||||
// as interpreted by the given Go version (instead of the version indicated
|
||||
// in the go.mod file).
|
||||
//
|
||||
// Modules are loaded automatically (and lazily) in LoadPackages:
|
||||
// LoadModGraph need only be called if LoadPackages is not,
|
||||
// typically in commands that care about modules but no particular package.
|
||||
func LoadModGraph(ctx context.Context) *ModuleGraph {
|
||||
rs, mg, err := expandGraph(ctx, LoadModFile(ctx))
|
||||
func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
|
||||
rs := LoadModFile(ctx)
|
||||
|
||||
if goVersion != "" {
|
||||
depth := modDepthFromGoVersion(goVersion)
|
||||
if depth == eager && rs.depth != eager {
|
||||
// Use newRequirements instead of convertDepth because convertDepth
|
||||
// also updates roots; here, we want to report the unmodified roots
|
||||
// even though they may seem inconsistent.
|
||||
rs = newRequirements(eager, rs.rootModules, rs.direct)
|
||||
}
|
||||
|
||||
mg, err := rs.Graph(ctx)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
return mg
|
||||
}
|
||||
|
||||
rs, mg, err := expandGraph(ctx, rs)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
@ -443,7 +465,7 @@ func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleG
|
||||
// roots — but in a lazy module it may pull in previously-irrelevant
|
||||
// transitive dependencies.
|
||||
|
||||
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil)
|
||||
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
||||
if rsErr != nil {
|
||||
// Failed to update roots, perhaps because of an error in a transitive
|
||||
// dependency needed for the update. Return the original Requirements
|
||||
@ -517,11 +539,11 @@ func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Require
|
||||
return tidyLazyRoots(ctx, rs.direct, pkgs)
|
||||
}
|
||||
|
||||
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version) (*Requirements, error) {
|
||||
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
|
||||
if rs.depth == eager {
|
||||
return updateEagerRoots(ctx, direct, rs, add)
|
||||
}
|
||||
return updateLazyRoots(ctx, direct, rs, pkgs, add)
|
||||
return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported)
|
||||
}
|
||||
|
||||
// tidyLazyRoots returns a minimal set of root requirements that maintains the
|
||||
@ -661,7 +683,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
|
||||
//
|
||||
// (See https://golang.org/design/36460-lazy-module-loading#invariants for more
|
||||
// detail.)
|
||||
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version) (*Requirements, error) {
|
||||
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
|
||||
roots := rs.rootModules
|
||||
rootsUpgraded := false
|
||||
|
||||
@ -688,6 +710,10 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
|
||||
//
|
||||
// (This is the “import invariant” that makes lazy loading possible.)
|
||||
|
||||
case rootsImported && pkg.flags.has(pkgFromRoot):
|
||||
// pkg is a transitive dependency of some root, and we are treating the
|
||||
// roots as if they are imported by the main module (as in 'go get').
|
||||
|
||||
case pkg.flags.has(pkgIsRoot):
|
||||
// pkg is a root of the package-import graph. (Generally this means that
|
||||
// it matches a command-line argument.) We want future invocations of the
|
||||
@ -815,7 +841,8 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
|
||||
|
||||
roots = make([]module.Version, 0, len(rs.rootModules))
|
||||
rootsUpgraded = false
|
||||
inRootPaths := make(map[string]bool, len(rs.rootModules))
|
||||
inRootPaths := make(map[string]bool, len(rs.rootModules)+1)
|
||||
inRootPaths[Target.Path] = true
|
||||
for _, m := range rs.rootModules {
|
||||
if inRootPaths[m.Path] {
|
||||
// This root specifies a redundant path. We already retained the
|
||||
|
@ -178,11 +178,13 @@ func (e *ImportMissingSumError) Error() string {
|
||||
// Importing package is unknown, or the missing package was named on the
|
||||
// command line. Recommend 'go mod download' for the modules that could
|
||||
// provide the package, since that shouldn't change go.mod.
|
||||
args := make([]string, len(e.mods))
|
||||
for i, mod := range e.mods {
|
||||
args[i] = mod.Path
|
||||
if len(e.mods) > 0 {
|
||||
args := make([]string, len(e.mods))
|
||||
for i, mod := range e.mods {
|
||||
args[i] = mod.Path
|
||||
}
|
||||
hint = fmt.Sprintf("; to add:\n\tgo mod download %s", strings.Join(args, " "))
|
||||
}
|
||||
hint = fmt.Sprintf("; to add:\n\tgo mod download %s", strings.Join(args, " "))
|
||||
} else {
|
||||
// Importing package is known (common case). Recommend 'go get' on the
|
||||
// current version of the importing package.
|
||||
@ -426,6 +428,15 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver
|
||||
mv = module.ZeroPseudoVersion("v0")
|
||||
}
|
||||
}
|
||||
mg, err := rs.Graph(ctx)
|
||||
if err != nil {
|
||||
return module.Version{}, err
|
||||
}
|
||||
if cmpVersion(mg.Selected(mp), mv) >= 0 {
|
||||
// We can't resolve the import by adding mp@mv to the module graph,
|
||||
// because the selected version of mp is already at least mv.
|
||||
continue
|
||||
}
|
||||
mods = append(mods, module.Version{Path: mp, Version: mv})
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ type Root int
|
||||
const (
|
||||
// AutoRoot is the default for most commands. modload.Init will look for
|
||||
// a go.mod file in the current directory or any parent. If none is found,
|
||||
// modules may be disabled (GO111MODULE=on) or commands may run in a
|
||||
// modules may be disabled (GO111MODULE=auto) or commands may run in a
|
||||
// limited module mode.
|
||||
AutoRoot Root = iota
|
||||
|
||||
@ -405,14 +405,23 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
||||
if modRoot == "" {
|
||||
Target = module.Version{Path: "command-line-arguments"}
|
||||
targetPrefix = "command-line-arguments"
|
||||
goVersion := latestGoVersion()
|
||||
goVersion := LatestGoVersion()
|
||||
rawGoVersion.Store(Target, goVersion)
|
||||
requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
|
||||
return requirements, false
|
||||
}
|
||||
|
||||
gomod := ModFilePath()
|
||||
data, err := lockedfile.Read(gomod)
|
||||
var data []byte
|
||||
var err error
|
||||
if gomodActual, ok := fsys.OverlayPath(gomod); ok {
|
||||
// Don't lock go.mod if it's part of the overlay.
|
||||
// On Plan 9, locking requires chmod, and we don't want to modify any file
|
||||
// in the overlay. See #44700.
|
||||
data, err = os.ReadFile(gomodActual)
|
||||
} else {
|
||||
data, err = lockedfile.Read(gomodActual)
|
||||
}
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
@ -432,7 +441,10 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
||||
initTarget(f.Module.Mod)
|
||||
index = indexModFile(data, f, fixed)
|
||||
|
||||
if err := checkModulePathLax(f.Module.Mod.Path); err != nil {
|
||||
if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
|
||||
if pathErr, ok := err.(*module.InvalidPathError); ok {
|
||||
pathErr.Kind = "module"
|
||||
}
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
|
||||
@ -448,7 +460,7 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
|
||||
// TODO(#45551): Do something more principled instead of checking
|
||||
// cfg.CmdName directly here.
|
||||
if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
|
||||
addGoStmt(latestGoVersion())
|
||||
addGoStmt(LatestGoVersion())
|
||||
if go117EnableLazyLoading {
|
||||
// We need to add a 'go' version to the go.mod file, but we must assume
|
||||
// that its existing contents match something between Go 1.11 and 1.16.
|
||||
@ -492,7 +504,15 @@ func CreateModFile(ctx context.Context, modPath string) {
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
} else if err := checkModulePathLax(modPath); err != nil {
|
||||
} else if err := module.CheckImportPath(modPath); err != nil {
|
||||
if pathErr, ok := err.(*module.InvalidPathError); ok {
|
||||
pathErr.Kind = "module"
|
||||
// Same as build.IsLocalPath()
|
||||
if pathErr.Path == "." || pathErr.Path == ".." ||
|
||||
strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
|
||||
pathErr.Err = errors.New("is a local import path")
|
||||
}
|
||||
}
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
|
||||
@ -500,7 +520,7 @@ func CreateModFile(ctx context.Context, modPath string) {
|
||||
modFile = new(modfile.File)
|
||||
modFile.AddModuleStmt(modPath)
|
||||
initTarget(modFile.Module.Mod)
|
||||
addGoStmt(latestGoVersion()) // Add the go directive before converted module requirements.
|
||||
addGoStmt(LatestGoVersion()) // Add the go directive before converted module requirements.
|
||||
|
||||
convertedFrom, err := convertLegacyConfig(modPath)
|
||||
if convertedFrom != "" {
|
||||
@ -536,49 +556,6 @@ func CreateModFile(ctx context.Context, modPath string) {
|
||||
}
|
||||
}
|
||||
|
||||
// checkModulePathLax checks that the path meets some minimum requirements
|
||||
// to avoid confusing users or the module cache. The requirements are weaker
|
||||
// than those of module.CheckPath to allow room for weakening module path
|
||||
// requirements in the future, but strong enough to help users avoid significant
|
||||
// problems.
|
||||
func checkModulePathLax(p string) error {
|
||||
// TODO(matloob): Replace calls of this function in this CL with calls
|
||||
// to module.CheckImportPath once it's been laxened, if it becomes laxened.
|
||||
// See golang.org/issue/29101 for a discussion about whether to make CheckImportPath
|
||||
// more lax or more strict.
|
||||
|
||||
errorf := func(format string, args ...interface{}) error {
|
||||
return fmt.Errorf("invalid module path %q: %s", p, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Disallow shell characters " ' * < > ? ` | to avoid triggering bugs
|
||||
// with file systems and subcommands. Disallow file path separators : and \
|
||||
// because path separators other than / will confuse the module cache.
|
||||
// See fileNameOK in golang.org/x/mod/module/module.go.
|
||||
shellChars := "`" + `"'*<>?|`
|
||||
fsChars := `\:`
|
||||
if i := strings.IndexAny(p, shellChars); i >= 0 {
|
||||
return errorf("contains disallowed shell character %q", p[i])
|
||||
}
|
||||
if i := strings.IndexAny(p, fsChars); i >= 0 {
|
||||
return errorf("contains disallowed path separator character %q", p[i])
|
||||
}
|
||||
|
||||
// Ensure path.IsAbs and build.IsLocalImport are false, and that the path is
|
||||
// invariant under path.Clean, also to avoid confusing the module cache.
|
||||
if path.IsAbs(p) {
|
||||
return errorf("is an absolute path")
|
||||
}
|
||||
if build.IsLocalImport(p) {
|
||||
return errorf("is a local import path")
|
||||
}
|
||||
if path.Clean(p) != p {
|
||||
return errorf("is not clean")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixVersion returns a modfile.VersionFixer implemented using the Query function.
|
||||
//
|
||||
// It resolves commit hashes and branch names to versions,
|
||||
@ -693,7 +670,7 @@ func requirementsFromModFile(ctx context.Context) *Requirements {
|
||||
for _, n := range mPathCount {
|
||||
if n > 1 {
|
||||
var err error
|
||||
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil)
|
||||
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
@ -793,17 +770,39 @@ func addGoStmt(v string) {
|
||||
rawGoVersion.Store(Target, v)
|
||||
}
|
||||
|
||||
// latestGoVersion returns the latest version of the Go language supported by
|
||||
// LatestGoVersion returns the latest version of the Go language supported by
|
||||
// this toolchain, like "1.17".
|
||||
func latestGoVersion() string {
|
||||
func LatestGoVersion() string {
|
||||
tags := build.Default.ReleaseTags
|
||||
version := tags[len(tags)-1]
|
||||
if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
|
||||
base.Fatalf("go: unrecognized default version %q", version)
|
||||
base.Fatalf("go: internal error: unrecognized default version %q", version)
|
||||
}
|
||||
return version[2:]
|
||||
}
|
||||
|
||||
// priorGoVersion returns the Go major release immediately preceding v,
|
||||
// or v itself if v is the first Go major release (1.0) or not a supported
|
||||
// Go version.
|
||||
func priorGoVersion(v string) string {
|
||||
vTag := "go" + v
|
||||
tags := build.Default.ReleaseTags
|
||||
for i, tag := range tags {
|
||||
if tag == vTag {
|
||||
if i == 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
version := tags[i-1]
|
||||
if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
|
||||
base.Fatalf("go: internal error: unrecognized version %q", version)
|
||||
}
|
||||
return version[2:]
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
var altConfigs = []string{
|
||||
"Gopkg.lock",
|
||||
|
||||
@ -918,14 +917,8 @@ func findModulePath(dir string) (string, error) {
|
||||
}
|
||||
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
|
||||
path := filepath.ToSlash(rel)
|
||||
// TODO(matloob): replace this with module.CheckImportPath
|
||||
// once it's been laxened.
|
||||
// Only checkModulePathLax here. There are some unpublishable
|
||||
// module names that are compatible with checkModulePathLax
|
||||
// but they already work in GOPATH so don't break users
|
||||
// trying to do a build with modules. gorelease will alert users
|
||||
// publishing their modules to fix their paths.
|
||||
if err := checkModulePathLax(path); err != nil {
|
||||
// gorelease will alert users publishing their modules to fix their paths.
|
||||
if err := module.CheckImportPath(path); err != nil {
|
||||
badPathErr = err
|
||||
break
|
||||
}
|
||||
@ -1015,10 +1008,14 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
|
||||
Indirect: !rs.direct[m.Path],
|
||||
})
|
||||
}
|
||||
modFile.SetRequire(list)
|
||||
if goVersion != "" {
|
||||
modFile.AddGoStmt(goVersion)
|
||||
}
|
||||
if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 {
|
||||
modFile.SetRequire(list)
|
||||
} else {
|
||||
modFile.SetRequireSeparateIndirect(list)
|
||||
}
|
||||
modFile.Cleanup()
|
||||
|
||||
dirty := index.modFileIsDirty(modFile)
|
||||
@ -1038,6 +1035,13 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
|
||||
}
|
||||
return
|
||||
}
|
||||
gomod := ModFilePath()
|
||||
if _, ok := fsys.OverlayPath(gomod); ok {
|
||||
if dirty {
|
||||
base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
new, err := modFile.Format()
|
||||
if err != nil {
|
||||
@ -1138,12 +1142,11 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
|
||||
}
|
||||
}
|
||||
|
||||
if rs.depth == lazy && rs.graph.Load() == nil {
|
||||
// The main module is lazy and we haven't needed to load the module graph so
|
||||
// far. Don't incur the cost of loading it now — since we haven't loaded the
|
||||
// graph, we probably don't have any checksums to contribute to the distant
|
||||
// parts of the graph anyway. Instead, just request sums for the roots that
|
||||
// we know about.
|
||||
if rs.graph.Load() == nil {
|
||||
// The module graph was not loaded, possibly because the main module is lazy
|
||||
// or possibly because we haven't needed to load the graph yet.
|
||||
// Save sums for the root modules (or their replacements), but don't
|
||||
// incur the cost of loading the graph just to find and retain the sums.
|
||||
for _, m := range rs.rootModules {
|
||||
r := resolveReplacement(m)
|
||||
keep[modkey(r)] = true
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user