mirror of
https://github.com/golang/go.git
synced 2026-01-30 07:32:05 +03:00
Compare commits
153 Commits
dev.corety
...
go1.18.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
947091d31c | ||
|
|
e9017c2416 | ||
|
|
0a723816cd | ||
|
|
9d2c73a9fd | ||
|
|
2b9596cb9b | ||
|
|
c8e1cf49d1 | ||
|
|
e40a130cc0 | ||
|
|
e0a364b061 | ||
|
|
d5a5db3b41 | ||
|
|
31d06b58fa | ||
|
|
170d78d9ba | ||
|
|
5bc9106458 | ||
|
|
889d326569 | ||
|
|
da8a350201 | ||
|
|
d2d59299a2 | ||
|
|
37cedd2697 | ||
|
|
175c2da725 | ||
|
|
7a575a5784 | ||
|
|
66197f01e1 | ||
|
|
569d949eea | ||
|
|
db26851593 | ||
|
|
bf812b32b0 | ||
|
|
27ced96c8d | ||
|
|
21befdc0c4 | ||
|
|
276a7bfff8 | ||
|
|
e1099eb289 | ||
|
|
e05bd75722 | ||
|
|
fcdd099a96 | ||
|
|
be59153dd8 | ||
|
|
9240558e4f | ||
|
|
be7c681478 | ||
|
|
6ff8801d3e | ||
|
|
76ba1a5e55 | ||
|
|
ed50277fd9 | ||
|
|
d06c911fe1 | ||
|
|
d252fdd630 | ||
|
|
4782f4275c | ||
|
|
12e00f6c6b | ||
|
|
88a06f40df | ||
|
|
fb979a5082 | ||
|
|
5ebd862b17 | ||
|
|
315e80d293 | ||
|
|
8e27a8ac4c | ||
|
|
0d1615b23f | ||
|
|
2924ced71d | ||
|
|
90f040ec51 | ||
|
|
ebea1e3353 | ||
|
|
222ee24a00 | ||
|
|
93aab18139 | ||
|
|
3a7cec2773 | ||
|
|
b80ae10221 | ||
|
|
e5ca37df46 | ||
|
|
5f2dff9b66 | ||
|
|
2f43de6da0 | ||
|
|
1872c338a3 | ||
|
|
f86c6b9a32 | ||
|
|
eeef5ebd84 | ||
|
|
460a93b54a | ||
|
|
3da88c0bdc | ||
|
|
f0eca661ea | ||
|
|
4068be56ce | ||
|
|
5a89bb334d | ||
|
|
da608a20ed | ||
|
|
a34f420774 | ||
|
|
c838098c32 | ||
|
|
6c65a4ab8e | ||
|
|
a08baaad1a | ||
|
|
e1b14f53c1 | ||
|
|
e46ac38370 | ||
|
|
32dedaa69e | ||
|
|
04337a6d10 | ||
|
|
cf5fa2b4a7 | ||
|
|
8ed0e51b5e | ||
|
|
020fc47501 | ||
|
|
b2b4b9e015 | ||
|
|
45568433ed | ||
|
|
6fabc53cae | ||
|
|
2375a6882e | ||
|
|
4f4542479d | ||
|
|
13bda0ebfb | ||
|
|
0287895996 | ||
|
|
c0599c5b78 | ||
|
|
2d6881b91a | ||
|
|
f4f19990c6 | ||
|
|
1fb46d2a20 | ||
|
|
9247bdddba | ||
|
|
500d75a555 | ||
|
|
66432dadb1 | ||
|
|
3e79a84853 | ||
|
|
a286c7dd79 | ||
|
|
c6688b58ea | ||
|
|
94274d0ffd | ||
|
|
24fcbb9808 | ||
|
|
0b0d2fe66d | ||
|
|
84264fceca | ||
|
|
9535031e32 | ||
|
|
2097b2d51b | ||
|
|
c9b9a010eb | ||
|
|
f92bfa440f | ||
|
|
abb3f05524 | ||
|
|
c6ba470316 | ||
|
|
30d9077a34 | ||
|
|
6412231192 | ||
|
|
0bf8319883 | ||
|
|
78b8b4a176 | ||
|
|
6ebf676289 | ||
|
|
69bc821a01 | ||
|
|
c258e9d07d | ||
|
|
d17c5731ba | ||
|
|
f4d55662d6 | ||
|
|
290883a559 | ||
|
|
39a8c8cf2e | ||
|
|
9d738480d2 | ||
|
|
32ff9b5de6 | ||
|
|
8b0583a054 | ||
|
|
677c6fe94c | ||
|
|
4aa1efed48 | ||
|
|
428533fab4 | ||
|
|
4b9b25a21d | ||
|
|
d69d093c77 | ||
|
|
c79ccd88ab | ||
|
|
8706c09622 | ||
|
|
2c6a889234 | ||
|
|
1edc1ccf15 | ||
|
|
a0c7e2620a | ||
|
|
c6b5b7e6e5 | ||
|
|
36d32da19f | ||
|
|
1e4dc06f1a | ||
|
|
d10b8192c0 | ||
|
|
bf366ef711 | ||
|
|
92644ff54a | ||
|
|
673d52b33a | ||
|
|
e3f9a4f2ae | ||
|
|
c827ddf9e5 | ||
|
|
aeced24498 | ||
|
|
f913f9dd1a | ||
|
|
a54f962c29 | ||
|
|
7f9a85f2d9 | ||
|
|
520f0d0401 | ||
|
|
0b38b0277e | ||
|
|
40f6480db6 | ||
|
|
839c0f3b3e | ||
|
|
ce427cf961 | ||
|
|
79cf7c839b | ||
|
|
9f40b4f7a4 | ||
|
|
9f5e2849e1 | ||
|
|
77a142486e | ||
|
|
fd5b9b7c07 | ||
|
|
6206d65235 | ||
|
|
9f2f0ee6ce | ||
|
|
cb5a598d7f | ||
|
|
e70ee95914 | ||
|
|
39d2f50919 |
@@ -1 +1,2 @@
|
||||
branch: master
|
||||
branch: release-branch.go1.18
|
||||
parent-branch: master
|
||||
|
||||
412
doc/go_spec.html
412
doc/go_spec.html
@@ -1,26 +1,16 @@
|
||||
<!--{
|
||||
"Title": "The Go Programming Language Specification - Go 1.18 Draft",
|
||||
"Subtitle": "Version of Feb 28, 2022",
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of March 10, 2022",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
<h2>Earlier version</h2>
|
||||
|
||||
<p>
|
||||
For the pre-Go1.18 specification without generics support see
|
||||
<a href="/doc/go1.17_spec.html">The Go Programming Language Specification</a>.
|
||||
</p>
|
||||
|
||||
<!-- TODO(gri) remove this before the final release -->
|
||||
<p><b>
|
||||
[For reviewers: Sections where we know of missing prose are marked like this. The markers will be removed before the release.]
|
||||
</b></p>
|
||||
|
||||
<h2 id="Introduction">Introduction</h2>
|
||||
|
||||
<p>
|
||||
This is a reference manual for the Go programming language. For
|
||||
more information and other documents, see <a href="/">golang.org</a>.
|
||||
This is the reference manual for the Go programming language.
|
||||
The pre-Go1.18 version, without generics, can be found
|
||||
<a href="/doc/go1.17_spec.html">here</a>.
|
||||
For more information and other documents, see <a href="/">golang.org</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -766,7 +756,7 @@ type given in its declaration, the type provided in the
|
||||
<code>new</code> call or composite literal, or the type of
|
||||
an element of a structured variable.
|
||||
Variables of interface type also have a distinct <i>dynamic type</i>,
|
||||
which is the concrete type of the value assigned to the variable at run time
|
||||
which is the (non-interface) type of the value assigned to the variable at run time
|
||||
(unless the value is the predeclared identifier <code>nil</code>,
|
||||
which has no type).
|
||||
The dynamic type may vary during execution but values stored in interface
|
||||
@@ -812,7 +802,7 @@ TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType
|
||||
<p>
|
||||
The language <a href="#Predeclared_identifiers">predeclares</a> certain type names.
|
||||
Others are introduced with <a href="#Type_declarations">type declarations</a>
|
||||
or <a href="#Type_parameter_lists">type parameter lists</a>.
|
||||
or <a href="#Type_parameter_declarations">type parameter lists</a>.
|
||||
<i>Composite types</i>—array, struct, pointer, function,
|
||||
interface, slice, map, and channel types—may be constructed using
|
||||
type literals.
|
||||
@@ -987,7 +977,7 @@ built-in function <a href="#Length_and_capacity"><code>cap(a)</code></a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A new, initialized slice value for a given element type <code>T</code> is
|
||||
A new, initialized slice value for a given element type <code>T</code> may be
|
||||
made using the built-in function
|
||||
<a href="#Making_slices_maps_and_channels"><code>make</code></a>,
|
||||
which takes a slice type
|
||||
@@ -1422,7 +1412,7 @@ interface {
|
||||
~int
|
||||
}
|
||||
|
||||
// An interface representing all types with underlying type int which implement the String method.
|
||||
// An interface representing all types with underlying type int that implement the String method.
|
||||
interface {
|
||||
~int
|
||||
String() string
|
||||
@@ -1455,32 +1445,32 @@ Union elements denote unions of type sets:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
// The Floats interface represents all floating-point types
|
||||
// The Float interface represents all floating-point types
|
||||
// (including any named types whose underlying types are
|
||||
// either float32 or float64).
|
||||
type Floats interface {
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
In a union, a term cannot be a type parameter, and the type sets of all
|
||||
In a union, a term cannot be a <a href="#Type_parameter_declarations">type parameter</a>, and the type sets of all
|
||||
non-interface terms must be pairwise disjoint (the pairwise intersection of the type sets must be empty).
|
||||
Given a type parameter <code>P</code>:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
interface {
|
||||
P // illegal: the term P is a type parameter
|
||||
int | P // illegal: the term P is a type parameter
|
||||
~int | MyInt // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
|
||||
float32 | Floats // overlapping type sets but Floats is an interface
|
||||
P // illegal: P is a type parameter
|
||||
int | P // illegal: P is a type parameter
|
||||
~int | MyInt // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
|
||||
float32 | Float // overlapping type sets but Float is an interface
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Implementation restriction:
|
||||
A union with more than one term cannot contain the
|
||||
A union (with more than one term) cannot contain the
|
||||
<a href="#Predeclared_identifiers">predeclared identifier</a> <code>comparable</code>
|
||||
or interfaces that specify methods, or embed <code>comparable</code> or interfaces
|
||||
that specify methods.
|
||||
@@ -1494,12 +1484,12 @@ non-interface types.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var x Floats // illegal: Floats is not a basic interface
|
||||
var x Float // illegal: Float is not a basic interface
|
||||
|
||||
var x interface{} = Floats(nil) // illegal
|
||||
var x interface{} = Float(nil) // illegal
|
||||
|
||||
type Floatish struct {
|
||||
f Floats // illegal
|
||||
f Float // illegal
|
||||
}
|
||||
</pre>
|
||||
|
||||
@@ -1545,7 +1535,7 @@ A type <code>T</code> implements an interface <code>I</code> if
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
A value <code>x</code> of type <code>T</code> implements an interface if <code>T</code>
|
||||
A value of type <code>T</code> implements an interface if <code>T</code>
|
||||
implements the interface.
|
||||
</p>
|
||||
|
||||
@@ -1701,10 +1691,9 @@ Each type <code>T</code> has an <i>underlying type</i>: If <code>T</code>
|
||||
is one of the predeclared boolean, numeric, or string types, or a type literal,
|
||||
the corresponding underlying type is <code>T</code> itself.
|
||||
Otherwise, <code>T</code>'s underlying type is the underlying type of the
|
||||
type to which <code>T</code> refers in its <a href="#Type_declarations">type
|
||||
declaration</a>. The underlying type of a type parameter is the
|
||||
underlying type of its <a href="#Type_constraints">type constraint</a>, which
|
||||
is always an interface.
|
||||
type to which <code>T</code> refers in its declaration.
|
||||
For a type parameter that is the underlying type of its
|
||||
<a href="#Type_constraints">type constraint</a>, which is always an interface.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -1755,7 +1744,7 @@ direction.
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
All other interfaces don't have a core type.
|
||||
No other interfaces have a core type.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -1775,7 +1764,7 @@ depending on the direction of the directional channels present.
|
||||
|
||||
<p>
|
||||
By definition, a core type is never a <a href="#Type_definitions">defined type</a>,
|
||||
<a href="#Type_parameter_lists">type parameter</a>, or
|
||||
<a href="#Type_parameter_declarations">type parameter</a>, or
|
||||
<a href="#Interface_types">interface type</a>.
|
||||
</p>
|
||||
|
||||
@@ -1795,7 +1784,7 @@ interface{ ~[]*data; String() string } // []*data
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Examples of interfaces whithout core types:
|
||||
Examples of interfaces without core types:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -1805,70 +1794,6 @@ interface{ chan int | chan<- string } // channels have different element
|
||||
interface{ <-chan int | chan<- int } // directional channels have different directions
|
||||
</pre>
|
||||
|
||||
<h3 id="Specific_types">Specific types</h3>
|
||||
|
||||
<p><b>
|
||||
[The definition of specific types is not quite correct yet.]
|
||||
</b></p>
|
||||
|
||||
<p>
|
||||
An interface specification that contains <a href="#Interface_types">type elements</a>
|
||||
defines a (possibly empty) set of <i>specific types</i>.
|
||||
Loosely speaking, these are the types <code>T</code> that appear in the
|
||||
interface definition in terms of the form <code>T</code>, <code>~T</code>,
|
||||
or in unions of such terms.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
More precisely, for a given interface, the set of specific types corresponds to
|
||||
the set 𝑅 of representative types of the interface, if 𝑅 is non-empty and finite.
|
||||
Otherwise, if 𝑅 is empty or infinite, the interface has <i>no specific types</i>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For a given interface, type element or type term, the set 𝑅 of representative types is defined as follows:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>For an interface with no type elements, 𝑅 is the (infinite) set of all types.
|
||||
</li>
|
||||
|
||||
<li>For an interface with type elements,
|
||||
𝑅 is the intersection of the representative types of its type elements.
|
||||
</li>
|
||||
|
||||
<li>For a non-interface type term <code>T</code> or a term of the form <code>~T</code>,
|
||||
𝑅 is the set consisting of the type <code>T</code>.
|
||||
</li>
|
||||
|
||||
<li>For a <i>union</i> of terms
|
||||
<code>t<sub>1</sub>|t<sub>2</sub>|…|t<sub>n</sub></code>,
|
||||
𝑅 is the union of the representative types of the terms.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
An interface may have specific types even if its <a href="#Interface_types">type set</a>
|
||||
is empty.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Examples of interfaces with their specific types:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
interface{} // no specific types
|
||||
interface{ int } // int
|
||||
interface{ ~string } // string
|
||||
interface{ int|~string } // int, string
|
||||
interface{ Celsius|Kelvin } // Celsius, Kelvin
|
||||
interface{ float64|any } // no specific types (union is all types)
|
||||
interface{ int; m() } // int (but type set is empty because int has no method m)
|
||||
interface{ ~int; m() } // int (but type set is infinite because many integer types have a method m)
|
||||
interface{ int; any } // int
|
||||
interface{ int; string } // no specific types (intersection is empty)
|
||||
</pre>
|
||||
|
||||
<h3 id="Type_identity">Type identity</h3>
|
||||
|
||||
<p>
|
||||
@@ -1973,21 +1898,21 @@ defined type while the latter is a type literal
|
||||
<h3 id="Assignability">Assignability</h3>
|
||||
|
||||
<p>
|
||||
A value <code>x</code> is <i>assignable</i> to a <a href="#Variables">variable</a> of type <code>T</code>
|
||||
A value <code>x</code> of type <code>V</code> is <i>assignable</i> to a <a href="#Variables">variable</a> of type <code>T</code>
|
||||
("<code>x</code> is assignable to <code>T</code>") if one of the following conditions applies:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>x</code>'s type is identical to <code>T</code>.
|
||||
<code>V</code> and <code>T</code> are identical.
|
||||
</li>
|
||||
<li>
|
||||
<code>x</code>'s type <code>V</code> and <code>T</code> have identical
|
||||
<code>V</code> and <code>T</code> have identical
|
||||
<a href="#Underlying_types">underlying types</a> and at least one of <code>V</code>
|
||||
or <code>T</code> is not a <a href="#Types">named type</a>.
|
||||
</li>
|
||||
<li>
|
||||
<code>x</code>'s type <code>V</code> and <code>T</code> are channel types with
|
||||
<code>V</code> and <code>T</code> are channel types with
|
||||
identical element types, <code>V</code> is a bidirectional channel,
|
||||
and at least one of <code>V</code> or <code>T</code> is not a <a href="#Types">named type</a>.
|
||||
</li>
|
||||
@@ -2008,25 +1933,24 @@ by a value of type <code>T</code>.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Additionally, if <code>x</code>'s type <code>V</code> or <code>T</code> are type parameters
|
||||
with <a href="#Specific_types">specific types</a>, <code>x</code>
|
||||
Additionally, if <code>x</code>'s type <code>V</code> or <code>T</code> are type parameters, <code>x</code>
|
||||
is assignable to a variable of type <code>T</code> if one of the following conditions applies:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>x</code> is the predeclared identifier <code>nil</code>, <code>T</code> is
|
||||
a type parameter, and <code>x</code> is assignable to each specific type of
|
||||
<code>T</code>.
|
||||
a type parameter, and <code>x</code> is assignable to each type in
|
||||
<code>T</code>'s type set.
|
||||
</li>
|
||||
<li>
|
||||
<code>V</code> is not a <a href="#Types">named type</a>, <code>T</code> is
|
||||
a type parameter, and <code>x</code> is assignable to each specific type of
|
||||
<code>T</code>.
|
||||
a type parameter, and <code>x</code> is assignable to each type in
|
||||
<code>T</code>'s type set.
|
||||
</li>
|
||||
<li>
|
||||
<code>V</code> is a type parameter and <code>T</code> is not a named type,
|
||||
and values of each specific type of <code>V</code> are assignable
|
||||
and values of each type in <code>V</code>'s type set are assignable
|
||||
to <code>T</code>.
|
||||
</li>
|
||||
</ul>
|
||||
@@ -2036,7 +1960,7 @@ to <code>T</code>.
|
||||
<p>
|
||||
A <a href="#Constants">constant</a> <code>x</code> is <i>representable</i>
|
||||
by a value of type <code>T</code>,
|
||||
where <code>T</code> is not a <a href="#Type_parameter_lists">type parameter</a>,
|
||||
where <code>T</code> is not a <a href="#Type_parameter_declarations">type parameter</a>,
|
||||
if one of the following conditions applies:
|
||||
</p>
|
||||
|
||||
@@ -2061,9 +1985,9 @@ are representable by values of <code>T</code>'s component type (<code>float32</c
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
If <code>T</code> is a type parameter with <a href="#Specific_types">specific types</a>,
|
||||
If <code>T</code> is a type parameter,
|
||||
<code>x</code> is representable by a value of type <code>T</code> if <code>x</code> is representable
|
||||
by a value of each specific type of <code>T</code>.
|
||||
by a value of each type in <code>T</code>'s type set.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -2176,6 +2100,7 @@ Blocks nest and influence <a href="#Declarations_and_scope">scoping</a>.
|
||||
A <i>declaration</i> binds a non-<a href="#Blank_identifier">blank</a> identifier to a
|
||||
<a href="#Constant_declarations">constant</a>,
|
||||
<a href="#Type_declarations">type</a>,
|
||||
<a href="#Type_parameter_declarations">type parameter</a>,
|
||||
<a href="#Variable_declarations">variable</a>,
|
||||
<a href="#Function_declarations">function</a>,
|
||||
<a href="#Labeled_statements">label</a>, or
|
||||
@@ -2220,13 +2145,13 @@ Go is lexically scoped using <a href="#Blocks">blocks</a>:
|
||||
<li>The scope of an identifier denoting a method receiver, function parameter,
|
||||
or result variable is the function body.</li>
|
||||
|
||||
<li>The scope of an identifier denoting a type parameter of a generic function
|
||||
<li>The scope of an identifier denoting a type parameter of a function
|
||||
or declared by a method receiver is the function body and all parameter lists of the
|
||||
function.
|
||||
</li>
|
||||
|
||||
<li>The scope of an identifier denoting a type parameter of a generic type
|
||||
begins after the name of the generic type and ends at the end
|
||||
<li>The scope of an identifier denoting a type parameter of a type
|
||||
begins after the name of the type and ends at the end
|
||||
of the TypeSpec.</li>
|
||||
|
||||
<li>The scope of a constant or variable identifier declared
|
||||
@@ -2512,7 +2437,7 @@ type (
|
||||
|
||||
type TreeNode struct {
|
||||
left, right *TreeNode
|
||||
value *Comparable
|
||||
value any
|
||||
}
|
||||
|
||||
type Block interface {
|
||||
@@ -2573,7 +2498,7 @@ func (tz TimeZone) String() string {
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If the type definition specifies <a href="#Type_parameter_lists">type parameters</a>,
|
||||
If the type definition specifies <a href="#Type_parameter_declarations">type parameters</a>,
|
||||
the type name denotes a <i>generic type</i>.
|
||||
Generic types must be <a href="#Instantiations">instantiated</a> when they
|
||||
are used.
|
||||
@@ -2584,15 +2509,10 @@ type List[T any] struct {
|
||||
next *List[T]
|
||||
value T
|
||||
}
|
||||
|
||||
type Tree[T constraints.Ordered] struct {
|
||||
left, right *Tree[T]
|
||||
value T
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The given type cannot be a type parameter in a type definition.
|
||||
In a type definition the given type cannot be a type parameter.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -2604,8 +2524,8 @@ func f[T any]() {
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A generic type may also have methods associated with it. In this case,
|
||||
the method receivers must declare the same number of type parameters as
|
||||
A generic type may also have <a href="#Method_declarations">methods</a> associated with it.
|
||||
In this case, the method receivers must declare the same number of type parameters as
|
||||
present in the generic type definition.
|
||||
</p>
|
||||
|
||||
@@ -2614,7 +2534,7 @@ present in the generic type definition.
|
||||
func (l *List[T]) Len() int { … }
|
||||
</pre>
|
||||
|
||||
<h3 id="Type_parameter_lists">Type parameter lists</h3>
|
||||
<h3 id="Type_parameter_declarations">Type parameter declarations</h3>
|
||||
|
||||
<p>
|
||||
A type parameter list declares the <i>type parameters</i> of a generic function or type declaration.
|
||||
@@ -2653,22 +2573,22 @@ has a corresponding (meta-)type which is called its
|
||||
|
||||
<p>
|
||||
A parsing ambiguity arises when the type parameter list for a generic type
|
||||
declares a single type parameter with a type constraint of the form <code>*C</code>
|
||||
or <code>(C)</code> where <code>C</code> is not a (possibly parenthesized)
|
||||
<a href="#Types">type literal</a>:
|
||||
declares a single type parameter <code>P</code> with a constraint <code>C</code>
|
||||
such that the text <code>P C</code> forms a valid expression:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
type T[P *C] …
|
||||
type T[P (C)] …
|
||||
type T[P *C|Q] …
|
||||
…
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
In these rare cases, the type parameter declaration is indistinguishable from
|
||||
the expressions <code>P*C</code> or <code>P(C)</code> and the type declaration
|
||||
is parsed as an array type declaration.
|
||||
To resolve the ambiguity, embed the constraint in an interface or use a trailing
|
||||
comma:
|
||||
In these rare cases, the type parameter list is indistinguishable from an
|
||||
expression and the type declaration is parsed as an array type declaration.
|
||||
To resolve the ambiguity, embed the constraint in an
|
||||
<a href="#Interface_types">interface</a> or use a trailing comma:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -2682,6 +2602,11 @@ of a <a href="#Method_declarations">method declaration</a> associated
|
||||
with a generic type.
|
||||
</p>
|
||||
|
||||
<!--
|
||||
This section needs to explain if and what kind of cycles are permitted
|
||||
using type parameters in a type parameter list.
|
||||
-->
|
||||
|
||||
<h4 id="Type_constraints">Type constraints</h4>
|
||||
|
||||
<p>
|
||||
@@ -2701,10 +2626,10 @@ the enclosing <code>interface{ … }</code> may be omitted for convenience:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
[T *P] // = [T interface{*P}]
|
||||
[T ~int] // = [T interface{~int}]
|
||||
[T int|string] // = [T interface{int|string}]
|
||||
type Constraint ~int // illegal: ~int is not inside a type parameter list
|
||||
[T []P] // = [T interface{[]P}]
|
||||
[T ~int] // = [T interface{~int}]
|
||||
[T int|string] // = [T interface{int|string}]
|
||||
type Constraint ~int // illegal: ~int is not inside a type parameter list
|
||||
</pre>
|
||||
|
||||
<!--
|
||||
@@ -2716,7 +2641,7 @@ other interfaces based on their type sets. But this should get us going for now.
|
||||
<p>
|
||||
The <a href="#Predeclared_identifiers">predeclared</a>
|
||||
<a href="#Interface_types">interface type</a> <code>comparable</code>
|
||||
denotes the set of all concrete (non-interface) types that are
|
||||
denotes the set of all non-interface types that are
|
||||
<a href="#Comparison_operators">comparable</a>. Specifically,
|
||||
a type <code>T</code> implements <code>comparable</code> if:
|
||||
</p>
|
||||
@@ -2897,14 +2822,14 @@ func IndexRune(s string, r rune) int {
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If the function declaration specifies <a href="#Type_parameter_lists">type parameters</a>,
|
||||
If the function declaration specifies <a href="#Type_parameter_declarations">type parameters</a>,
|
||||
the function name denotes a <i>generic function</i>.
|
||||
Generic functions must be <a href="#Instantiations">instantiated</a> when they
|
||||
are used.
|
||||
A generic function must be <a href="#Instantiations">instantiated</a> before it can be
|
||||
called or used as a value.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
func min[T constraints.Ordered](x, y T) T {
|
||||
func min[T ~int|~float64](x, y T) T {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
@@ -2963,7 +2888,7 @@ the non-blank method and field names must be distinct.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given defined type <code>Point</code>, the declarations
|
||||
Given defined type <code>Point</code> the declarations
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -2987,13 +2912,10 @@ to the base type <code>Point</code>.
|
||||
If the receiver base type is a <a href="#Type_declarations">generic type</a>, the
|
||||
receiver specification must declare corresponding type parameters for the method
|
||||
to use. This makes the receiver type parameters available to the method.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Syntactically, this type parameter declaration looks like an
|
||||
<a href="#Instantiations">instantiation</a> of the receiver base type, except that
|
||||
the type arguments are the type parameters being declared, one for each type parameter
|
||||
of the receiver base type.
|
||||
<a href="#Instantiations">instantiation</a> of the receiver base type: the type
|
||||
arguments must be identifiers denoting the type parameters being declared, one
|
||||
for each type parameter of the receiver base type.
|
||||
The type parameter names do not need to match their corresponding parameter names in the
|
||||
receiver base type definition, and all non-blank parameter names must be unique in the
|
||||
receiver parameter section and the method signature.
|
||||
@@ -3007,8 +2929,8 @@ type Pair[A, B any] struct {
|
||||
b B
|
||||
}
|
||||
|
||||
func (p Pair[A, B]) Swap() Pair[B, A] { return Pair[B, A]{p.b, p.a} }
|
||||
func (p Pair[First, _]) First() First { return p.a }
|
||||
func (p Pair[A, B]) Swap() Pair[B, A] { … } // receiver declares A, B
|
||||
func (p Pair[First, _]) First() First { … } // receiver declares First, corresponds to A in Pair
|
||||
</pre>
|
||||
|
||||
<h2 id="Expressions">Expressions</h2>
|
||||
@@ -3048,6 +2970,14 @@ The <a href="#Blank_identifier">blank identifier</a> may appear as an
|
||||
operand only on the left-hand side of an <a href="#Assignments">assignment</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Implementation restriction: A compiler need not report an error if an operand's
|
||||
type is a <a href="#Type_parameter_declarations">type parameter</a> with an empty
|
||||
<a href="#Interface_types">type set</a>. Functions with such type parameters
|
||||
cannot be <a href="#Instantiations">instantiated</a>; any attempt will lead
|
||||
to an error at the instantiation site.
|
||||
</p>
|
||||
|
||||
<h3 id="Qualified_identifiers">Qualified identifiers</h3>
|
||||
|
||||
<p>
|
||||
@@ -3354,10 +3284,6 @@ f.p[i].x()
|
||||
|
||||
<h3 id="Selectors">Selectors</h3>
|
||||
|
||||
<p><b>
|
||||
[This section is missing rules for x.f where x's type is a type parameter and f is a field.]
|
||||
</b></p>
|
||||
|
||||
<p>
|
||||
For a <a href="#Primary_expressions">primary expression</a> <code>x</code>
|
||||
that is not a <a href="#Package_clause">package name</a>, the
|
||||
@@ -3758,7 +3684,7 @@ The following rules apply:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If <code>a</code> is not a map:
|
||||
If <code>a</code> is neither a map nor a type parameter:
|
||||
</p>
|
||||
<ul>
|
||||
<li>the index <code>x</code> must be an untyped constant or its
|
||||
@@ -3827,23 +3753,22 @@ For <code>a</code> of <a href="#Map_types">map type</a> <code>M</code>:
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
For <code>a</code> of <a href="#Type_parameter_lists">type parameter type</a> <code>P</code>:
|
||||
For <code>a</code> of <a href="#Type_parameter_declarations">type parameter type</a> <code>P</code>:
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>P</code> must have <a href="#Specific_types">specific types</a>.</li>
|
||||
<li>The index expression <code>a[x]</code> must be valid for values
|
||||
of all specific types of <code>P</code>.</li>
|
||||
<li>The element types of all specific types of <code>P</code> must be identical.
|
||||
of all types in <code>P</code>'s type set.</li>
|
||||
<li>The element types of all types in <code>P</code>'s type set must be identical.
|
||||
In this context, the element type of a string type is <code>byte</code>.</li>
|
||||
<li>If there is a map type among the specific types of <code>P</code>,
|
||||
all specific types must be map types, and the respective key types
|
||||
<li>If there is a map type in the type set of <code>P</code>,
|
||||
all types in that type set must be map types, and the respective key types
|
||||
must be all identical.</li>
|
||||
<li><code>a[x]</code> is the array, slice, or string element at index <code>x</code>,
|
||||
or the map element with key <code>x</code> of the type argument
|
||||
that <code>P</code> is instantiated with, and the type of <code>a[x]</code> is
|
||||
the type of the (identical) element types.</li>
|
||||
<li><code>a[x]</code> may not be assigned to if the specific types of <code>P</code>
|
||||
include string types.
|
||||
<li><code>a[x]</code> may not be assigned to if <code>P</code>'s type set
|
||||
includes string types.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
@@ -4021,7 +3946,7 @@ If the indices are out of range at run time, a <a href="#Run_time_panics">run-ti
|
||||
|
||||
<p>
|
||||
For an expression <code>x</code> of <a href="#Interface_types">interface type</a>,
|
||||
but not a <a href="#Type_parameter_lists">type parameter</a>, and a type <code>T</code>,
|
||||
but not a <a href="#Type_parameter_declarations">type parameter</a>, and a type <code>T</code>,
|
||||
the primary expression
|
||||
</p>
|
||||
|
||||
@@ -4236,7 +4161,7 @@ with the same underlying array.
|
||||
<p>
|
||||
A generic function or type is <i>instantiated</i> by substituting <i>type arguments</i>
|
||||
for the type parameters.
|
||||
Instantiation proceeds in two phases:
|
||||
Instantiation proceeds in two steps:
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
@@ -4249,7 +4174,7 @@ including the type parameter list itself and any types in that list.
|
||||
|
||||
<li>
|
||||
After substitution, each type argument must <a href="#Interface_types">implement</a>
|
||||
the <a href="#Type_parameter_lists">constraint</a> (instantiated, if necessary)
|
||||
the <a href="#Type_parameter_declarations">constraint</a> (instantiated, if necessary)
|
||||
of the corresponding type parameter. Otherwise instantiation fails.
|
||||
</li>
|
||||
</ol>
|
||||
@@ -4262,55 +4187,57 @@ instantiating a function produces a new non-generic function.
|
||||
<pre>
|
||||
type parameter list type arguments after substitution
|
||||
|
||||
[P any] int [int any]
|
||||
[S ~[]E, E any] []int, int [[]int ~[]int, int any]
|
||||
[P io.Writer] string [string io.Writer] // illegal: string doesn't implement io.Writer
|
||||
[P any] int int implements any
|
||||
[S ~[]E, E any] []int, int []int implements ~[]int, int implements any
|
||||
[P io.Writer] string illegal: string doesn't implement io.Writer
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Type arguments may be provided explicitly, or they may be partially or completely
|
||||
<a href="#Type_inference">inferred</a>.
|
||||
A partially provided type argument list cannot be empty; there must be at least the
|
||||
first argument.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
type T[P1 ~int, P2 ~[]P1] struct{ … }
|
||||
|
||||
T[] // illegal: at least the first type argument must be present, even if it could be inferred
|
||||
T[int] // argument for P1 explicitly provided, argument for P2 inferred
|
||||
T[int, []int] // both arguments explicitly provided
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A partial type argument list specifies a prefix of the full list of type arguments, leaving
|
||||
the remaining arguments to be inferred. Loosely speaking, type arguments may be omitted from
|
||||
"right to left".
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Generic types, and generic functions that are not <a href="#Calls">called</a>,
|
||||
require a type argument list for instantiation; if the list is partial, all
|
||||
For a generic function, type arguments may be provided explicitly, or they
|
||||
may be partially or completely <a href="#Type_inference">inferred</a>.
|
||||
A generic function that is is <i>not</i> <a href="#Calls">called</a> requires a
|
||||
type argument list for instantiation; if the list is partial, all
|
||||
remaining type arguments must be inferrable.
|
||||
Calls to generic functions may provide a (possibly partial) type
|
||||
A generic function that is called may provide a (possibly partial) type
|
||||
argument list, or may omit it entirely if the omitted type arguments are
|
||||
inferrable from the ordinary (non-type) function arguments.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
func min[T constraints.Ordered](x, y T) T { … }
|
||||
func min[T ~int|~float64](x, y T) T { … }
|
||||
|
||||
f := min // illegal: min must be instantiated when used without being called
|
||||
f := min // illegal: min must be instantiated with type arguments when used without being called
|
||||
minInt := min[int] // minInt has type func(x, y int) int
|
||||
a := minInt(2, 3) // a has value 2 of type int
|
||||
b := min[float64](2.0, 3) // b has value 2.0 of type float64
|
||||
c := min(b, -1) // c has value -1.0 of type float64
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A partial type argument list cannot be empty; at least the first argument must be present.
|
||||
The list is a prefix of the full list of type arguments, leaving the remaining arguments
|
||||
to be inferred. Loosely speaking, type arguments may be omitted from "right to left".
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
func apply[S ~[]E, E any](s S, f(E) E) S { … }
|
||||
|
||||
f0 := apply[] // illegal: type argument list cannot be empty
|
||||
f1 := apply[[]int] // type argument for S explicitly provided, type argument for E inferred
|
||||
f2 := apply[[]string, string] // both type arguments explicitly provided
|
||||
|
||||
var bytes []byte
|
||||
r := apply(bytes, func(byte) byte { … }) // both type arguments inferred from the function arguments
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
For a generic type, all type arguments must always be provided explicitly.
|
||||
</p>
|
||||
|
||||
<h3 id="Type_inference">Type inference</h3>
|
||||
|
||||
<p>
|
||||
Missing type arguments may be <i>inferred</i> by a series of steps, described below.
|
||||
Missing function type arguments may be <i>inferred</i> by a series of steps, described below.
|
||||
Each step attempts to use known information to infer additional type arguments.
|
||||
Type inference stops as soon as all type arguments are known.
|
||||
After type inference is complete, it is still necessary to substitute all type arguments
|
||||
@@ -4326,7 +4253,7 @@ Type inference is based on
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
a <a href="#Type_parameter_lists">type parameter list</a>
|
||||
a <a href="#Type_parameter_declarations">type parameter list</a>
|
||||
</li>
|
||||
<li>
|
||||
a substitution map <i>M</i> initialized with the known type arguments, if any
|
||||
@@ -4491,9 +4418,8 @@ unresolved type parameters left.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Function argument type inference can be used when the function has ordinary parameters
|
||||
whose types are defined using the function's type parameters. Inference happens in two
|
||||
separate phases; each phase operates on a specific list of (parameter, argument) pairs:
|
||||
Inference happens in two separate phases; each phase operates on a specific list of
|
||||
(parameter, argument) pairs:
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
@@ -4550,7 +4476,7 @@ Example:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
func min[T constraints.Ordered](x, y T) T
|
||||
func min[T ~int|~float64](x, y T) T
|
||||
|
||||
var x int
|
||||
min(x, 2.0) // T is int, inferred from typed argument x; 2.0 is assignable to int
|
||||
@@ -4841,9 +4767,8 @@ The bitwise logical and shift operators apply to integers only.
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Excluding shifts, if the operand type is a <a href="#Type_parameter_lists">type parameter</a>,
|
||||
it must have <a href="#Specific_types">specific types</a>, and the operator must
|
||||
apply to each specific type.
|
||||
If the operand type is a <a href="#Type_parameter_declarations">type parameter</a>,
|
||||
the operator must apply to each type in that type set.
|
||||
The operands are represented as values of the type argument that the type parameter
|
||||
is <a href="#Instantiations">instantiated</a> with, and the operation is computed
|
||||
with the precision of that type argument. For example, given the function:
|
||||
@@ -4866,11 +4791,6 @@ are computed with <code>float32</code> or <code>float64</code> precision,
|
||||
respectively, depending on the type argument for <code>F</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For shifts, the <a href="#Core_types">core type</a> of both operands must be
|
||||
an integer.
|
||||
</p>
|
||||
|
||||
<h4 id="Integer_operators">Integer operators</h4>
|
||||
|
||||
<p>
|
||||
@@ -5296,7 +5216,7 @@ as for non-constant <code>x</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Converting a constant to a type that is not a <a href="#Type_parameter_lists">type parameter</a>
|
||||
Converting a constant to a type that is not a <a href="#Type_parameter_declarations">type parameter</a>
|
||||
yields a typed constant.
|
||||
</p>
|
||||
|
||||
@@ -5351,7 +5271,7 @@ in any of these cases:
|
||||
<li>
|
||||
ignoring struct tags (see below),
|
||||
<code>x</code>'s type and <code>T</code> are not
|
||||
<a href="#Type_parameter_lists">type parameters</a> but have
|
||||
<a href="#Type_parameter_declarations">type parameters</a> but have
|
||||
<a href="#Type_identity">identical</a> <a href="#Types">underlying types</a>.
|
||||
</li>
|
||||
<li>
|
||||
@@ -5383,23 +5303,23 @@ in any of these cases:
|
||||
|
||||
<p>
|
||||
Additionally, if <code>T</code> or <code>x</code>'s type <code>V</code> are type
|
||||
parameters with <a href="#Specific_types">specific types</a>, <code>x</code>
|
||||
parameters, <code>x</code>
|
||||
can also be converted to type <code>T</code> if one of the following conditions applies:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Both <code>V</code> and <code>T</code> are type parameters and a value of each
|
||||
specific type of <code>V</code> can be converted to each specific type
|
||||
of <code>T</code>.
|
||||
type in <code>V</code>'s type set can be converted to each type in <code>T</code>'s
|
||||
type set.
|
||||
</li>
|
||||
<li>
|
||||
Only <code>V</code> is a type parameter and a value of each
|
||||
specific type of <code>V</code> can be converted to <code>T</code>.
|
||||
type in <code>V</code>'s type set can be converted to <code>T</code>.
|
||||
</li>
|
||||
<li>
|
||||
Only <code>T</code> is a type parameter and <code>x</code> can be converted to each
|
||||
specific type of <code>T</code>.
|
||||
type in <code>T</code>'s type set.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -6270,7 +6190,7 @@ switch x.(type) {
|
||||
Cases then match actual types <code>T</code> against the dynamic type of the
|
||||
expression <code>x</code>. As with type assertions, <code>x</code> must be of
|
||||
<a href="#Interface_types">interface type</a>, but not a
|
||||
<a href="#Type_parameter_lists">type parameter</a>, and each non-interface type
|
||||
<a href="#Type_parameter_declarations">type parameter</a>, and each non-interface type
|
||||
<code>T</code> listed in a case must implement the type of <code>x</code>.
|
||||
The types listed in the cases of a type switch must all be
|
||||
<a href="#Type_identity">different</a>.
|
||||
@@ -6352,7 +6272,7 @@ if v == nil {
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A <a href="#Type_parameter_lists">type parameter</a> or a <a href="#Type_declarations">generic type</a>
|
||||
A <a href="#Type_parameter_declarations">type parameter</a> or a <a href="#Type_declarations">generic type</a>
|
||||
may be used as a type in a case. If upon <a href="#Instantiations">instantiation</a> that type turns
|
||||
out to duplicate another entry in the switch, the first matching case is chosen.
|
||||
</p>
|
||||
@@ -7093,10 +7013,9 @@ cap(s) [n]T, *[n]T array length (== n)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If the argument type is a <a href="#Type_parameter_lists">type parameter</a> <code>P</code>,
|
||||
<code>P</code> must have <a href="#Specific_types">specific types</a>, and
|
||||
If the argument type is a <a href="#Type_parameter_declarations">type parameter</a> <code>P</code>,
|
||||
the call <code>len(e)</code> (or <code>cap(e)</code> respectively) must be valid for
|
||||
each specific type of <code>P</code>.
|
||||
each type in <code>P</code>'s type set.
|
||||
The result is the length (or capacity, respectively) of the argument whose type
|
||||
corresponds to the type argument with which <code>P</code> was
|
||||
<a href="#Instantiations">instantiated</a>.
|
||||
@@ -7197,8 +7116,9 @@ make(T, n) channel buffered channel of type T, buffer size n
|
||||
|
||||
|
||||
<p>
|
||||
Each of the size arguments <code>n</code> and <code>m</code> must be of <a href="#Numeric_types">integer type</a>
|
||||
or an untyped <a href="#Constants">constant</a>.
|
||||
Each of the size arguments <code>n</code> and <code>m</code> must be of <a href="#Numeric_types">integer type</a>,
|
||||
have a <a href="#Interface_types">type set</a> containing only integer types,
|
||||
or be an untyped <a href="#Constants">constant</a>.
|
||||
A constant size 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 both <code>n</code> and <code>m</code> are provided and are constant, then
|
||||
@@ -7235,9 +7155,9 @@ by the arguments overlaps.
|
||||
<p>
|
||||
The <a href="#Function_types">variadic</a> function <code>append</code>
|
||||
appends zero or more values <code>x</code> to a slice <code>s</code>
|
||||
and returns the resulting slice.
|
||||
and returns the resulting slice of the same type as <code>s</code>.
|
||||
The <a href="#Core_types">core type</a> of <code>s</code> must be a slice
|
||||
of the form <code>[]E</code>.
|
||||
of type <code>[]E</code>.
|
||||
The values <code>x</code> are passed to a parameter of type <code>...E</code>
|
||||
and the respective <a href="#Passing_arguments_to_..._parameters">parameter
|
||||
passing rules</a> apply.
|
||||
@@ -7247,7 +7167,7 @@ followed by <code>...</code>. This form appends the bytes of the string.
|
||||
</p>
|
||||
|
||||
<pre class="grammar">
|
||||
append(s S, x ...E) S // E is the element type of the core type of S
|
||||
append(s S, x ...E) S // core type of S is []E
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
@@ -7317,9 +7237,8 @@ delete(m, k) // remove element m[k] from map m
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If the type of <code>m</code> is a <a href="#Type_parameter_lists">type parameter</a>,
|
||||
it must have <a href="#Specific_types">specific types</a>, all specific types
|
||||
must be maps, and they must all have identical key types.
|
||||
If the type of <code>m</code> is a <a href="#Type_parameter_declarations">type parameter</a>,
|
||||
all types in that type set must be maps, and they must all have identical key types.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -7330,10 +7249,6 @@ does not exist, <code>delete</code> is a no-op.
|
||||
|
||||
<h3 id="Complex_numbers">Manipulating complex numbers</h3>
|
||||
|
||||
<p><b>
|
||||
[We don't support generic arguments for these built-ins for Go 1.18.]
|
||||
</b></p>
|
||||
|
||||
<p>
|
||||
Three functions assemble and disassemble complex numbers.
|
||||
The built-in function <code>complex</code> constructs a complex
|
||||
@@ -7396,6 +7311,10 @@ const c = imag(b) // untyped constant -1.4
|
||||
_ = imag(3 << s) // illegal: 3 assumes complex type, cannot shift
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Arguments of type parameter type are not permitted.
|
||||
</p>
|
||||
|
||||
<h3 id="Handling_panics">Handling panics</h3>
|
||||
|
||||
<p> Two built-in functions, <code>panic</code> and <code>recover</code>,
|
||||
@@ -8004,11 +7923,17 @@ func Add(ptr Pointer, len IntegerType) Pointer
|
||||
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
|
||||
</pre>
|
||||
|
||||
<!--
|
||||
These conversions also apply to type parameters with suitable core types.
|
||||
Determine if we can simply use core type insted of underlying type here,
|
||||
of if the general conversion rules take care of this.
|
||||
-->
|
||||
|
||||
<p>
|
||||
A <code>Pointer</code> is a <a href="#Pointer_types">pointer type</a> but a <code>Pointer</code>
|
||||
value may not be <a href="#Address_operators">dereferenced</a>.
|
||||
Any pointer or value of <a href="#Types">underlying type</a> <code>uintptr</code> can be converted to
|
||||
a type of underlying type <code>Pointer</code> and vice versa.
|
||||
Any pointer or value of <a href="#Types">underlying type</a> <code>uintptr</code> can be
|
||||
<a href="#Conversions">converted</a> to a type of underlying type <code>Pointer</code> and vice versa.
|
||||
The effect of converting between <code>Pointer</code> and <code>uintptr</code> is implementation-defined.
|
||||
</p>
|
||||
|
||||
@@ -8055,7 +7980,8 @@ uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
|
||||
|
||||
<p>
|
||||
A (variable of) type <code>T</code> has <i>variable size</i> if <code>T</code>
|
||||
is a type parameter, or if it is an array or struct type containing elements
|
||||
is a <a href="#Type_parameter_declarations">type parameter</a>, or if it is an
|
||||
array or struct type containing elements
|
||||
or fields of variable size. Otherwise the size is <i>constant</i>.
|
||||
Calls to <code>Alignof</code>, <code>Offsetof</code>, and <code>Sizeof</code>
|
||||
are compile-time <a href="#Constant_expressions">constant expressions</a> of
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -518,38 +519,13 @@ func TestEarlySignalHandler(t *testing.T) {
|
||||
|
||||
func TestSignalForwarding(t *testing.T) {
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], "1")...)
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("%v\n%s", cmd.Args, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
@@ -560,7 +536,7 @@ func TestSignalForwarding(t *testing.T) {
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,32 +547,7 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
|
||||
}
|
||||
checkSignalForwardingTest(t)
|
||||
|
||||
if !testWork {
|
||||
defer func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
buildSignalForwardingTest(t)
|
||||
|
||||
// We want to send the process a signal and see if it dies.
|
||||
// Normally the signal goes to the C thread, the Go signal
|
||||
@@ -609,42 +560,27 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
// fail.
|
||||
const tries = 20
|
||||
for i := 0; i < tries; i++ {
|
||||
cmd = exec.Command(bin[0], append(bin[1:], "2")...)
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that the process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatalf("Did not receive OK signal")
|
||||
}
|
||||
|
||||
// Give the program a chance to enter the sleep function.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
err := runSignalForwardingTest(t, "2")
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if expectSignal(t, err, syscall.SIGSEGV) {
|
||||
// If the signal is delivered to a C thread, as expected,
|
||||
// the Go signal handler will disable itself and re-raise
|
||||
// the signal, causing the program to die with SIGSEGV.
|
||||
//
|
||||
// It is also possible that the signal will be
|
||||
// delivered to a Go thread, such as a GC thread.
|
||||
// Currently when the Go runtime sees that a SIGSEGV was
|
||||
// sent from a different program, it first tries to send
|
||||
// the signal to the os/signal API. If nothing is looking
|
||||
// for (or explicitly ignoring) SIGSEGV, then it crashes.
|
||||
// Because the Go runtime is invoked via a c-archive,
|
||||
// it treats this as GOTRACEBACK=crash, meaning that it
|
||||
// dumps a stack trace for all goroutines, which it does
|
||||
// by raising SIGQUIT. The effect is that we will see the
|
||||
// program die with SIGQUIT in that case, not SIGSEGV.
|
||||
if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -652,6 +588,23 @@ func TestSignalForwardingExternal(t *testing.T) {
|
||||
t.Errorf("program succeeded unexpectedly %d times", tries)
|
||||
}
|
||||
|
||||
func TestSignalForwardingGo(t *testing.T) {
|
||||
// This test fails on darwin-amd64 because of the special
|
||||
// handling of user-generated SIGSEGV signals in fixsigcode in
|
||||
// runtime/signal_darwin_amd64.go.
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
|
||||
t.Skip("not supported on darwin-amd64")
|
||||
}
|
||||
|
||||
checkSignalForwardingTest(t)
|
||||
buildSignalForwardingTest(t)
|
||||
err := runSignalForwardingTest(t, "4")
|
||||
|
||||
// Occasionally the signal will be delivered to a C thread,
|
||||
// and the program will crash with SIGSEGV.
|
||||
expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
|
||||
}
|
||||
|
||||
// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
|
||||
// doesn't work on this platform.
|
||||
func checkSignalForwardingTest(t *testing.T) {
|
||||
@@ -666,18 +619,121 @@ func checkSignalForwardingTest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// buildSignalForwardingTest builds the executable used by the various
|
||||
// signal forwarding tests.
|
||||
func buildSignalForwardingTest(t *testing.T) {
|
||||
if !testWork {
|
||||
t.Cleanup(func() {
|
||||
os.Remove("libgo2.a")
|
||||
os.Remove("libgo2.h")
|
||||
os.Remove("testp" + exeSuffix)
|
||||
os.RemoveAll(filepath.Join(GOPATH, "pkg"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
|
||||
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkLineComments(t, "libgo2.h")
|
||||
checkArchive(t, "libgo2.a")
|
||||
|
||||
ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
|
||||
if runtime.Compiler == "gccgo" {
|
||||
ccArgs = append(ccArgs, "-lgo")
|
||||
}
|
||||
t.Log(ccArgs)
|
||||
out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runSignalForwardingTest(t *testing.T, arg string) error {
|
||||
t.Logf("%v %s", bin, arg)
|
||||
cmd := exec.Command(bin[0], append(bin[1:], arg)...)
|
||||
|
||||
var out strings.Builder
|
||||
cmd.Stdout = &out
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer stderr.Close()
|
||||
|
||||
r := bufio.NewReader(stderr)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for trigger to ensure that process is started.
|
||||
ok, err := r.ReadString('\n')
|
||||
|
||||
// Verify trigger.
|
||||
if err != nil || ok != "OK\n" {
|
||||
t.Fatal("Did not receive OK signal")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var errsb strings.Builder
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(&errsb, r)
|
||||
}()
|
||||
|
||||
// Give the program a chance to enter the function.
|
||||
// If the program doesn't get there the test will still
|
||||
// pass, although it doesn't quite test what we intended.
|
||||
// This is fine as long as the program normally makes it.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
cmd.Process.Signal(syscall.SIGSEGV)
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
s := out.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
wg.Wait()
|
||||
s = errsb.String()
|
||||
if len(s) > 0 {
|
||||
t.Log(s)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// expectSignal checks that err, the exit status of a test program,
|
||||
// shows a failure due to a specific signal. Returns whether we found
|
||||
// the expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
|
||||
// shows a failure due to a specific signal or two. Returns whether we
|
||||
// found an expected signal.
|
||||
func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Error("test program succeeded unexpectedly")
|
||||
} else if ee, ok := err.(*exec.ExitError); !ok {
|
||||
t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
|
||||
} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
|
||||
t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
|
||||
} else if !ws.Signaled() || ws.Signal() != sig {
|
||||
t.Errorf("got %v; expected signal %v", ee, sig)
|
||||
} else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
|
||||
if sig2 == 0 {
|
||||
t.Errorf("got %q; expected signal %q", ee, sig1)
|
||||
} else {
|
||||
t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
@@ -1013,14 +1069,14 @@ func TestCompileWithoutShared(t *testing.T) {
|
||||
binArgs := append(cmdToRun(exe), "1")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGSEGV)
|
||||
expectSignal(t, err, syscall.SIGSEGV, 0)
|
||||
|
||||
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
|
||||
if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
|
||||
binArgs := append(cmdToRun(exe), "3")
|
||||
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
|
||||
t.Logf("%v\n%s", binArgs, out)
|
||||
expectSignal(t, err, syscall.SIGPIPE)
|
||||
expectSignal(t, err, syscall.SIGPIPE, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@ func RunGoroutines() {
|
||||
}
|
||||
}
|
||||
|
||||
// Block blocks the current thread while running Go code.
|
||||
//export Block
|
||||
func Block() {
|
||||
select {}
|
||||
}
|
||||
|
||||
var P *byte
|
||||
|
||||
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
|
||||
|
||||
13
misc/cgo/testcarchive/testdata/main5.c
vendored
13
misc/cgo/testcarchive/testdata/main5.c
vendored
@@ -29,10 +29,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
verbose = (argc > 2);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling RunGoroutines\n");
|
||||
}
|
||||
|
||||
Noop();
|
||||
|
||||
switch (test) {
|
||||
@@ -90,6 +86,15 @@ int main(int argc, char** argv) {
|
||||
printf("did not receive SIGPIPE\n");
|
||||
return 0;
|
||||
}
|
||||
case 4: {
|
||||
fprintf(stderr, "OK\n");
|
||||
fflush(stderr);
|
||||
|
||||
if (verbose) {
|
||||
printf("calling Block\n");
|
||||
}
|
||||
Block();
|
||||
}
|
||||
default:
|
||||
printf("Unknown test: %d\n", test);
|
||||
return 0;
|
||||
|
||||
@@ -283,6 +283,12 @@ func TestMethod2(t *testing.T) {
|
||||
run(t, "./method2.exe")
|
||||
}
|
||||
|
||||
func TestMethod3(t *testing.T) {
|
||||
goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
|
||||
goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
|
||||
run(t, "./method3.exe")
|
||||
}
|
||||
|
||||
func TestIssue44956(t *testing.T) {
|
||||
goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
|
||||
goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
|
||||
|
||||
32
misc/cgo/testplugin/testdata/method3/main.go
vendored
Normal file
32
misc/cgo/testplugin/testdata/method3/main.go
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
// An unexported method can be reachable from the plugin via interface
|
||||
// when a package is shared. So it need to be live.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"plugin"
|
||||
|
||||
"testplugin/method3/p"
|
||||
)
|
||||
|
||||
var i p.I
|
||||
|
||||
func main() {
|
||||
pl, err := plugin.Open("method3.so")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
f, err := pl.Lookup("F")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
f.(func())()
|
||||
|
||||
i = p.T(123)
|
||||
}
|
||||
17
misc/cgo/testplugin/testdata/method3/p/p.go
vendored
Normal file
17
misc/cgo/testplugin/testdata/method3/p/p.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
type T int
|
||||
|
||||
func (T) m() { println("m") }
|
||||
|
||||
type I interface { m() }
|
||||
|
||||
func F() {
|
||||
i.m()
|
||||
}
|
||||
|
||||
var i I = T(123)
|
||||
11
misc/cgo/testplugin/testdata/method3/plugin.go
vendored
Normal file
11
misc/cgo/testplugin/testdata/method3/plugin.go
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import "testplugin/method3/p"
|
||||
|
||||
func main() {}
|
||||
|
||||
func F() { p.F() }
|
||||
2
misc/cgo/testsanitizers/testdata/tsan11.go
vendored
2
misc/cgo/testsanitizers/testdata/tsan11.go
vendored
@@ -45,7 +45,7 @@ static void register_handler(int signo) {
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
ch := make(chan os.Signal)
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR2)
|
||||
|
||||
C.register_handler(C.int(syscall.SIGUSR1))
|
||||
|
||||
2
misc/cgo/testsanitizers/testdata/tsan12.go
vendored
2
misc/cgo/testsanitizers/testdata/tsan12.go
vendored
@@ -22,7 +22,7 @@ import (
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
ch := make(chan os.Signal)
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR1)
|
||||
|
||||
if err := exec.Command("true").Run(); err != nil {
|
||||
|
||||
@@ -143,6 +143,10 @@ const (
|
||||
blockSize = 512 // Size of each block in a tar stream
|
||||
nameSize = 100 // Max length of the name field in USTAR format
|
||||
prefixSize = 155 // Max length of the prefix field in USTAR format
|
||||
|
||||
// Max length of a special file (PAX header, GNU long name or link).
|
||||
// This matches the limit used by libarchive.
|
||||
maxSpecialFileSize = 1 << 20
|
||||
)
|
||||
|
||||
// blockPadding computes the number of bytes needed to pad offset up to the
|
||||
|
||||
@@ -103,7 +103,7 @@ func (tr *Reader) next() (*Header, error) {
|
||||
continue // This is a meta header affecting the next header
|
||||
case TypeGNULongName, TypeGNULongLink:
|
||||
format.mayOnlyBe(FormatGNU)
|
||||
realname, err := io.ReadAll(tr)
|
||||
realname, err := readSpecialFile(tr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -293,7 +293,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
|
||||
// parsePAX parses PAX headers.
|
||||
// If an extended header (type 'x') is invalid, ErrHeader is returned
|
||||
func parsePAX(r io.Reader) (map[string]string, error) {
|
||||
buf, err := io.ReadAll(r)
|
||||
buf, err := readSpecialFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -828,6 +828,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// readSpecialFile is like io.ReadAll except it returns
|
||||
// ErrFieldTooLong if more than maxSpecialFileSize is read.
|
||||
func readSpecialFile(r io.Reader) ([]byte, error) {
|
||||
buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1))
|
||||
if len(buf) > maxSpecialFileSize {
|
||||
return nil, ErrFieldTooLong
|
||||
}
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// discard skips n bytes in r, reporting an error if unable to do so.
|
||||
func discard(r io.Reader, n int64) error {
|
||||
// If possible, Seek to the last byte before the end of the data section.
|
||||
|
||||
@@ -6,6 +6,7 @@ package tar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -243,6 +244,9 @@ func TestReader(t *testing.T) {
|
||||
}, {
|
||||
file: "testdata/pax-bad-hdr-file.tar",
|
||||
err: ErrHeader,
|
||||
}, {
|
||||
file: "testdata/pax-bad-hdr-large.tar.bz2",
|
||||
err: ErrFieldTooLong,
|
||||
}, {
|
||||
file: "testdata/pax-bad-mtime-file.tar",
|
||||
err: ErrHeader,
|
||||
@@ -625,9 +629,14 @@ func TestReader(t *testing.T) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var fr io.Reader = f
|
||||
if strings.HasSuffix(v.file, ".bz2") {
|
||||
fr = bzip2.NewReader(fr)
|
||||
}
|
||||
|
||||
// Capture all headers and checksums.
|
||||
var (
|
||||
tr = NewReader(f)
|
||||
tr = NewReader(fr)
|
||||
hdrs []*Header
|
||||
chksums []string
|
||||
rdbuf = make([]byte, 8)
|
||||
|
||||
BIN
src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2
vendored
Normal file
BIN
src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2
vendored
Normal file
Binary file not shown.
@@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
|
||||
flag = TypeXHeader
|
||||
}
|
||||
data := buf.String()
|
||||
if len(data) > maxSpecialFileSize {
|
||||
return ErrFieldTooLong
|
||||
}
|
||||
if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
|
||||
return err // Global headers return here
|
||||
}
|
||||
|
||||
@@ -1004,6 +1004,33 @@ func TestIssue12594(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLongHeader(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
h *Header
|
||||
}{{
|
||||
name: "name too long",
|
||||
h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "linkname too long",
|
||||
h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "uname too long",
|
||||
h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "gname too long",
|
||||
h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)},
|
||||
}, {
|
||||
name: "PAX header too long",
|
||||
h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}},
|
||||
}} {
|
||||
w := NewWriter(io.Discard)
|
||||
if err := w.WriteHeader(test.h); err != ErrFieldTooLong {
|
||||
t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testNonEmptyWriter wraps an io.Writer and ensures that
|
||||
// Write is never called with an empty buffer.
|
||||
type testNonEmptyWriter struct{ io.Writer }
|
||||
|
||||
@@ -909,7 +909,11 @@ func containsRune(s string, r rune) bool {
|
||||
// Trim returns a subslice of s by slicing off all leading and
|
||||
// trailing UTF-8-encoded code points contained in cutset.
|
||||
func Trim(s []byte, cutset string) []byte {
|
||||
if len(s) == 0 || cutset == "" {
|
||||
if len(s) == 0 {
|
||||
// This is what we've historically done.
|
||||
return nil
|
||||
}
|
||||
if cutset == "" {
|
||||
return s
|
||||
}
|
||||
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
|
||||
@@ -924,7 +928,11 @@ func Trim(s []byte, cutset string) []byte {
|
||||
// TrimLeft returns a subslice of s by slicing off all leading
|
||||
// UTF-8-encoded code points contained in cutset.
|
||||
func TrimLeft(s []byte, cutset string) []byte {
|
||||
if len(s) == 0 || cutset == "" {
|
||||
if len(s) == 0 {
|
||||
// This is what we've historically done.
|
||||
return nil
|
||||
}
|
||||
if cutset == "" {
|
||||
return s
|
||||
}
|
||||
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
|
||||
@@ -940,6 +948,10 @@ func trimLeftByte(s []byte, c byte) []byte {
|
||||
for len(s) > 0 && s[0] == c {
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
// This is what we've historically done.
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -950,6 +962,10 @@ func trimLeftASCII(s []byte, as *asciiSet) []byte {
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
// This is what we've historically done.
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -964,6 +980,10 @@ func trimLeftUnicode(s []byte, cutset string) []byte {
|
||||
}
|
||||
s = s[n:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
// This is what we've historically done.
|
||||
return nil
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -1278,24 +1278,69 @@ var trimTests = []TrimTest{
|
||||
{"TrimSuffix", "aabb", "b", "aab"},
|
||||
}
|
||||
|
||||
type TrimNilTest struct {
|
||||
f string
|
||||
in []byte
|
||||
arg string
|
||||
out []byte
|
||||
}
|
||||
|
||||
var trimNilTests = []TrimNilTest{
|
||||
{"Trim", nil, "", nil},
|
||||
{"Trim", []byte{}, "", nil},
|
||||
{"Trim", []byte{'a'}, "a", nil},
|
||||
{"Trim", []byte{'a', 'a'}, "a", nil},
|
||||
{"Trim", []byte{'a'}, "ab", nil},
|
||||
{"Trim", []byte{'a', 'b'}, "ab", nil},
|
||||
{"Trim", []byte("☺"), "☺", nil},
|
||||
{"TrimLeft", nil, "", nil},
|
||||
{"TrimLeft", []byte{}, "", nil},
|
||||
{"TrimLeft", []byte{'a'}, "a", nil},
|
||||
{"TrimLeft", []byte{'a', 'a'}, "a", nil},
|
||||
{"TrimLeft", []byte{'a'}, "ab", nil},
|
||||
{"TrimLeft", []byte{'a', 'b'}, "ab", nil},
|
||||
{"TrimLeft", []byte("☺"), "☺", nil},
|
||||
{"TrimRight", nil, "", nil},
|
||||
{"TrimRight", []byte{}, "", []byte{}},
|
||||
{"TrimRight", []byte{'a'}, "a", []byte{}},
|
||||
{"TrimRight", []byte{'a', 'a'}, "a", []byte{}},
|
||||
{"TrimRight", []byte{'a'}, "ab", []byte{}},
|
||||
{"TrimRight", []byte{'a', 'b'}, "ab", []byte{}},
|
||||
{"TrimRight", []byte("☺"), "☺", []byte{}},
|
||||
{"TrimPrefix", nil, "", nil},
|
||||
{"TrimPrefix", []byte{}, "", []byte{}},
|
||||
{"TrimPrefix", []byte{'a'}, "a", []byte{}},
|
||||
{"TrimPrefix", []byte("☺"), "☺", []byte{}},
|
||||
{"TrimSuffix", nil, "", nil},
|
||||
{"TrimSuffix", []byte{}, "", []byte{}},
|
||||
{"TrimSuffix", []byte{'a'}, "a", []byte{}},
|
||||
{"TrimSuffix", []byte("☺"), "☺", []byte{}},
|
||||
}
|
||||
|
||||
func TestTrim(t *testing.T) {
|
||||
for _, tc := range trimTests {
|
||||
name := tc.f
|
||||
var f func([]byte, string) []byte
|
||||
var fb func([]byte, []byte) []byte
|
||||
toFn := func(name string) (func([]byte, string) []byte, func([]byte, []byte) []byte) {
|
||||
switch name {
|
||||
case "Trim":
|
||||
f = Trim
|
||||
return Trim, nil
|
||||
case "TrimLeft":
|
||||
f = TrimLeft
|
||||
return TrimLeft, nil
|
||||
case "TrimRight":
|
||||
f = TrimRight
|
||||
return TrimRight, nil
|
||||
case "TrimPrefix":
|
||||
fb = TrimPrefix
|
||||
return nil, TrimPrefix
|
||||
case "TrimSuffix":
|
||||
fb = TrimSuffix
|
||||
return nil, TrimSuffix
|
||||
default:
|
||||
t.Errorf("Undefined trim function %s", name)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range trimTests {
|
||||
name := tc.f
|
||||
f, fb := toFn(name)
|
||||
if f == nil && fb == nil {
|
||||
continue
|
||||
}
|
||||
var actual string
|
||||
if f != nil {
|
||||
@@ -1307,6 +1352,36 @@ func TestTrim(t *testing.T) {
|
||||
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range trimNilTests {
|
||||
name := tc.f
|
||||
f, fb := toFn(name)
|
||||
if f == nil && fb == nil {
|
||||
continue
|
||||
}
|
||||
var actual []byte
|
||||
if f != nil {
|
||||
actual = f(tc.in, tc.arg)
|
||||
} else {
|
||||
actual = fb(tc.in, []byte(tc.arg))
|
||||
}
|
||||
report := func(s []byte) string {
|
||||
if s == nil {
|
||||
return "nil"
|
||||
} else {
|
||||
return fmt.Sprintf("%q", s)
|
||||
}
|
||||
}
|
||||
if len(actual) != 0 {
|
||||
t.Errorf("%s(%s, %q) returned non-empty value", name, report(tc.in), tc.arg)
|
||||
} else {
|
||||
actualNil := actual == nil
|
||||
outNil := tc.out == nil
|
||||
if actualNil != outNil {
|
||||
t.Errorf("%s(%s, %q) got nil %t; want nil %t", name, report(tc.in), tc.arg, actualNil, outNil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type predicate struct {
|
||||
|
||||
@@ -78,6 +78,8 @@ func storeByType(t *types.Type) obj.As {
|
||||
return x86.AMOVL
|
||||
case 8:
|
||||
return x86.AMOVQ
|
||||
case 16:
|
||||
return x86.AMOVUPS
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("bad store type %v", t))
|
||||
|
||||
@@ -163,4 +163,5 @@ func markHiddenClosureDead(n ir.Node) {
|
||||
if clo.Func.IsHiddenClosure() {
|
||||
clo.Func.SetIsDeadcodeClosure(true)
|
||||
}
|
||||
ir.VisitList(clo.Func.Body, markHiddenClosureDead)
|
||||
}
|
||||
|
||||
@@ -91,6 +91,11 @@ func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope,
|
||||
continue
|
||||
}
|
||||
apdecls = append(apdecls, n)
|
||||
if n.Type().Kind() == types.TSSA {
|
||||
// Can happen for TypeInt128 types. This only happens for
|
||||
// spill locations, so not a huge deal.
|
||||
continue
|
||||
}
|
||||
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const (
|
||||
)
|
||||
|
||||
type ident struct {
|
||||
pkg string
|
||||
pkg *types2.Package
|
||||
name string
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ func (r *importReader) obj(name string) {
|
||||
t := types2.NewTypeParam(tn, nil)
|
||||
// To handle recursive references to the typeparam within its
|
||||
// bound, save the partial type in tparamIndex before reading the bounds.
|
||||
id := ident{r.currPkg.Name(), name}
|
||||
id := ident{r.currPkg, name}
|
||||
r.p.tparamIndex[id] = t
|
||||
|
||||
var implicit bool
|
||||
@@ -687,7 +687,7 @@ func (r *importReader) doType(base *types2.Named) types2.Type {
|
||||
errorf("unexpected type param type")
|
||||
}
|
||||
pkg, name := r.qualifiedIdent()
|
||||
id := ident{pkg.Name(), name}
|
||||
id := ident{pkg, name}
|
||||
if t, ok := r.p.tparamIndex[id]; ok {
|
||||
// We're already in the process of importing this typeparam.
|
||||
return t
|
||||
|
||||
@@ -771,18 +771,18 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
|
||||
// CalleeEffects appends any side effects from evaluating callee to init.
|
||||
func CalleeEffects(init *ir.Nodes, callee ir.Node) {
|
||||
for {
|
||||
init.Append(ir.TakeInit(callee)...)
|
||||
|
||||
switch callee.Op() {
|
||||
case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
|
||||
return // done
|
||||
|
||||
case ir.OCONVNOP:
|
||||
conv := callee.(*ir.ConvExpr)
|
||||
init.Append(ir.TakeInit(conv)...)
|
||||
callee = conv.X
|
||||
|
||||
case ir.OINLCALL:
|
||||
ic := callee.(*ir.InlinedCallExpr)
|
||||
init.Append(ir.TakeInit(ic)...)
|
||||
init.Append(ic.Body.Take()...)
|
||||
callee = ic.SingleResult()
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
|
||||
return n
|
||||
}
|
||||
|
||||
// A SwitchStmt is a switch statement: switch Init; Expr { Cases }.
|
||||
// A SwitchStmt is a switch statement: switch Init; Tag { Cases }.
|
||||
type SwitchStmt struct {
|
||||
miniStmt
|
||||
Tag Node
|
||||
|
||||
@@ -26,6 +26,7 @@ var Syms struct {
|
||||
GCWriteBarrier *obj.LSym
|
||||
Goschedguarded *obj.LSym
|
||||
Growslice *obj.LSym
|
||||
Memmove *obj.LSym
|
||||
Msanread *obj.LSym
|
||||
Msanwrite *obj.LSym
|
||||
Msanmove *obj.LSym
|
||||
|
||||
@@ -114,11 +114,11 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
|
||||
// the Fields to represent the receiver's method set.
|
||||
if recv := fn.Type().Recv(); recv != nil {
|
||||
typ := types.ReceiverBaseType(recv.Type)
|
||||
if typ.OrigSym() != nil {
|
||||
if orig := typ.OrigType(); orig != nil {
|
||||
// For a generic method, we mark the methods on the
|
||||
// base generic type, since those are the methods
|
||||
// that will be stenciled.
|
||||
typ = typ.OrigSym().Def.Type()
|
||||
typ = orig
|
||||
}
|
||||
meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0)
|
||||
meth.SetNointerface(true)
|
||||
|
||||
@@ -193,8 +193,7 @@ func (g *genInst) scanForGenCalls(decl ir.Node) {
|
||||
targs := deref(meth.Type().Recv().Type).RParams()
|
||||
|
||||
t := meth.X.Type()
|
||||
baseSym := deref(t).OrigSym()
|
||||
baseType := baseSym.Def.(*ir.Name).Type()
|
||||
baseType := deref(t).OrigType()
|
||||
var gf *ir.Name
|
||||
for _, m := range baseType.Methods().Slice() {
|
||||
if meth.Sel == m.Sym {
|
||||
@@ -348,7 +347,7 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
// actually generic, so no need to build a closure.
|
||||
return x
|
||||
}
|
||||
baseType := recv.OrigSym().Def.Type()
|
||||
baseType := recv.OrigType()
|
||||
var gf *ir.Name
|
||||
for _, m := range baseType.Methods().Slice() {
|
||||
if se.Sel == m.Sym {
|
||||
@@ -418,6 +417,7 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
var dictAssign *ir.AssignStmt
|
||||
if outer != nil {
|
||||
dictVar = ir.NewNameAt(pos, typecheck.LookupNum(typecheck.LocalDictName, g.dnum))
|
||||
dictVar.SetSym(outer.Sym().Pkg.Lookup(dictVar.Sym().Name))
|
||||
g.dnum++
|
||||
dictVar.Class = ir.PAUTO
|
||||
typed(types.Types[types.TUINTPTR], dictVar)
|
||||
@@ -432,6 +432,9 @@ func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
||||
var rcvrAssign ir.Node
|
||||
if rcvrValue != nil {
|
||||
rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
|
||||
if outer != nil {
|
||||
rcvrVar.SetSym(outer.Sym().Pkg.Lookup(rcvrVar.Sym().Name))
|
||||
}
|
||||
g.dnum++
|
||||
typed(rcvrValue.Type(), rcvrVar)
|
||||
rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
|
||||
@@ -543,8 +546,7 @@ func (g *genInst) instantiateMethods() {
|
||||
typecheck.NeedRuntimeType(typ)
|
||||
// Lookup the method on the base generic type, since methods may
|
||||
// not be set on imported instantiated types.
|
||||
baseSym := typ.OrigSym()
|
||||
baseType := baseSym.Def.(*ir.Name).Type()
|
||||
baseType := typ.OrigType()
|
||||
for j, _ := range typ.Methods().Slice() {
|
||||
if baseType.Methods().Slice()[j].Nointerface() {
|
||||
typ.Methods().Slice()[j].SetNointerface(true)
|
||||
@@ -644,7 +646,7 @@ func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMe
|
||||
if recvType.IsFullyInstantiated() {
|
||||
// Get the type of the base generic type, so we get
|
||||
// its original typeparams.
|
||||
recvType = recvType.OrigSym().Def.(*ir.Name).Type()
|
||||
recvType = recvType.OrigType()
|
||||
}
|
||||
tparams = recvType.RParams()
|
||||
} else {
|
||||
@@ -898,7 +900,7 @@ func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int)
|
||||
base.Fatalf(fmt.Sprintf("bad dict index %d", i))
|
||||
}
|
||||
|
||||
r := getDictionaryEntry(pos, info.dictParam, i, info.dictInfo.startSubDict)
|
||||
r := getDictionaryEntry(pos, dictParam, i, info.dictInfo.startSubDict)
|
||||
// change type of retrieved dictionary entry to *byte, which is the
|
||||
// standard typing of a *runtime._type in the compiler
|
||||
typed(types.Types[types.TUINT8].PtrTo(), r)
|
||||
@@ -1179,6 +1181,26 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||
subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func)
|
||||
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
|
||||
|
||||
case ir.OSWITCH:
|
||||
m := m.(*ir.SwitchStmt)
|
||||
if m.Tag != nil && m.Tag.Op() == ir.OTYPESW {
|
||||
break // Nothing to do here for type switches.
|
||||
}
|
||||
if m.Tag != nil && !m.Tag.Type().IsEmptyInterface() && m.Tag.Type().HasShape() {
|
||||
// To implement a switch on a value that is or has a type parameter, we first convert
|
||||
// that thing we're switching on to an interface{}.
|
||||
m.Tag = assignconvfn(m.Tag, types.Types[types.TINTER])
|
||||
}
|
||||
for _, c := range m.Cases {
|
||||
for i, x := range c.List {
|
||||
// If we have a case that is or has a type parameter, convert that case
|
||||
// to an interface{}.
|
||||
if !x.Type().IsEmptyInterface() && x.Type().HasShape() {
|
||||
c.List[i] = assignconvfn(x, types.Types[types.TINTER])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -1270,8 +1292,9 @@ func (g *genInst) dictPass(info *instInfo) {
|
||||
mce := m.(*ir.ConvExpr)
|
||||
// Note: x's argument is still typed as a type parameter.
|
||||
// m's argument now has an instantiated type.
|
||||
if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
|
||||
m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type(), false)
|
||||
|
||||
if mce.X.Type().HasShape() || m.Type().HasShape() {
|
||||
m = convertUsingDictionary(info, info.dictParam, m.Pos(), mce.X, m, m.Type(), false)
|
||||
}
|
||||
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
||||
if !m.Type().HasShape() {
|
||||
@@ -1367,7 +1390,7 @@ func findDictType(info *instInfo, t *types.Type) int {
|
||||
// If nonEscaping is true, the caller guarantees that the backing store needed for the interface data
|
||||
// word will not escape.
|
||||
func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type, nonEscaping bool) ir.Node {
|
||||
assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
|
||||
assert(v.Type().HasShape() || in.Type().HasShape())
|
||||
assert(dst.IsInterface())
|
||||
|
||||
if v.Type().IsInterface() {
|
||||
@@ -1576,12 +1599,14 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
||||
se := call.X.(*ir.SelectorExpr)
|
||||
if se.X.Type().IsShape() {
|
||||
// This is a method call enabled by a type bound.
|
||||
|
||||
// We need this extra check for method expressions,
|
||||
// which don't add in the implicit XDOTs.
|
||||
tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
|
||||
tmpse = typecheck.AddImplicitDots(tmpse)
|
||||
tparam := tmpse.X.Type()
|
||||
tparam := se.X.Type()
|
||||
if call.X.Op() == ir.ODOTMETH {
|
||||
// We need this extra check for method expressions,
|
||||
// which don't add in the implicit XDOTs.
|
||||
tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
|
||||
tmpse = typecheck.AddImplicitDots(tmpse)
|
||||
tparam = tmpse.X.Type()
|
||||
}
|
||||
if !tparam.IsShape() {
|
||||
// The method expression is not
|
||||
// really on a typeparam.
|
||||
@@ -1608,7 +1633,7 @@ func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool
|
||||
// instantiated type, so we need a
|
||||
// sub-dictionary.
|
||||
targs := recvType.RParams()
|
||||
genRecvType := recvType.OrigSym().Def.Type()
|
||||
genRecvType := recvType.OrigType()
|
||||
nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
|
||||
sym = g.getDictionarySym(nameNode, targs, true)
|
||||
} else {
|
||||
@@ -1687,7 +1712,7 @@ func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsub
|
||||
// also give the receiver type. For method expressions with embedded types, we
|
||||
// need to look at the type of the selection to get the final receiver type.
|
||||
recvType := deref(se.Selection.Type.Recv().Type)
|
||||
genRecvType := recvType.OrigSym().Def.Type()
|
||||
genRecvType := recvType.OrigType()
|
||||
nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
|
||||
subtargs := recvType.RParams()
|
||||
s2targs := make([]*types.Type, len(subtargs))
|
||||
@@ -1746,6 +1771,7 @@ func (g *genInst) finalizeSyms() {
|
||||
g.instantiateMethods()
|
||||
itabLsym := reflectdata.ITabLsym(srctype, dsttype)
|
||||
d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
|
||||
markTypeUsed(srctype, lsym)
|
||||
infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
|
||||
}
|
||||
}
|
||||
@@ -1896,7 +1922,7 @@ func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instI
|
||||
}
|
||||
case ir.OCONVIFACE:
|
||||
if n.Type().IsInterface() && !n.Type().IsEmptyInterface() &&
|
||||
n.(*ir.ConvExpr).X.Type().HasShape() {
|
||||
(n.Type().HasShape() || n.(*ir.ConvExpr).X.Type().HasShape()) {
|
||||
infoPrint(" Itab for interface conv: %v\n", n)
|
||||
info.itabConvs = append(info.itabConvs, n)
|
||||
}
|
||||
@@ -2091,6 +2117,9 @@ func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*t
|
||||
for i := 0; i < typ.NumParams(); i++ {
|
||||
t := typ.Params().Field(i).Type
|
||||
arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i))
|
||||
if outer != nil {
|
||||
arg.SetSym(outer.Sym().Pkg.Lookup(arg.Sym().Name))
|
||||
}
|
||||
arg.Class = ir.PPARAM
|
||||
typed(t, arg)
|
||||
arg.Curfn = fn
|
||||
@@ -2103,6 +2132,9 @@ func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*t
|
||||
for i := 0; i < typ.NumResults(); i++ {
|
||||
t := typ.Results().Field(i).Type
|
||||
result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed?
|
||||
if outer != nil {
|
||||
result.SetSym(outer.Sym().Pkg.Lookup(result.Sym().Name))
|
||||
}
|
||||
result.Class = ir.PPARAMOUT
|
||||
typed(t, result)
|
||||
result.Curfn = fn
|
||||
|
||||
@@ -242,7 +242,7 @@ func transformCompare(n *ir.BinaryExpr) {
|
||||
aop, _ := typecheck.Assignop(rt, lt)
|
||||
if aop != ir.OXXX {
|
||||
types.CalcSize(rt)
|
||||
if rt.HasTParam() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 {
|
||||
if rt.HasShape() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 {
|
||||
r = ir.NewConvExpr(base.Pos, aop, lt, r)
|
||||
r.SetTypecheck(1)
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
|
||||
//fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam())
|
||||
|
||||
// Save the symbol for the base generic type.
|
||||
ntyp.SetOrigSym(g.pkg(typ.Obj().Pkg()).Lookup(typ.Obj().Name()))
|
||||
ntyp.SetOrigType(base.Type())
|
||||
ntyp.SetUnderlying(g.typ1(typ.Underlying()))
|
||||
if typ.NumMethods() != 0 {
|
||||
// Save a delayed call to g.fillinMethods() (once
|
||||
|
||||
@@ -1321,21 +1321,21 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) {
|
||||
// type itab struct {
|
||||
// inter *interfacetype
|
||||
// _type *_type
|
||||
// hash uint32
|
||||
// hash uint32 // copy of _type.hash. Used for type switches.
|
||||
// _ [4]byte
|
||||
// fun [1]uintptr // variable sized
|
||||
// fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
|
||||
// }
|
||||
o := objw.SymPtr(lsym, 0, writeType(iface), 0)
|
||||
o = objw.SymPtr(lsym, o, writeType(typ), 0)
|
||||
o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
|
||||
o += 4 // skip unused field
|
||||
if !completeItab {
|
||||
// If typ doesn't implement iface, make method entries be zero.
|
||||
o = objw.Uintptr(lsym, o, 0)
|
||||
entries = entries[:0]
|
||||
}
|
||||
for _, fn := range entries {
|
||||
if !completeItab {
|
||||
// If typ doesn't implement iface, make method entries be zero.
|
||||
o = objw.Uintptr(lsym, o, 0)
|
||||
} else {
|
||||
o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
|
||||
}
|
||||
o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
|
||||
}
|
||||
// Nothing writes static itabs, so they are read only.
|
||||
objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
|
||||
@@ -1430,7 +1430,7 @@ func WriteBasicTypes() {
|
||||
|
||||
type typeAndStr struct {
|
||||
t *types.Type
|
||||
short string // "short" here means NameString
|
||||
short string // "short" here means TypeSymName
|
||||
regular string
|
||||
}
|
||||
|
||||
@@ -1878,14 +1878,14 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
// the TOC to the appropriate value for that module. But if it returns
|
||||
// directly to the wrapper's caller, nothing will reset it to the correct
|
||||
// value for that function.
|
||||
var call *ir.CallExpr
|
||||
if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
|
||||
call.Args = ir.ParamNames(tfn.Type())
|
||||
call.IsDDD = tfn.Type().IsVariadic()
|
||||
fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
|
||||
} else {
|
||||
fn.SetWrapper(true) // ignore frame for panic+recover matching
|
||||
var call *ir.CallExpr
|
||||
|
||||
if generic && dot.X != nthis {
|
||||
// If there is embedding involved, then we should do the
|
||||
@@ -1921,7 +1921,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
|
||||
// Target method uses shaped names.
|
||||
targs2 := make([]*types.Type, len(targs))
|
||||
origRParams := deref(orig).OrigSym().Def.(*ir.Name).Type().RParams()
|
||||
origRParams := deref(orig).OrigType().RParams()
|
||||
for i, t := range targs {
|
||||
targs2[i] = typecheck.Shapify(t, i, origRParams[i])
|
||||
}
|
||||
@@ -1970,7 +1970,22 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
typecheck.Stmts(fn.Body)
|
||||
|
||||
if AfterGlobalEscapeAnalysis {
|
||||
inline.InlineCalls(fn)
|
||||
// Inlining the method may reveal closures, which require walking all function bodies
|
||||
// to decide whether to capture free variables by value or by ref. So we only do inline
|
||||
// if the method do not contain any closures, otherwise, the escape analysis may make
|
||||
// dead variables resurrected, and causing liveness analysis confused, see issue #53702.
|
||||
var canInline bool
|
||||
switch x := call.X.(type) {
|
||||
case *ir.Name:
|
||||
canInline = len(x.Func.Closures) == 0
|
||||
case *ir.SelectorExpr:
|
||||
if x.Op() == ir.OMETHEXPR {
|
||||
canInline = x.FuncName().Func != nil && len(x.FuncName().Func.Closures) == 0
|
||||
}
|
||||
}
|
||||
if canInline {
|
||||
inline.InlineCalls(fn)
|
||||
}
|
||||
escape.Batch([]*ir.Func{fn}, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -532,7 +532,7 @@
|
||||
(If (GreaterThanF cc) yes no) => (FGT cc yes no)
|
||||
(If (GreaterEqualF cc) yes no) => (FGE cc yes no)
|
||||
|
||||
(If cond yes no) => (NZ cond yes no)
|
||||
(If cond yes no) => (TBNZ [0] cond yes no)
|
||||
|
||||
// atomic intrinsics
|
||||
// Note: these ops do not accept offset.
|
||||
@@ -593,6 +593,21 @@
|
||||
(NZ (GreaterThanF cc) yes no) => (FGT cc yes no)
|
||||
(NZ (GreaterEqualF cc) yes no) => (FGE cc yes no)
|
||||
|
||||
(TBNZ [0] (Equal cc) yes no) => (EQ cc yes no)
|
||||
(TBNZ [0] (NotEqual cc) yes no) => (NE cc yes no)
|
||||
(TBNZ [0] (LessThan cc) yes no) => (LT cc yes no)
|
||||
(TBNZ [0] (LessThanU cc) yes no) => (ULT cc yes no)
|
||||
(TBNZ [0] (LessEqual cc) yes no) => (LE cc yes no)
|
||||
(TBNZ [0] (LessEqualU cc) yes no) => (ULE cc yes no)
|
||||
(TBNZ [0] (GreaterThan cc) yes no) => (GT cc yes no)
|
||||
(TBNZ [0] (GreaterThanU cc) yes no) => (UGT cc yes no)
|
||||
(TBNZ [0] (GreaterEqual cc) yes no) => (GE cc yes no)
|
||||
(TBNZ [0] (GreaterEqualU cc) yes no) => (UGE cc yes no)
|
||||
(TBNZ [0] (LessThanF cc) yes no) => (FLT cc yes no)
|
||||
(TBNZ [0] (LessEqualF cc) yes no) => (FLE cc yes no)
|
||||
(TBNZ [0] (GreaterThanF cc) yes no) => (FGT cc yes no)
|
||||
(TBNZ [0] (GreaterEqualF cc) yes no) => (FGE cc yes no)
|
||||
|
||||
(EQ (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (EQ (TSTWconst [int32(c)] y) yes no)
|
||||
(NE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (NE (TSTWconst [int32(c)] y) yes no)
|
||||
(LT (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LT (TSTWconst [int32(c)] y) yes no)
|
||||
@@ -1563,9 +1578,9 @@
|
||||
(GreaterThanF (InvertFlags x)) => (LessThanF x)
|
||||
(GreaterEqualF (InvertFlags x)) => (LessEqualF x)
|
||||
|
||||
// Boolean-generating instructions always
|
||||
// Boolean-generating instructions (NOTE: NOT all boolean Values) always
|
||||
// zero upper bit of the register; no need to zero-extend
|
||||
(MOVBUreg x) && x.Type.IsBoolean() => (MOVDreg x)
|
||||
(MOVBUreg x:((Equal|NotEqual|LessThan|LessThanU|LessThanF|LessEqual|LessEqualU|LessEqualF|GreaterThan|GreaterThanU|GreaterThanF|GreaterEqual|GreaterEqualU|GreaterEqualF) _)) => (MOVDreg x)
|
||||
|
||||
// absorb flag constants into conditional instructions
|
||||
(CSEL [cc] x _ flag) && ccARM64Eval(cc, flag) > 0 => x
|
||||
|
||||
@@ -445,19 +445,19 @@
|
||||
(If (FGreaterThan cc) yes no) => (FGT cc yes no)
|
||||
(If (FGreaterEqual cc) yes no) => (FGE cc yes no)
|
||||
|
||||
(If cond yes no) => (NE (CMPWconst [0] cond) yes no)
|
||||
(If cond yes no) => (NE (CMPWconst [0] (ANDconst <typ.UInt32> [1] cond)) yes no)
|
||||
|
||||
// Absorb boolean tests into block
|
||||
(NE (CMPWconst [0] (Equal cc)) yes no) => (EQ cc yes no)
|
||||
(NE (CMPWconst [0] (NotEqual cc)) yes no) => (NE cc yes no)
|
||||
(NE (CMPWconst [0] (LessThan cc)) yes no) => (LT cc yes no)
|
||||
(NE (CMPWconst [0] (LessEqual cc)) yes no) => (LE cc yes no)
|
||||
(NE (CMPWconst [0] (GreaterThan cc)) yes no) => (GT cc yes no)
|
||||
(NE (CMPWconst [0] (GreaterEqual cc)) yes no) => (GE cc yes no)
|
||||
(NE (CMPWconst [0] (FLessThan cc)) yes no) => (FLT cc yes no)
|
||||
(NE (CMPWconst [0] (FLessEqual cc)) yes no) => (FLE cc yes no)
|
||||
(NE (CMPWconst [0] (FGreaterThan cc)) yes no) => (FGT cc yes no)
|
||||
(NE (CMPWconst [0] (FGreaterEqual cc)) yes no) => (FGE cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (Equal cc))) yes no) => (EQ cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (NotEqual cc))) yes no) => (NE cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (LessThan cc))) yes no) => (LT cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (LessEqual cc))) yes no) => (LE cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (GreaterThan cc))) yes no) => (GT cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (GreaterEqual cc))) yes no) => (GE cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (FLessThan cc))) yes no) => (FLT cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (FLessEqual cc))) yes no) => (FLE cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (FGreaterThan cc))) yes no) => (FGT cc yes no)
|
||||
(NE (CMPWconst [0] (ANDconst [1] (FGreaterEqual cc))) yes no) => (FGE cc yes no)
|
||||
|
||||
// Elide compares of bit tests // TODO need to make both CC and result of ANDCC available.
|
||||
(EQ (CMPconst [0] (ANDconst [c] x)) yes no) => (EQ (ANDCCconst [c] x) yes no)
|
||||
|
||||
@@ -583,7 +583,7 @@
|
||||
(AtomicOr32 ...) => (LoweredAtomicOr32 ...)
|
||||
|
||||
// Conditional branches
|
||||
(If cond yes no) => (BNEZ cond yes no)
|
||||
(If cond yes no) => (BNEZ (MOVBUreg <typ.UInt64> cond) yes no)
|
||||
|
||||
// Optimizations
|
||||
|
||||
@@ -621,6 +621,10 @@
|
||||
(MOVWstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVWstorezero [off] {sym} ptr mem)
|
||||
(MOVDstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVDstorezero [off] {sym} ptr mem)
|
||||
|
||||
// Boolean ops are already extended.
|
||||
(MOVBUreg x:((SEQZ|SNEZ) _)) => x
|
||||
(MOVBUreg x:((SLT|SLTU) _ _)) => x
|
||||
|
||||
// Avoid sign/zero extension for consts.
|
||||
(MOVBreg (MOVDconst [c])) => (MOVDconst [int64(int8(c))])
|
||||
(MOVHreg (MOVDconst [c])) => (MOVDconst [int64(int16(c))])
|
||||
|
||||
@@ -2082,7 +2082,13 @@
|
||||
|
||||
// Inline small or disjoint runtime.memmove calls with constant length.
|
||||
// See the comment in op Move in genericOps.go for discussion of the type.
|
||||
|
||||
//
|
||||
// Note that we've lost any knowledge of the type and alignment requirements
|
||||
// of the source and destination. We only know the size, and that the type
|
||||
// contains no pointers.
|
||||
// The type of the move is not necessarily v.Args[0].Type().Elem()!
|
||||
// See issue 55122 for details.
|
||||
//
|
||||
// Because expand calls runs after prove, constants useful to this pattern may not appear.
|
||||
// Both versions need to exist; the memory and register variants.
|
||||
//
|
||||
@@ -2090,31 +2096,28 @@
|
||||
(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 TUNSAFEPTR, see issue 30061
|
||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||
&& clobber(s1, s2, s3, call)
|
||||
=> (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// Match post-expansion calls, register version.
|
||||
(SelectN [0] call:(StaticCall {sym} dst src (Const(64|32) [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& 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)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// Match pre-expansion calls.
|
||||
(SelectN [0] call:(StaticLECall {sym} dst src (Const(64|32) [sz]) mem))
|
||||
&& sz >= 0
|
||||
&& call.Uses == 1 // this will exclude all calls with results
|
||||
&& isSameCall(sym, "runtime.memmove")
|
||||
&& 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)
|
||||
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
|
||||
// De-virtualize late-expanded interface calls into late-expanded static calls.
|
||||
// Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
|
||||
|
||||
@@ -355,7 +355,9 @@ var genericOps = []opData{
|
||||
{name: "Load", argLength: 2}, // Load from arg0. arg1=memory
|
||||
{name: "Dereference", argLength: 2}, // Load from arg0. arg1=memory. Helper op for arg/result passing, result is an otherwise not-SSA-able "value".
|
||||
{name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // Store arg1 to arg0. arg2=memory, aux=type. Returns memory.
|
||||
// The source and destination of Move may overlap in some cases. See e.g.
|
||||
// Normally we require that the source and destination of Move do not overlap.
|
||||
// There is an exception when we know all the loads will happen before all
|
||||
// the stores. In that case, overlap is ok. See
|
||||
// memmove inlining in generic.rules. When inlineablememmovesize (in ../rewrite.go)
|
||||
// returns true, we must do all loads before all stores, when lowering Move.
|
||||
// The type of Move is used for the write barrier pass to insert write barriers
|
||||
|
||||
@@ -159,6 +159,13 @@ func findIndVar(f *Func) []indVar {
|
||||
step = -step
|
||||
}
|
||||
|
||||
if flags&indVarMaxInc != 0 && max.Op == OpConst64 && max.AuxInt+step < max.AuxInt {
|
||||
// For a <= comparison, we need to make sure that a value equal to
|
||||
// max can be incremented without overflowing.
|
||||
// (For a < comparison, the %step check below ensures no overflow.)
|
||||
continue
|
||||
}
|
||||
|
||||
// Up to now we extracted the induction variable (ind),
|
||||
// the increment delta (inc), the temporary sum (nxt),
|
||||
// the mininum value (min) and the maximum value (max).
|
||||
|
||||
@@ -40,6 +40,7 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
|
||||
var states map[string]bool
|
||||
for {
|
||||
change := false
|
||||
deadChange := false
|
||||
for _, b := range f.Blocks {
|
||||
var b0 *Block
|
||||
if debug > 1 {
|
||||
@@ -73,7 +74,7 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
|
||||
// Not quite a deadcode pass, because it does not handle cycles.
|
||||
// But it should help Uses==1 rules to fire.
|
||||
v.reset(OpInvalid)
|
||||
change = true
|
||||
deadChange = true
|
||||
}
|
||||
// No point rewriting values which aren't used.
|
||||
continue
|
||||
@@ -145,15 +146,16 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
|
||||
}
|
||||
}
|
||||
}
|
||||
if !change {
|
||||
if !change && !deadChange {
|
||||
break
|
||||
}
|
||||
iters++
|
||||
if iters > 1000 || debug >= 2 {
|
||||
if (iters > 1000 || debug >= 2) && change {
|
||||
// We've done a suspiciously large number of rewrites (or we're in debug mode).
|
||||
// As of Sep 2021, 90% of rewrites complete in 4 iterations or fewer
|
||||
// and the maximum value encountered during make.bash is 12.
|
||||
// Start checking for cycles. (This is too expensive to do routinely.)
|
||||
// Note: we avoid this path for deadChange-only iterations, to fix #51639.
|
||||
if states == nil {
|
||||
states = make(map[string]bool)
|
||||
}
|
||||
@@ -1356,7 +1358,8 @@ func zeroUpper56Bits(x *Value, depth int) bool {
|
||||
|
||||
// isInlinableMemmove reports whether the given arch performs a Move of the given size
|
||||
// faster than memmove. It will only return true if replacing the memmove with a Move is
|
||||
// safe, either because Move is small or because the arguments are disjoint.
|
||||
// safe, either because Move will do all of its loads before any of its stores, or
|
||||
// because the arguments are known to be disjoint.
|
||||
// This is used as a check for replacing memmove with Move ops.
|
||||
func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
// It is always safe to convert memmove into Move when its arguments are disjoint.
|
||||
@@ -1375,6 +1378,9 @@ func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func IsInlinableMemmove(dst, src *Value, sz int64, c *Config) bool {
|
||||
return isInlinableMemmove(dst, src, sz, c)
|
||||
}
|
||||
|
||||
// logLargeCopy logs the occurrence of a large copy.
|
||||
// The best place to do this is in the rewrite rules where the size of the move is easy to find.
|
||||
@@ -1388,6 +1394,14 @@ func logLargeCopy(v *Value, s int64) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
func LogLargeCopy(funcName string, pos src.XPos, s int64) {
|
||||
if s < 128 {
|
||||
return
|
||||
}
|
||||
if logopt.Enabled() {
|
||||
logopt.LogOpt(pos, "copy", "lower", funcName, fmt.Sprintf("%d bytes", s))
|
||||
}
|
||||
}
|
||||
|
||||
// hasSmallRotate reports whether the architecture has rotate instructions
|
||||
// for sizes < 32-bit. This is used to decide whether to promote some rotations.
|
||||
|
||||
@@ -7321,12 +7321,154 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool {
|
||||
v.AuxInt = int64ToAuxInt(int64(uint8(c)))
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x)
|
||||
// cond: x.Type.IsBoolean()
|
||||
// match: (MOVBUreg x:(Equal _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if !(x.Type.IsBoolean()) {
|
||||
if x.Op != OpARM64Equal {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(NotEqual _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64NotEqual {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(LessThan _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64LessThan {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(LessThanU _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64LessThanU {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(LessThanF _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64LessThanF {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(LessEqual _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64LessEqual {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(LessEqualU _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64LessEqualU {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(LessEqualF _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64LessEqualF {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(GreaterThan _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64GreaterThan {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(GreaterThanU _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64GreaterThanU {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(GreaterThanF _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64GreaterThanF {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(GreaterEqual _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64GreaterEqual {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(GreaterEqualU _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64GreaterEqualU {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(GreaterEqualF _))
|
||||
// result: (MOVDreg x)
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpARM64GreaterEqualF {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDreg)
|
||||
@@ -28660,10 +28802,11 @@ func rewriteBlockARM64(b *Block) bool {
|
||||
return true
|
||||
}
|
||||
// match: (If cond yes no)
|
||||
// result: (NZ cond yes no)
|
||||
// result: (TBNZ [0] cond yes no)
|
||||
for {
|
||||
cond := b.Controls[0]
|
||||
b.resetWithControl(BlockARM64NZ, cond)
|
||||
b.resetWithControl(BlockARM64TBNZ, cond)
|
||||
b.AuxInt = int64ToAuxInt(0)
|
||||
return true
|
||||
}
|
||||
case BlockARM64LE:
|
||||
@@ -30052,6 +30195,161 @@ func rewriteBlockARM64(b *Block) bool {
|
||||
b.Reset(BlockFirst)
|
||||
return true
|
||||
}
|
||||
case BlockARM64TBNZ:
|
||||
// match: (TBNZ [0] (Equal cc) yes no)
|
||||
// result: (EQ cc yes no)
|
||||
for b.Controls[0].Op == OpARM64Equal {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64EQ, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (NotEqual cc) yes no)
|
||||
// result: (NE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64NotEqual {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64NE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (LessThan cc) yes no)
|
||||
// result: (LT cc yes no)
|
||||
for b.Controls[0].Op == OpARM64LessThan {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64LT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (LessThanU cc) yes no)
|
||||
// result: (ULT cc yes no)
|
||||
for b.Controls[0].Op == OpARM64LessThanU {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64ULT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (LessEqual cc) yes no)
|
||||
// result: (LE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64LessEqual {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64LE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (LessEqualU cc) yes no)
|
||||
// result: (ULE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64LessEqualU {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64ULE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (GreaterThan cc) yes no)
|
||||
// result: (GT cc yes no)
|
||||
for b.Controls[0].Op == OpARM64GreaterThan {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64GT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (GreaterThanU cc) yes no)
|
||||
// result: (UGT cc yes no)
|
||||
for b.Controls[0].Op == OpARM64GreaterThanU {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64UGT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (GreaterEqual cc) yes no)
|
||||
// result: (GE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64GreaterEqual {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64GE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (GreaterEqualU cc) yes no)
|
||||
// result: (UGE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64GreaterEqualU {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64UGE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (LessThanF cc) yes no)
|
||||
// result: (FLT cc yes no)
|
||||
for b.Controls[0].Op == OpARM64LessThanF {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64FLT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (LessEqualF cc) yes no)
|
||||
// result: (FLE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64LessEqualF {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64FLE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (GreaterThanF cc) yes no)
|
||||
// result: (FGT cc yes no)
|
||||
for b.Controls[0].Op == OpARM64GreaterThanF {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64FGT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (TBNZ [0] (GreaterEqualF cc) yes no)
|
||||
// result: (FGE cc yes no)
|
||||
for b.Controls[0].Op == OpARM64GreaterEqualF {
|
||||
v_0 := b.Controls[0]
|
||||
cc := v_0.Args[0]
|
||||
if auxIntToInt64(b.AuxInt) != 0 {
|
||||
break
|
||||
}
|
||||
b.resetWithControl(BlockARM64FGE, cc)
|
||||
return true
|
||||
}
|
||||
case BlockARM64UGE:
|
||||
// match: (UGE (FlagConstant [fc]) yes no)
|
||||
// cond: fc.uge()
|
||||
|
||||
@@ -17194,6 +17194,7 @@ func rewriteValuePPC64_OpZero(v *Value) bool {
|
||||
return false
|
||||
}
|
||||
func rewriteBlockPPC64(b *Block) bool {
|
||||
typ := &b.Func.Config.Types
|
||||
switch b.Kind {
|
||||
case BlockPPC64EQ:
|
||||
// match: (EQ (CMPconst [0] (ANDconst [c] x)) yes no)
|
||||
@@ -17767,12 +17768,15 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
return true
|
||||
}
|
||||
// match: (If cond yes no)
|
||||
// result: (NE (CMPWconst [0] cond) yes no)
|
||||
// result: (NE (CMPWconst [0] (ANDconst <typ.UInt32> [1] cond)) yes no)
|
||||
for {
|
||||
cond := b.Controls[0]
|
||||
v0 := b.NewValue0(cond.Pos, OpPPC64CMPWconst, types.TypeFlags)
|
||||
v0.AuxInt = int32ToAuxInt(0)
|
||||
v0.AddArg(cond)
|
||||
v1 := b.NewValue0(cond.Pos, OpPPC64ANDconst, typ.UInt32)
|
||||
v1.AuxInt = int64ToAuxInt(1)
|
||||
v1.AddArg(cond)
|
||||
v0.AddArg(v1)
|
||||
b.resetWithControl(BlockPPC64NE, v0)
|
||||
return true
|
||||
}
|
||||
@@ -18078,7 +18082,7 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
case BlockPPC64NE:
|
||||
// match: (NE (CMPWconst [0] (Equal cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (Equal cc))) yes no)
|
||||
// result: (EQ cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18086,14 +18090,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64Equal {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64Equal {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64EQ, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (NotEqual cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (NotEqual cc))) yes no)
|
||||
// result: (NE cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18101,14 +18109,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64NotEqual {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64NotEqual {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64NE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (LessThan cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (LessThan cc))) yes no)
|
||||
// result: (LT cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18116,14 +18128,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64LessThan {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64LessThan {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64LT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (LessEqual cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (LessEqual cc))) yes no)
|
||||
// result: (LE cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18131,14 +18147,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64LessEqual {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64LessEqual {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64LE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (GreaterThan cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (GreaterThan cc))) yes no)
|
||||
// result: (GT cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18146,14 +18166,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64GreaterThan {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64GreaterThan {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64GT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (GreaterEqual cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (GreaterEqual cc))) yes no)
|
||||
// result: (GE cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18161,14 +18185,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64GreaterEqual {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64GreaterEqual {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64GE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (FLessThan cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (FLessThan cc))) yes no)
|
||||
// result: (FLT cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18176,14 +18204,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64FLessThan {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64FLessThan {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64FLT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (FLessEqual cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (FLessEqual cc))) yes no)
|
||||
// result: (FLE cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18191,14 +18223,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64FLessEqual {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64FLessEqual {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64FLE, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (FGreaterThan cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (FGreaterThan cc))) yes no)
|
||||
// result: (FGT cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18206,14 +18242,18 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64FGreaterThan {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64FGreaterThan {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64FGT, cc)
|
||||
return true
|
||||
}
|
||||
// match: (NE (CMPWconst [0] (FGreaterEqual cc)) yes no)
|
||||
// match: (NE (CMPWconst [0] (ANDconst [1] (FGreaterEqual cc))) yes no)
|
||||
// result: (FGE cc yes no)
|
||||
for b.Controls[0].Op == OpPPC64CMPWconst {
|
||||
v_0 := b.Controls[0]
|
||||
@@ -18221,10 +18261,14 @@ func rewriteBlockPPC64(b *Block) bool {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64FGreaterEqual {
|
||||
if v_0_0.Op != OpPPC64ANDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
|
||||
break
|
||||
}
|
||||
cc := v_0_0.Args[0]
|
||||
v_0_0_0 := v_0_0.Args[0]
|
||||
if v_0_0_0.Op != OpPPC64FGreaterEqual {
|
||||
break
|
||||
}
|
||||
cc := v_0_0_0.Args[0]
|
||||
b.resetWithControl(BlockPPC64FGE, cc)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -3152,6 +3152,46 @@ func rewriteValueRISCV64_OpRISCV64MOVBUload(v *Value) bool {
|
||||
func rewriteValueRISCV64_OpRISCV64MOVBUreg(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
// match: (MOVBUreg x:(SEQZ _))
|
||||
// result: x
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpRISCV64SEQZ {
|
||||
break
|
||||
}
|
||||
v.copyOf(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(SNEZ _))
|
||||
// result: x
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpRISCV64SNEZ {
|
||||
break
|
||||
}
|
||||
v.copyOf(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(SLT _ _))
|
||||
// result: x
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpRISCV64SLT {
|
||||
break
|
||||
}
|
||||
v.copyOf(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(SLTU _ _))
|
||||
// result: x
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpRISCV64SLTU {
|
||||
break
|
||||
}
|
||||
v.copyOf(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg (MOVDconst [c]))
|
||||
// result: (MOVDconst [int64(uint8(c))])
|
||||
for {
|
||||
@@ -6483,6 +6523,7 @@ func rewriteValueRISCV64_OpZero(v *Value) bool {
|
||||
}
|
||||
}
|
||||
func rewriteBlockRISCV64(b *Block) bool {
|
||||
typ := &b.Func.Config.Types
|
||||
switch b.Kind {
|
||||
case BlockRISCV64BEQ:
|
||||
// match: (BEQ (MOVDconst [0]) cond yes no)
|
||||
@@ -6690,10 +6731,12 @@ func rewriteBlockRISCV64(b *Block) bool {
|
||||
}
|
||||
case BlockIf:
|
||||
// match: (If cond yes no)
|
||||
// result: (BNEZ cond yes no)
|
||||
// result: (BNEZ (MOVBUreg <typ.UInt64> cond) yes no)
|
||||
for {
|
||||
cond := b.Controls[0]
|
||||
b.resetWithControl(BlockRISCV64BNEZ, cond)
|
||||
v0 := b.NewValue0(cond.Pos, OpRISCV64MOVBUreg, typ.UInt64)
|
||||
v0.AddArg(cond)
|
||||
b.resetWithControl(BlockRISCV64BNEZ, v0)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21053,8 +21053,8 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21084,21 +21084,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
if s3.Op != OpStore {
|
||||
break
|
||||
}
|
||||
t := auxToType(s3.Aux)
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(t.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21128,21 +21127,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
if s3.Op != OpStore {
|
||||
break
|
||||
}
|
||||
t := auxToType(s3.Aux)
|
||||
mem := s3.Args[2]
|
||||
dst := s3.Args[1]
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(t.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21160,18 +21158,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21189,18 +21187,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt32(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21218,18 +21216,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt64(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem))
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||
for {
|
||||
if auxIntToInt64(v.AuxInt) != 0 {
|
||||
break
|
||||
@@ -21247,12 +21245,12 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
||||
break
|
||||
}
|
||||
sz := auxIntToInt32(call_2.AuxInt)
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpMove)
|
||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||
v.Aux = typeToAux(dst.Type.Elem())
|
||||
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||
v.AddArg3(dst, src, mem)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ func InitConfig() {
|
||||
ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier")
|
||||
ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded")
|
||||
ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice")
|
||||
ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove")
|
||||
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
|
||||
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
|
||||
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
|
||||
@@ -1387,7 +1388,47 @@ func (s *state) zero(t *types.Type, dst *ssa.Value) {
|
||||
}
|
||||
|
||||
func (s *state) move(t *types.Type, dst, src *ssa.Value) {
|
||||
s.moveWhichMayOverlap(t, dst, src, false)
|
||||
}
|
||||
func (s *state) moveWhichMayOverlap(t *types.Type, dst, src *ssa.Value, mayOverlap bool) {
|
||||
s.instrumentMove(t, dst, src)
|
||||
if mayOverlap && t.IsArray() && t.NumElem() > 1 && !ssa.IsInlinableMemmove(dst, src, t.Size(), s.f.Config) {
|
||||
// Normally, when moving Go values of type T from one location to another,
|
||||
// we don't need to worry about partial overlaps. The two Ts must either be
|
||||
// in disjoint (nonoverlapping) memory or in exactly the same location.
|
||||
// There are 2 cases where this isn't true:
|
||||
// 1) Using unsafe you can arrange partial overlaps.
|
||||
// 2) Since Go 1.17, you can use a cast from a slice to a ptr-to-array.
|
||||
// https://go.dev/ref/spec#Conversions_from_slice_to_array_pointer
|
||||
// This feature can be used to construct partial overlaps of array types.
|
||||
// var a [3]int
|
||||
// p := (*[2]int)(a[:])
|
||||
// q := (*[2]int)(a[1:])
|
||||
// *p = *q
|
||||
// We don't care about solving 1. Or at least, we haven't historically
|
||||
// and no one has complained.
|
||||
// For 2, we need to ensure that if there might be partial overlap,
|
||||
// then we can't use OpMove; we must use memmove instead.
|
||||
// (memmove handles partial overlap by copying in the correct
|
||||
// direction. OpMove does not.)
|
||||
//
|
||||
// Note that we have to be careful here not to introduce a call when
|
||||
// we're marshaling arguments to a call or unmarshaling results from a call.
|
||||
// Cases where this is happening must pass mayOverlap to false.
|
||||
// (Currently this only happens when unmarshaling results of a call.)
|
||||
if t.HasPointers() {
|
||||
s.rtcall(ir.Syms.Typedmemmove, true, nil, s.reflectType(t), dst, src)
|
||||
// We would have otherwise implemented this move with straightline code,
|
||||
// including a write barrier. Pretend we issue a write barrier here,
|
||||
// so that the write barrier tests work. (Otherwise they'd need to know
|
||||
// the details of IsInlineableMemmove.)
|
||||
s.curfn.SetWBPos(s.peekPos())
|
||||
} else {
|
||||
s.rtcall(ir.Syms.Memmove, true, nil, dst, src, s.constInt(types.Types[types.TUINTPTR], t.Size()))
|
||||
}
|
||||
ssa.LogLargeCopy(s.f.Name, s.peekPos(), t.Size())
|
||||
return
|
||||
}
|
||||
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem())
|
||||
store.Aux = t
|
||||
s.vars[memVar] = store
|
||||
@@ -1565,6 +1606,36 @@ func (s *state) stmt(n ir.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
// mayOverlap keeps track of whether the LHS and RHS might
|
||||
// refer to overlapping memory.
|
||||
mayOverlap := true
|
||||
if n.Y == nil {
|
||||
// Not a move at all, mayOverlap is not relevant.
|
||||
} else if n.Def {
|
||||
// A variable being defined cannot overlap anything else.
|
||||
mayOverlap = false
|
||||
} else if n.X.Op() == ir.ONAME && n.Y.Op() == ir.ONAME {
|
||||
// Two named things never overlap.
|
||||
// (Or they are identical, which we treat as nonoverlapping.)
|
||||
mayOverlap = false
|
||||
} else if n.Y.Op() == ir.ODEREF {
|
||||
p := n.Y.(*ir.StarExpr).X
|
||||
for p.Op() == ir.OCONVNOP {
|
||||
p = p.(*ir.ConvExpr).X
|
||||
}
|
||||
if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsString() {
|
||||
// Pointer fields of strings point to unmodifiable memory.
|
||||
// That memory can't overlap with the memory being written.
|
||||
mayOverlap = false
|
||||
}
|
||||
} else if n.Y.Op() == ir.ORESULT || n.Y.Op() == ir.OCALLFUNC || n.Y.Op() == ir.OCALLINTER {
|
||||
// When copying values out of the return area of a call, we know
|
||||
// the source and destination don't overlap. Importantly, we must
|
||||
// set mayOverlap so we don't introduce a call to memmove while
|
||||
// we still have live data in the argument area.
|
||||
mayOverlap = false
|
||||
}
|
||||
|
||||
// Evaluate RHS.
|
||||
rhs := n.Y
|
||||
if rhs != nil {
|
||||
@@ -1665,7 +1736,7 @@ func (s *state) stmt(n ir.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
s.assign(n.X, r, deref, skip)
|
||||
s.assignWhichMayOverlap(n.X, r, deref, skip, mayOverlap)
|
||||
|
||||
case ir.OIF:
|
||||
n := n.(*ir.IfStmt)
|
||||
@@ -3503,7 +3574,11 @@ const (
|
||||
// If deref is true, then we do left = *right instead (and right has already been nil-checked).
|
||||
// If deref is true and right == nil, just do left = 0.
|
||||
// skip indicates assignments (at the top level) that can be avoided.
|
||||
// mayOverlap indicates whether left&right might partially overlap in memory. Default is false.
|
||||
func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask) {
|
||||
s.assignWhichMayOverlap(left, right, deref, skip, false)
|
||||
}
|
||||
func (s *state) assignWhichMayOverlap(left ir.Node, right *ssa.Value, deref bool, skip skipMask, mayOverlap bool) {
|
||||
if left.Op() == ir.ONAME && ir.IsBlank(left) {
|
||||
return
|
||||
}
|
||||
@@ -3604,7 +3679,7 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask
|
||||
if right == nil {
|
||||
s.zero(t, addr)
|
||||
} else {
|
||||
s.move(t, addr, right)
|
||||
s.moveWhichMayOverlap(t, addr, right, mayOverlap)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -895,6 +895,18 @@ func evalunsafe(n ir.Node) int64 {
|
||||
sel.X = Expr(sel.X)
|
||||
sbase := sel.X
|
||||
|
||||
// Implicit dot may already be resolved for instantiating generic function. So we
|
||||
// need to remove any implicit dot until we reach the first non-implicit one, it's
|
||||
// the right base selector. See issue #53137.
|
||||
var clobberBase func(n ir.Node) ir.Node
|
||||
clobberBase = func(n ir.Node) ir.Node {
|
||||
if sel, ok := n.(*ir.SelectorExpr); ok && sel.Implicit() {
|
||||
return clobberBase(sel.X)
|
||||
}
|
||||
return n
|
||||
}
|
||||
sbase = clobberBase(sbase)
|
||||
|
||||
tsel := Expr(sel)
|
||||
n.X = tsel
|
||||
if tsel.Type() == nil {
|
||||
|
||||
@@ -66,9 +66,9 @@ func (p *crawler) markObject(n *ir.Name) {
|
||||
// inline bodies may be needed. For instantiated generic types, it visits the base
|
||||
// generic type, which has the relevant methods.
|
||||
func (p *crawler) markType(t *types.Type) {
|
||||
if t.OrigSym() != nil {
|
||||
if orig := t.OrigType(); orig != nil {
|
||||
// Convert to the base generic type.
|
||||
t = t.OrigSym().Def.Type()
|
||||
t = orig
|
||||
}
|
||||
if p.marked[t] {
|
||||
return
|
||||
@@ -154,9 +154,9 @@ func (p *crawler) markEmbed(t *types.Type) {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.OrigSym() != nil {
|
||||
if orig := t.OrigType(); orig != nil {
|
||||
// Convert to the base generic type.
|
||||
t = t.OrigSym().Def.Type()
|
||||
t = orig
|
||||
}
|
||||
|
||||
if p.embedded[t] {
|
||||
@@ -194,9 +194,9 @@ func (p *crawler) markGeneric(t *types.Type) {
|
||||
if t.IsPtr() {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.OrigSym() != nil {
|
||||
if orig := t.OrigType(); orig != nil {
|
||||
// Convert to the base generic type.
|
||||
t = t.OrigSym().Def.Type()
|
||||
t = orig
|
||||
}
|
||||
if p.generic[t] {
|
||||
return
|
||||
@@ -229,12 +229,12 @@ func (p *crawler) checkForFullyInst(t *types.Type) {
|
||||
// them available for import, and so will not need
|
||||
// another round of method and dictionary
|
||||
// instantiation after inlining.
|
||||
baseType := t.OrigSym().Def.(*ir.Name).Type()
|
||||
baseType := t.OrigType()
|
||||
shapes := make([]*types.Type, len(t.RParams()))
|
||||
for i, t1 := range t.RParams() {
|
||||
shapes[i] = Shapify(t1, i, baseType.RParams()[i])
|
||||
}
|
||||
for j := range t.Methods().Slice() {
|
||||
for j, tmethod := range t.Methods().Slice() {
|
||||
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
|
||||
dictsym := MakeDictSym(baseNname.Sym(), t.RParams(), true)
|
||||
if dictsym.Def == nil {
|
||||
@@ -255,6 +255,8 @@ func (p *crawler) checkForFullyInst(t *types.Type) {
|
||||
ImportedBody(methNode.Func)
|
||||
methNode.Func.SetExportInline(true)
|
||||
}
|
||||
// Make sure that any associated types are also exported. (See #52279)
|
||||
p.checkForFullyInst(tmethod.Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@ func (w *exportWriter) startType(k itag) {
|
||||
|
||||
func (w *exportWriter) doTyp(t *types.Type) {
|
||||
s := t.Sym()
|
||||
if s != nil && t.OrigSym() != nil {
|
||||
if s != nil && t.OrigType() != nil {
|
||||
assert(base.Flag.G > 0)
|
||||
// This is an instantiated type - could be a re-instantiation like
|
||||
// Value[T2] or a full instantiation like Value[int].
|
||||
@@ -970,7 +970,7 @@ func (w *exportWriter) doTyp(t *types.Type) {
|
||||
// types or existing typeparams from the function/method header.
|
||||
w.typeList(t.RParams())
|
||||
// Export a reference to the base type.
|
||||
baseType := t.OrigSym().Def.(*ir.Name).Type()
|
||||
baseType := t.OrigType()
|
||||
w.typ(baseType)
|
||||
return
|
||||
}
|
||||
@@ -1851,7 +1851,10 @@ func (w *exportWriter) expr(n ir.Node) {
|
||||
n := n.(*ir.ClosureExpr)
|
||||
w.op(ir.OCLOSURE)
|
||||
w.pos(n.Pos())
|
||||
old := w.currPkg
|
||||
w.setPkg(n.Type().Pkg(), true)
|
||||
w.signature(n.Type())
|
||||
w.setPkg(old, true)
|
||||
|
||||
// Write out id for the Outer of each conditional variable. The
|
||||
// conditional variable itself for this closure will be re-created
|
||||
@@ -2281,7 +2284,7 @@ func (w *exportWriter) localIdent(s *types.Sym) {
|
||||
return
|
||||
}
|
||||
|
||||
if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, LocalDictName) {
|
||||
if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, LocalDictName) && !strings.HasPrefix(name, ".rcvr") {
|
||||
base.Fatalf("unexpected dot in identifier: %v", name)
|
||||
}
|
||||
|
||||
|
||||
@@ -761,7 +761,7 @@ func (p *iimporter) typAt(off uint64) *types.Type {
|
||||
// No need to calc sizes for re-instantiated generic types, and
|
||||
// they are not necessarily resolved until the top-level type is
|
||||
// defined (because of recursive types).
|
||||
if t.OrigSym() == nil || !t.HasTParam() {
|
||||
if t.OrigType() == nil || !t.HasTParam() {
|
||||
types.CheckSize(t)
|
||||
}
|
||||
p.typCache[off] = t
|
||||
@@ -1374,7 +1374,9 @@ func (r *importReader) node() ir.Node {
|
||||
case ir.OCLOSURE:
|
||||
//println("Importing CLOSURE")
|
||||
pos := r.pos()
|
||||
r.setPkg()
|
||||
typ := r.signature(nil, nil)
|
||||
r.setPkg()
|
||||
|
||||
// All the remaining code below is similar to (*noder).funcLit(), but
|
||||
// with Dcls and ClosureVars lists already set up
|
||||
@@ -1480,7 +1482,7 @@ func (r *importReader) node() ir.Node {
|
||||
} else {
|
||||
genType := types.ReceiverBaseType(n1.X.Type())
|
||||
if genType.IsInstantiatedGeneric() {
|
||||
genType = genType.OrigSym().Def.Type()
|
||||
genType = genType.OrigType()
|
||||
}
|
||||
m = Lookdot1(n1, sel, genType, genType.Methods(), 1)
|
||||
}
|
||||
@@ -1911,7 +1913,7 @@ func Instantiate(pos src.XPos, baseType *types.Type, targs []*types.Type) *types
|
||||
|
||||
t := NewIncompleteNamedType(baseType.Pos(), instSym)
|
||||
t.SetRParams(targs)
|
||||
t.SetOrigSym(baseSym)
|
||||
t.SetOrigType(baseType)
|
||||
|
||||
// baseType may still be TFORW or its methods may not be fully filled in
|
||||
// (since we are in the middle of importing it). So, delay call to
|
||||
@@ -1936,7 +1938,7 @@ func resumeDoInst() {
|
||||
for len(deferredInstStack) > 0 {
|
||||
t := deferredInstStack[0]
|
||||
deferredInstStack = deferredInstStack[1:]
|
||||
substInstType(t, t.OrigSym().Def.(*ir.Name).Type(), t.RParams())
|
||||
substInstType(t, t.OrigType(), t.RParams())
|
||||
}
|
||||
}
|
||||
deferInst--
|
||||
@@ -1948,7 +1950,7 @@ func resumeDoInst() {
|
||||
// instantiations of mutually recursive types.
|
||||
func doInst(t *types.Type) *types.Type {
|
||||
assert(t.Kind() == types.TFORW)
|
||||
return Instantiate(t.Pos(), t.OrigSym().Def.(*ir.Name).Type(), t.RParams())
|
||||
return Instantiate(t.Pos(), t.OrigType(), t.RParams())
|
||||
}
|
||||
|
||||
// substInstType completes the instantiation of a generic type by doing a
|
||||
|
||||
@@ -1120,10 +1120,10 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
|
||||
forw = NewIncompleteNamedType(t.Pos(), newsym)
|
||||
//println("Creating new type by sub", newsym.Name, forw.HasTParam())
|
||||
forw.SetRParams(neededTargs)
|
||||
// Copy the OrigSym from the re-instantiated type (which is the sym of
|
||||
// Copy the OrigType from the re-instantiated type (which is the sym of
|
||||
// the base generic type).
|
||||
assert(t.OrigSym() != nil)
|
||||
forw.SetOrigSym(t.OrigSym())
|
||||
assert(t.OrigType() != nil)
|
||||
forw.SetOrigType(t.OrigType())
|
||||
}
|
||||
|
||||
var newt *types.Type
|
||||
@@ -1536,7 +1536,7 @@ func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type {
|
||||
// Note: pointers to arrays are special because of slice-to-array-pointer
|
||||
// conversions. See issue 49295.
|
||||
if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY &&
|
||||
tparam.Bound().StructuralType() == nil {
|
||||
tparam.Bound().StructuralType() == nil && !u.Elem().NotInHeap() {
|
||||
u = types.Types[types.TUINT8].PtrTo()
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ const (
|
||||
fmtDebug
|
||||
fmtTypeID
|
||||
fmtTypeIDName
|
||||
fmtTypeIDHash
|
||||
)
|
||||
|
||||
// Sym
|
||||
@@ -144,10 +145,21 @@ func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
|
||||
if q := pkgqual(s.Pkg, verb, mode); q != "" {
|
||||
b.WriteString(q)
|
||||
b.WriteByte('.')
|
||||
if mode == fmtTypeIDName {
|
||||
switch mode {
|
||||
case fmtTypeIDName:
|
||||
// If name is a generic instantiation, it might have local package placeholders
|
||||
// in it. Replace those placeholders with the package name. See issue 49547.
|
||||
name = strings.Replace(name, LocalPkg.Prefix, q, -1)
|
||||
case fmtTypeIDHash:
|
||||
// If name is a generic instantiation, don't hash the instantiating types.
|
||||
// This isn't great, but it is safe. If we hash the instantiating types, then
|
||||
// we need to make sure they have just the package name. At this point, they
|
||||
// either have "", or the whole package path, and it is hard to reconcile
|
||||
// the two without depending on -p (which we might do someday).
|
||||
// See issue 51250.
|
||||
if i := strings.Index(name, "["); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteString(name)
|
||||
@@ -173,7 +185,7 @@ func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
|
||||
case fmtDebug:
|
||||
return pkg.Name
|
||||
|
||||
case fmtTypeIDName:
|
||||
case fmtTypeIDName, fmtTypeIDHash:
|
||||
// dcommontype, typehash
|
||||
return pkg.Name
|
||||
|
||||
@@ -331,7 +343,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||
if t == AnyType || t == ByteType || t == RuneType {
|
||||
// in %-T mode collapse predeclared aliases with their originals.
|
||||
switch mode {
|
||||
case fmtTypeIDName, fmtTypeID:
|
||||
case fmtTypeIDName, fmtTypeIDHash, fmtTypeID:
|
||||
t = Types[t.Kind()]
|
||||
default:
|
||||
sconv2(b, t.Sym(), 'S', mode)
|
||||
@@ -422,7 +434,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||
case TPTR:
|
||||
b.WriteByte('*')
|
||||
switch mode {
|
||||
case fmtTypeID, fmtTypeIDName:
|
||||
case fmtTypeID, fmtTypeIDName, fmtTypeIDHash:
|
||||
if verb == 'S' {
|
||||
tconv2(b, t.Elem(), 'S', mode, visited)
|
||||
return
|
||||
@@ -484,7 +496,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||
case IsExported(f.Sym.Name):
|
||||
sconv2(b, f.Sym, 'S', mode)
|
||||
default:
|
||||
if mode != fmtTypeIDName {
|
||||
if mode != fmtTypeIDName && mode != fmtTypeIDHash {
|
||||
mode = fmtTypeID
|
||||
}
|
||||
sconv2(b, f.Sym, 'v', mode)
|
||||
@@ -554,7 +566,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||
b.WriteByte(byte(open))
|
||||
fieldVerb := 'v'
|
||||
switch mode {
|
||||
case fmtTypeID, fmtTypeIDName, fmtGo:
|
||||
case fmtTypeID, fmtTypeIDName, fmtTypeIDHash, fmtGo:
|
||||
// no argument names on function signature, and no "noescape"/"nosplit" tags
|
||||
fieldVerb = 'S'
|
||||
}
|
||||
@@ -688,7 +700,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty
|
||||
if name == ".F" {
|
||||
name = "F" // Hack for toolstash -cmp.
|
||||
}
|
||||
if !IsExported(name) && mode != fmtTypeIDName {
|
||||
if !IsExported(name) && mode != fmtTypeIDName && mode != fmtTypeIDHash {
|
||||
name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
|
||||
}
|
||||
} else {
|
||||
@@ -756,7 +768,7 @@ func FmtConst(v constant.Value, sharp bool) string {
|
||||
|
||||
// TypeHash computes a hash value for type t to use in type switch statements.
|
||||
func TypeHash(t *Type) uint32 {
|
||||
p := t.NameString()
|
||||
p := tconv(t, 0, fmtTypeIDHash)
|
||||
|
||||
// Using MD5 is overkill, but reduces accidental collisions.
|
||||
h := md5.Sum([]byte(p))
|
||||
|
||||
@@ -696,6 +696,12 @@ func PtrDataSize(t *Type) int64 {
|
||||
}
|
||||
return 0
|
||||
|
||||
case TSSA:
|
||||
if t != TypeInt128 {
|
||||
base.Fatalf("PtrDataSize: unexpected ssa type %v", t)
|
||||
}
|
||||
return 0
|
||||
|
||||
default:
|
||||
base.Fatalf("PtrDataSize: unexpected type, %v", t)
|
||||
return 0
|
||||
|
||||
@@ -202,10 +202,10 @@ type Type struct {
|
||||
// TODO(danscales): choose a better name.
|
||||
rparams *[]*Type
|
||||
|
||||
// For an instantiated generic type, the symbol for the base generic type.
|
||||
// For an instantiated generic type, the base generic type.
|
||||
// This backpointer is useful, because the base type is the type that has
|
||||
// the method bodies.
|
||||
origSym *Sym
|
||||
origType *Type
|
||||
}
|
||||
|
||||
func (*Type) CanBeAnSSAAux() {}
|
||||
@@ -250,10 +250,10 @@ func (t *Type) Kind() Kind { return t.kind }
|
||||
func (t *Type) Sym() *Sym { return t.sym }
|
||||
func (t *Type) SetSym(sym *Sym) { t.sym = sym }
|
||||
|
||||
// OrigSym returns the name of the original generic type that t is an
|
||||
// OrigType returns the original generic type that t is an
|
||||
// instantiation of, if any.
|
||||
func (t *Type) OrigSym() *Sym { return t.origSym }
|
||||
func (t *Type) SetOrigSym(sym *Sym) { t.origSym = sym }
|
||||
func (t *Type) OrigType() *Type { return t.origType }
|
||||
func (t *Type) SetOrigType(orig *Type) { t.origType = orig }
|
||||
|
||||
// Underlying returns the underlying type of type t.
|
||||
func (t *Type) Underlying() *Type { return t.underlying }
|
||||
@@ -1721,6 +1721,11 @@ var (
|
||||
TypeResultMem = newResults([]*Type{TypeMem})
|
||||
)
|
||||
|
||||
func init() {
|
||||
TypeInt128.width = 16
|
||||
TypeInt128.align = 8
|
||||
}
|
||||
|
||||
// NewNamed returns a new named type for the given type name. obj should be an
|
||||
// ir.Name. The new type is incomplete (marked as TFORW kind), and the underlying
|
||||
// type should be set later via SetUnderlying(). References to the type are
|
||||
|
||||
@@ -204,12 +204,12 @@ type Info struct {
|
||||
// qualified identifiers are collected in the Uses map.
|
||||
Types map[syntax.Expr]TypeAndValue
|
||||
|
||||
// Instances maps identifiers denoting parameterized types or functions to
|
||||
// their type arguments and instantiated type.
|
||||
// Instances maps identifiers denoting generic types or functions to their
|
||||
// type arguments and instantiated type.
|
||||
//
|
||||
// For example, Instances will map the identifier for 'T' in the type
|
||||
// instantiation T[int, string] to the type arguments [int, string] and
|
||||
// resulting instantiated *Named type. Given a parameterized function
|
||||
// resulting instantiated *Named type. Given a generic function
|
||||
// func F[A any](A), Instances will map the identifier for 'F' in the call
|
||||
// expression F(int(1)) to the inferred type arguments [int], and resulting
|
||||
// instantiated *Signature.
|
||||
@@ -421,8 +421,11 @@ func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Packa
|
||||
}
|
||||
|
||||
// AssertableTo reports whether a value of type V can be asserted to have type T.
|
||||
// The behavior of AssertableTo is undefined if V is a generalized interface; i.e.,
|
||||
// an interface that may only be used as a type constraint in Go code.
|
||||
//
|
||||
// The behavior of AssertableTo is undefined in two cases:
|
||||
// - if V is a generalized interface; i.e., an interface that may only be used
|
||||
// as a type constraint in Go code
|
||||
// - if T is an uninstantiated generic type
|
||||
func AssertableTo(V *Interface, T Type) bool {
|
||||
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
|
||||
// handling here.
|
||||
@@ -432,20 +435,31 @@ func AssertableTo(V *Interface, T Type) bool {
|
||||
return (*Checker)(nil).newAssertableTo(V, T) == nil
|
||||
}
|
||||
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable
|
||||
// of type T.
|
||||
//
|
||||
// The behavior of AssignableTo is undefined if V or T is an uninstantiated
|
||||
// generic type.
|
||||
func AssignableTo(V, T Type) bool {
|
||||
x := operand{mode: value, typ: V}
|
||||
ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
|
||||
return ok
|
||||
}
|
||||
|
||||
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
|
||||
// ConvertibleTo reports whether a value of type V is convertible to a value of
|
||||
// type T.
|
||||
//
|
||||
// The behavior of ConvertibleTo is undefined if V or T is an uninstantiated
|
||||
// generic type.
|
||||
func ConvertibleTo(V, T Type) bool {
|
||||
x := operand{mode: value, typ: V}
|
||||
return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
|
||||
}
|
||||
|
||||
// Implements reports whether type V implements interface T.
|
||||
//
|
||||
// The behavior of Implements is undefined if V is an uninstantiated generic
|
||||
// type.
|
||||
func Implements(V Type, T *Interface) bool {
|
||||
if T.Empty() {
|
||||
// All types (even Typ[Invalid]) implement the empty interface.
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"internal/testenv"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -403,69 +404,61 @@ func TestTypesInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstanceInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
src string
|
||||
const lib = `package lib
|
||||
|
||||
func F[P any](P) {}
|
||||
|
||||
type T[P any] []P
|
||||
`
|
||||
|
||||
type testInst struct {
|
||||
name string
|
||||
targs []string
|
||||
typ string
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
src string
|
||||
instances []testInst // recorded instances in source order
|
||||
}{
|
||||
{`package p0; func f[T any](T) {}; func _() { f(42) }`,
|
||||
`f`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
[]testInst{{`f`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
{`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
|
||||
`f`,
|
||||
[]string{`rune`},
|
||||
`func(rune) rune`,
|
||||
[]testInst{{`f`, []string{`rune`}, `func(rune) rune`}},
|
||||
},
|
||||
{`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
|
||||
`f`,
|
||||
[]string{`complex128`},
|
||||
`func(...complex128) complex128`,
|
||||
[]testInst{{`f`, []string{`complex128`}, `func(...complex128) complex128`}},
|
||||
},
|
||||
{`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
|
||||
`f`,
|
||||
[]string{`float64`, `string`, `byte`},
|
||||
`func(float64, *string, []byte)`,
|
||||
[]testInst{{`f`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}},
|
||||
},
|
||||
{`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
|
||||
`f`,
|
||||
[]string{`float64`, `byte`},
|
||||
`func(float64, *byte, ...[]byte)`,
|
||||
[]testInst{{`f`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}},
|
||||
},
|
||||
|
||||
// we don't know how to translate these but we can type-check them
|
||||
{`package q0; type T struct{}; func (T) m[P any](P) {}; func _(x T) { x.m(42) }`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
[]testInst{{`m`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
{`package q1; type T struct{}; func (T) m[P any](P) P { panic(0) }; func _(x T) { x.m(42) }`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(int) int`,
|
||||
[]testInst{{`m`, []string{`int`}, `func(int) int`}},
|
||||
},
|
||||
{`package q2; type T struct{}; func (T) m[P any](...P) P { panic(0) }; func _(x T) { x.m(42) }`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(...int) int`,
|
||||
[]testInst{{`m`, []string{`int`}, `func(...int) int`}},
|
||||
},
|
||||
{`package q3; type T struct{}; func (T) m[A, B, C any](A, *B, []C) {}; func _(x T) { x.m(1.2, new(string), []byte{}) }`,
|
||||
`m`,
|
||||
[]string{`float64`, `string`, `byte`},
|
||||
`func(float64, *string, []byte)`,
|
||||
[]testInst{{`m`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}},
|
||||
},
|
||||
{`package q4; type T struct{}; func (T) m[A, B any](A, *B, ...[]B) {}; func _(x T) { x.m(1.2, new(byte)) }`,
|
||||
`m`,
|
||||
[]string{`float64`, `byte`},
|
||||
`func(float64, *byte, ...[]byte)`,
|
||||
[]testInst{{`m`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}},
|
||||
},
|
||||
|
||||
{`package r0; type T[P any] struct{}; func (_ T[P]) m[Q any](Q) {}; func _[P any](x T[P]) { x.m(42) }`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
{`package r0; type T[P1 any] struct{}; func (_ T[P2]) m[Q any](Q) {}; func _[P3 any](x T[P3]) { x.m(42) }`,
|
||||
[]testInst{
|
||||
{`T`, []string{`P2`}, `struct{}`},
|
||||
{`T`, []string{`P3`}, `struct{}`},
|
||||
{`m`, []string{`int`}, `func(int)`},
|
||||
},
|
||||
},
|
||||
// TODO(gri) record method type parameters in syntax.FuncType so we can check this
|
||||
// {`package r1; type T interface{ m[P any](P) }; func _(x T) { x.m(4.2) }`,
|
||||
@@ -474,97 +467,113 @@ func TestInstanceInfo(t *testing.T) {
|
||||
// `func(float64)`,
|
||||
// },
|
||||
|
||||
{`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`string`, `*string`},
|
||||
`func(x string)`,
|
||||
{`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`,
|
||||
[]testInst{{`f`, []string{`string`, `*string`}, `func(x string)`}},
|
||||
},
|
||||
{`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`int`, `*int`},
|
||||
`func(x []int)`,
|
||||
{`package s2; func f[T any, P interface{*T}](x []T) {}; func _(x []int) { f(x) }`,
|
||||
[]testInst{{`f`, []string{`int`, `*int`}, `func(x []int)`}},
|
||||
},
|
||||
{`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`},
|
||||
`func(x []int)`,
|
||||
{`package s3; type C[T any] interface{chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`f`, []string{`int`, `chan<- int`}, `func(x []int)`},
|
||||
},
|
||||
},
|
||||
{`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func(x []int)`,
|
||||
{`package s4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
|
||||
{`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func(x []int)`},
|
||||
},
|
||||
},
|
||||
|
||||
{`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
|
||||
`f`,
|
||||
[]string{`string`, `*string`},
|
||||
`func() string`,
|
||||
{`package t1; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = f[string] }`,
|
||||
[]testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
|
||||
},
|
||||
{`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
|
||||
`f`,
|
||||
[]string{`string`, `*string`},
|
||||
`func() string`,
|
||||
{`package t2; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
|
||||
[]testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
|
||||
},
|
||||
{`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func() []int`,
|
||||
{`package t3; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
|
||||
{`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
|
||||
},
|
||||
},
|
||||
{`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func() []int`,
|
||||
{`package t4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = (f[int]) }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
|
||||
{`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
|
||||
},
|
||||
},
|
||||
{`package i0; import lib "generic_lib"; func _() { lib.F(42) }`,
|
||||
`F`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
{`package i0; import "lib"; func _() { lib.F(42) }`,
|
||||
[]testInst{{`F`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
|
||||
{`package duplfunc0; func f[T any](T) {}; func _() { f(42); f("foo"); f[int](3) }`,
|
||||
[]testInst{
|
||||
{`f`, []string{`int`}, `func(int)`},
|
||||
{`f`, []string{`string`}, `func(string)`},
|
||||
{`f`, []string{`int`}, `func(int)`},
|
||||
},
|
||||
},
|
||||
{`package duplfunc1; import "lib"; func _() { lib.F(42); lib.F("foo"); lib.F(3) }`,
|
||||
[]testInst{
|
||||
{`F`, []string{`int`}, `func(int)`},
|
||||
{`F`, []string{`string`}, `func(string)`},
|
||||
{`F`, []string{`int`}, `func(int)`},
|
||||
},
|
||||
},
|
||||
|
||||
{`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
[]testInst{{`T`, []string{`int`}, `struct{x int}`}},
|
||||
},
|
||||
{`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
[]testInst{{`T`, []string{`int`}, `struct{x int}`}},
|
||||
},
|
||||
{`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
[]testInst{{`T`, []string{`int`}, `struct{x int}`}},
|
||||
},
|
||||
{`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
|
||||
`T`,
|
||||
[]string{`[]int`, `int`},
|
||||
`struct{x []int; y int}`,
|
||||
[]testInst{{`T`, []string{`[]int`, `int`}, `struct{x []int; y int}`}},
|
||||
},
|
||||
{`package type4; import lib "generic_lib"; var _ lib.T[int]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`[]int`,
|
||||
{`package type4; import "lib"; var _ lib.T[int]`,
|
||||
[]testInst{{`T`, []string{`int`}, `[]int`}},
|
||||
},
|
||||
|
||||
{`package dupltype0; type T[P interface{~int}] struct{ x P }; var x T[int]; var y T[int]`,
|
||||
[]testInst{
|
||||
{`T`, []string{`int`}, `struct{x int}`},
|
||||
{`T`, []string{`int`}, `struct{x int}`},
|
||||
},
|
||||
},
|
||||
{`package dupltype1; type T[P ~int] struct{ x P }; func (r *T[Q]) add(z T[Q]) { r.x += z.x }`,
|
||||
[]testInst{
|
||||
{`T`, []string{`Q`}, `struct{x Q}`},
|
||||
{`T`, []string{`Q`}, `struct{x Q}`},
|
||||
},
|
||||
},
|
||||
{`package dupltype1; import "lib"; var x lib.T[int]; var y lib.T[int]; var z lib.T[string]`,
|
||||
[]testInst{
|
||||
{`T`, []string{`int`}, `[]int`},
|
||||
{`T`, []string{`int`}, `[]int`},
|
||||
{`T`, []string{`string`}, `[]string`},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
const lib = `package generic_lib
|
||||
|
||||
func F[P any](P) {}
|
||||
|
||||
type T[P any] []P
|
||||
`
|
||||
|
||||
imports := make(testImporter)
|
||||
conf := Config{Importer: imports}
|
||||
instances := make(map[*syntax.Name]Instance)
|
||||
uses := make(map[*syntax.Name]Object)
|
||||
instMap := make(map[*syntax.Name]Instance)
|
||||
useMap := make(map[*syntax.Name]Object)
|
||||
makePkg := func(src string) *Package {
|
||||
f, err := parseSrc("p.go", src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pkg, err := conf.Check("", []*syntax.File{f}, &Info{Instances: instances, Uses: uses})
|
||||
pkg, err := conf.Check("", []*syntax.File{f}, &Info{Instances: instMap, Uses: useMap})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -574,60 +583,72 @@ type T[P any] []P
|
||||
makePkg(lib)
|
||||
pkg := makePkg(test.src)
|
||||
|
||||
// look for instance information
|
||||
var targs []Type
|
||||
var typ Type
|
||||
for ident, inst := range instances {
|
||||
if syntax.String(ident) == test.name {
|
||||
for i := 0; i < inst.TypeArgs.Len(); i++ {
|
||||
targs = append(targs, inst.TypeArgs.At(i))
|
||||
}
|
||||
typ = inst.Type
|
||||
t.Run(pkg.Name(), func(t *testing.T) {
|
||||
// Sort instances in source order for stability.
|
||||
instances := sortedInstances(instMap)
|
||||
if got, want := len(instances), len(test.instances); got != want {
|
||||
t.Fatalf("got %d instances, want %d", got, want)
|
||||
}
|
||||
|
||||
// Check that we can find the corresponding parameterized type.
|
||||
ptype := uses[ident].Type()
|
||||
// Pairwise compare with the expected instances.
|
||||
for ii, inst := range instances {
|
||||
var targs []Type
|
||||
for i := 0; i < inst.Inst.TypeArgs.Len(); i++ {
|
||||
targs = append(targs, inst.Inst.TypeArgs.At(i))
|
||||
}
|
||||
typ := inst.Inst.Type
|
||||
|
||||
testInst := test.instances[ii]
|
||||
if got := inst.Name.Value; got != testInst.name {
|
||||
t.Fatalf("got name %s, want %s", got, testInst.name)
|
||||
}
|
||||
|
||||
if len(targs) != len(testInst.targs) {
|
||||
t.Fatalf("got %d type arguments; want %d", len(targs), len(testInst.targs))
|
||||
}
|
||||
for i, targ := range targs {
|
||||
if got := targ.String(); got != testInst.targs[i] {
|
||||
t.Errorf("type argument %d: got %s; want %s", i, got, testInst.targs[i])
|
||||
}
|
||||
}
|
||||
if got := typ.Underlying().String(); got != testInst.typ {
|
||||
t.Errorf("package %s: got %s; want %s", pkg.Name(), got, testInst.typ)
|
||||
}
|
||||
|
||||
// Verify the invariant that re-instantiating the corresponding generic
|
||||
// type with TypeArgs results in an identical instance.
|
||||
ptype := useMap[inst.Name].Type()
|
||||
lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
|
||||
if lister == nil || lister.TypeParams().Len() == 0 {
|
||||
t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
|
||||
continue
|
||||
t.Fatalf("info.Types[%v] = %v, want parameterized type", inst.Name, ptype)
|
||||
}
|
||||
|
||||
// Verify the invariant that re-instantiating the generic type with
|
||||
// TypeArgs results in an equivalent type.
|
||||
inst2, err := Instantiate(nil, ptype, targs, true)
|
||||
if err != nil {
|
||||
t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
|
||||
}
|
||||
if !Identical(inst.Type, inst2) {
|
||||
t.Errorf("%v and %v are not identical", inst.Type, inst2)
|
||||
if !Identical(inst.Inst.Type, inst2) {
|
||||
t.Errorf("%v and %v are not identical", inst.Inst.Type, inst2)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if targs == nil {
|
||||
t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
|
||||
continue
|
||||
}
|
||||
|
||||
// check that type arguments are correct
|
||||
if len(targs) != len(test.targs) {
|
||||
t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
|
||||
continue
|
||||
}
|
||||
for i, targ := range targs {
|
||||
if got := targ.String(); got != test.targs[i] {
|
||||
t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// check that the types match
|
||||
if got := typ.Underlying().String(); got != test.typ {
|
||||
t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type recordedInstance struct {
|
||||
Name *syntax.Name
|
||||
Inst Instance
|
||||
}
|
||||
|
||||
func sortedInstances(m map[*syntax.Name]Instance) (instances []recordedInstance) {
|
||||
for id, inst := range m {
|
||||
instances = append(instances, recordedInstance{id, inst})
|
||||
}
|
||||
sort.Slice(instances, func(i, j int) bool {
|
||||
return instances[i].Name.Pos().Cmp(instances[j].Name.Pos()) < 0
|
||||
})
|
||||
return instances
|
||||
}
|
||||
|
||||
func TestDefsInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
src string
|
||||
@@ -1598,19 +1619,41 @@ func TestLookupFieldOrMethod(t *testing.T) {
|
||||
{"var x T; type T struct{ f int }", true, []int{0}, false},
|
||||
{"var x T; type T struct{ a, b, f, c int }", true, []int{2}, false},
|
||||
|
||||
// field lookups on a generic type
|
||||
{"var x T[int]; type T[P any] struct{}", false, nil, false},
|
||||
{"var x T[int]; type T[P any] struct{ f P }", true, []int{0}, false},
|
||||
{"var x T[int]; type T[P any] struct{ a, b, f, c P }", true, []int{2}, false},
|
||||
|
||||
// method lookups
|
||||
{"var a T; type T struct{}; func (T) f() {}", true, []int{0}, false},
|
||||
{"var a *T; type T struct{}; func (T) f() {}", true, []int{0}, true},
|
||||
{"var a T; type T struct{}; func (*T) f() {}", true, []int{0}, false},
|
||||
{"var a *T; type T struct{}; func (*T) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false?
|
||||
|
||||
// method lookups on a generic type
|
||||
{"var a T[int]; type T[P any] struct{}; func (T[P]) f() {}", true, []int{0}, false},
|
||||
{"var a *T[int]; type T[P any] struct{}; func (T[P]) f() {}", true, []int{0}, true},
|
||||
{"var a T[int]; type T[P any] struct{}; func (*T[P]) f() {}", true, []int{0}, false},
|
||||
{"var a *T[int]; type T[P any] struct{}; func (*T[P]) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false?
|
||||
|
||||
// collisions
|
||||
{"type ( E1 struct{ f int }; E2 struct{ f int }; x struct{ E1; *E2 })", false, []int{1, 0}, false},
|
||||
{"type ( E1 struct{ f int }; E2 struct{}; x struct{ E1; *E2 }); func (E2) f() {}", false, []int{1, 0}, false},
|
||||
|
||||
// collisions on a generic type
|
||||
{"type ( E1[P any] struct{ f P }; E2[P any] struct{ f P }; x struct{ E1[int]; *E2[int] })", false, []int{1, 0}, false},
|
||||
{"type ( E1[P any] struct{ f P }; E2[P any] struct{}; x struct{ E1[int]; *E2[int] }); func (E2[P]) f() {}", false, []int{1, 0}, false},
|
||||
|
||||
// outside methodset
|
||||
// (*T).f method exists, but value of type T is not addressable
|
||||
{"var x T; type T struct{}; func (*T) f() {}", false, nil, true},
|
||||
|
||||
// outside method set of a generic type
|
||||
{"var x T[int]; type T[P any] struct{}; func (*T[P]) f() {}", false, nil, true},
|
||||
|
||||
// recursive generic types; see golang/go#52715
|
||||
{"var a T[int]; type ( T[P any] struct { *N[P] }; N[P any] struct { *T[P] } ); func (N[P]) f() {}", true, []int{0, 0}, true},
|
||||
{"var a T[int]; type ( T[P any] struct { *N[P] }; N[P any] struct { *T[P] } ); func (T[P]) f() {}", true, []int{0}, false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -1645,6 +1688,37 @@ func TestLookupFieldOrMethod(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test for golang/go#52715
|
||||
func TestLookupFieldOrMethod_RecursiveGeneric(t *testing.T) {
|
||||
const src = `
|
||||
package pkg
|
||||
|
||||
type Tree[T any] struct {
|
||||
*Node[T]
|
||||
}
|
||||
|
||||
func (*Tree[R]) N(r R) R { return r }
|
||||
|
||||
type Node[T any] struct {
|
||||
*Tree[T]
|
||||
}
|
||||
|
||||
type Instance = *Tree[int]
|
||||
`
|
||||
|
||||
f, err := parseSrc("foo.go", src)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pkg := NewPackage("pkg", f.PkgName.Value)
|
||||
if err := NewChecker(nil, pkg, nil).Files([]*syntax.File{f}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
T := pkg.Scope().Lookup("Instance").Type()
|
||||
_, _, _ = LookupFieldOrMethod(T, false, pkg, "M") // verify that LookupFieldOrMethod terminates
|
||||
}
|
||||
|
||||
func sameSlice(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
@@ -1697,7 +1771,7 @@ func F(){
|
||||
var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
|
||||
|
||||
var a []int
|
||||
for i, x := range /*i=undef*/ /*x=var:16*/ a /*i=var:20*/ /*x=var:20*/ { _ = i; _ = x }
|
||||
for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
|
||||
|
||||
var i interface{}
|
||||
switch y := i.(type) { /*y=undef*/
|
||||
@@ -2267,6 +2341,103 @@ func TestInstanceIdentity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestInstantiatedObjects verifies properties of instantiated objects.
|
||||
func TestInstantiatedObjects(t *testing.T) {
|
||||
const src = `
|
||||
package p
|
||||
|
||||
type T[P any] struct {
|
||||
field P
|
||||
}
|
||||
|
||||
func (recv *T[Q]) concreteMethod() {}
|
||||
|
||||
type FT[P any] func(ftp P) (ftrp P)
|
||||
|
||||
func F[P any](fp P) (frp P){ return }
|
||||
|
||||
type I[P any] interface {
|
||||
interfaceMethod(P)
|
||||
}
|
||||
|
||||
var (
|
||||
t T[int]
|
||||
ft FT[int]
|
||||
f = F[int]
|
||||
i I[int]
|
||||
)
|
||||
`
|
||||
info := &Info{
|
||||
Defs: make(map[*syntax.Name]Object),
|
||||
}
|
||||
f, err := parseSrc("p.go", src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conf := Config{}
|
||||
pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
lookup := func(name string) Type { return pkg.Scope().Lookup(name).Type() }
|
||||
tests := []struct {
|
||||
ident string
|
||||
obj Object
|
||||
}{
|
||||
{"field", lookup("t").Underlying().(*Struct).Field(0)},
|
||||
{"concreteMethod", lookup("t").(*Named).Method(0)},
|
||||
{"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()},
|
||||
{"ftp", lookup("ft").Underlying().(*Signature).Params().At(0)},
|
||||
{"ftrp", lookup("ft").Underlying().(*Signature).Results().At(0)},
|
||||
{"fp", lookup("f").(*Signature).Params().At(0)},
|
||||
{"frp", lookup("f").(*Signature).Results().At(0)},
|
||||
{"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)},
|
||||
}
|
||||
|
||||
// Collect all identifiers by name.
|
||||
idents := make(map[string][]*syntax.Name)
|
||||
syntax.Inspect(f, func(n syntax.Node) bool {
|
||||
if id, ok := n.(*syntax.Name); ok {
|
||||
idents[id.Value] = append(idents[id.Value], id)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.ident, func(t *testing.T) {
|
||||
if got := len(idents[test.ident]); got != 1 {
|
||||
t.Fatalf("found %d identifiers named %s, want 1", got, test.ident)
|
||||
}
|
||||
ident := idents[test.ident][0]
|
||||
def := info.Defs[ident]
|
||||
if def == test.obj {
|
||||
t.Fatalf("info.Defs[%s] contains the test object", test.ident)
|
||||
}
|
||||
if def.Pkg() != test.obj.Pkg() {
|
||||
t.Errorf("Pkg() = %v, want %v", def.Pkg(), test.obj.Pkg())
|
||||
}
|
||||
if def.Name() != test.obj.Name() {
|
||||
t.Errorf("Name() = %v, want %v", def.Name(), test.obj.Name())
|
||||
}
|
||||
if def.Pos() != test.obj.Pos() {
|
||||
t.Errorf("Pos() = %v, want %v", def.Pos(), test.obj.Pos())
|
||||
}
|
||||
if def.Parent() != test.obj.Parent() {
|
||||
t.Fatalf("Parent() = %v, want %v", def.Parent(), test.obj.Parent())
|
||||
}
|
||||
if def.Exported() != test.obj.Exported() {
|
||||
t.Fatalf("Exported() = %v, want %v", def.Exported(), test.obj.Exported())
|
||||
}
|
||||
if def.Id() != test.obj.Id() {
|
||||
t.Fatalf("Id() = %v, want %v", def.Id(), test.obj.Id())
|
||||
}
|
||||
// String and Type are expected to differ.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImplements(t *testing.T) {
|
||||
const src = `
|
||||
package p
|
||||
|
||||
@@ -423,7 +423,7 @@ var cgoPrefixes = [...]string{
|
||||
"_Cmacro_", // function to evaluate the expanded expression
|
||||
}
|
||||
|
||||
func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
|
||||
func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, def *Named) {
|
||||
// these must be declared before the "goto Error" statements
|
||||
var (
|
||||
obj Object
|
||||
@@ -526,6 +526,12 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
|
||||
|
||||
check.exprOrType(x, e.X, false)
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
// don't crash for "type T T.x" (was issue #51509)
|
||||
if def != nil && x.typ == def {
|
||||
check.cycleError([]Object{def.obj})
|
||||
goto Error
|
||||
}
|
||||
case builtin:
|
||||
check.errorf(e.Pos(), "cannot select on %s", x)
|
||||
goto Error
|
||||
|
||||
@@ -12,11 +12,12 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// An Context is an opaque type checking context. It may be used to share
|
||||
// A Context is an opaque type checking context. It may be used to share
|
||||
// identical type instances across type-checked packages or calls to
|
||||
// Instantiate.
|
||||
// Instantiate. Contexts are safe for concurrent use.
|
||||
//
|
||||
// It is safe for concurrent use.
|
||||
// The use of a shared context does not guarantee that identical instances are
|
||||
// deduplicated in all cases.
|
||||
type Context struct {
|
||||
mu sync.Mutex
|
||||
typeMap map[string][]ctxtEntry // type hash -> instances entries
|
||||
|
||||
@@ -636,14 +636,12 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
||||
base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
|
||||
if base != nil {
|
||||
assert(base.targs.Len() == 0) // collectMethods should not be called on an instantiated type
|
||||
u := base.under()
|
||||
if t, _ := u.(*Struct); t != nil {
|
||||
for _, fld := range t.fields {
|
||||
if fld.name != "_" {
|
||||
assert(mset.insert(fld) == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See issue #52529: we must delay the expansion of underlying here, as
|
||||
// base may not be fully set-up.
|
||||
check.later(func() {
|
||||
check.checkFieldUniqueness(base)
|
||||
}).describef(obj, "verifying field uniqueness for %v", base)
|
||||
|
||||
// Checker.Files may be called multiple times; additional package files
|
||||
// may add methods to already type-checked types. Add pre-existing methods
|
||||
@@ -662,17 +660,10 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
||||
assert(m.name != "_")
|
||||
if alt := mset.insert(m); alt != nil {
|
||||
var err error_
|
||||
switch alt.(type) {
|
||||
case *Var:
|
||||
err.errorf(m.pos, "field and method with the same name %s", m.name)
|
||||
case *Func:
|
||||
if check.conf.CompilerErrorMessages {
|
||||
err.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name)
|
||||
} else {
|
||||
err.errorf(m.pos, "method %s already declared for %s", m.name, obj)
|
||||
}
|
||||
default:
|
||||
unreachable()
|
||||
if check.conf.CompilerErrorMessages {
|
||||
err.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name)
|
||||
} else {
|
||||
err.errorf(m.pos, "method %s already declared for %s", m.name, obj)
|
||||
}
|
||||
err.recordAltDecl(alt)
|
||||
check.report(&err)
|
||||
@@ -686,6 +677,36 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) checkFieldUniqueness(base *Named) {
|
||||
if t, _ := base.under().(*Struct); t != nil {
|
||||
var mset objset
|
||||
for i := 0; i < base.methods.Len(); i++ {
|
||||
m := base.methods.At(i, nil)
|
||||
assert(m.name != "_")
|
||||
assert(mset.insert(m) == nil)
|
||||
}
|
||||
|
||||
// Check that any non-blank field names of base are distinct from its
|
||||
// method names.
|
||||
for _, fld := range t.fields {
|
||||
if fld.name != "_" {
|
||||
if alt := mset.insert(fld); alt != nil {
|
||||
// Struct fields should already be unique, so we should only
|
||||
// encounter an alternate via collision with a method name.
|
||||
_ = alt.(*Func)
|
||||
|
||||
// For historical consistency, we report the primary error on the
|
||||
// method, and the alt decl on the field.
|
||||
var err error_
|
||||
err.errorf(alt, "field and method with the same name %s", fld.name)
|
||||
err.recordAltDecl(fld)
|
||||
check.report(&err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
||||
assert(obj.typ == nil)
|
||||
|
||||
@@ -735,7 +756,7 @@ func (check *Checker) declStmt(list []syntax.Decl) {
|
||||
top := len(check.delayed)
|
||||
|
||||
// iota is the index of the current constDecl within the group
|
||||
if first < 0 || list[index-1].(*syntax.ConstDecl).Group != s.Group {
|
||||
if first < 0 || s.Group == nil || list[index-1].(*syntax.ConstDecl).Group != s.Group {
|
||||
first = index
|
||||
last = nil
|
||||
}
|
||||
|
||||
@@ -1556,7 +1556,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
|
||||
return kind
|
||||
|
||||
case *syntax.SelectorExpr:
|
||||
check.selector(x, e)
|
||||
check.selector(x, e, nil)
|
||||
|
||||
case *syntax.IndexExpr:
|
||||
if check.indexExpr(x, e) {
|
||||
@@ -1642,6 +1642,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
|
||||
case invalid:
|
||||
goto Error
|
||||
case typexpr:
|
||||
check.validVarType(e.X, x.typ)
|
||||
x.typ = &Pointer{base: x.typ}
|
||||
default:
|
||||
var base Type
|
||||
|
||||
@@ -488,21 +488,88 @@ func (check *Checker) inferB(pos syntax.Pos, tparams []*TypeParam, targs []Type)
|
||||
}
|
||||
}
|
||||
|
||||
// If a constraint has a core type, unify the corresponding type parameter with it.
|
||||
for _, tpar := range tparams {
|
||||
if ctype := adjCoreType(tpar); ctype != nil {
|
||||
if !u.unify(tpar, ctype) {
|
||||
// TODO(gri) improve error message by providing the type arguments
|
||||
// which we know already
|
||||
check.errorf(pos, "%s does not match %s", tpar, ctype)
|
||||
return nil, 0
|
||||
// Repeatedly apply constraint type inference as long as
|
||||
// there are still unknown type arguments and progress is
|
||||
// being made.
|
||||
//
|
||||
// This is an O(n^2) algorithm where n is the number of
|
||||
// type parameters: if there is progress (and iteration
|
||||
// continues), at least one type argument is inferred
|
||||
// per iteration and we have a doubly nested loop.
|
||||
// In practice this is not a problem because the number
|
||||
// of type parameters tends to be very small (< 5 or so).
|
||||
// (It should be possible for unification to efficiently
|
||||
// signal newly inferred type arguments; then the loops
|
||||
// here could handle the respective type parameters only,
|
||||
// but that will come at a cost of extra complexity which
|
||||
// may not be worth it.)
|
||||
for n := u.x.unknowns(); n > 0; {
|
||||
nn := n
|
||||
|
||||
for i, tpar := range tparams {
|
||||
// If there is a core term (i.e., a core type with tilde information)
|
||||
// unify the type parameter with the core type.
|
||||
if core, single := coreTerm(tpar); core != nil {
|
||||
// A type parameter can be unified with its core type in two cases.
|
||||
tx := u.x.at(i)
|
||||
switch {
|
||||
case tx != nil:
|
||||
// The corresponding type argument tx is known.
|
||||
// In this case, if the core type has a tilde, the type argument's underlying
|
||||
// type must match the core type, otherwise the type argument and the core type
|
||||
// must match.
|
||||
// If tx is an external type parameter, don't consider its underlying type
|
||||
// (which is an interface). Core type unification will attempt to unify against
|
||||
// core.typ.
|
||||
// Note also that even with inexact unification we cannot leave away the under
|
||||
// call here because it's possible that both tx and core.typ are named types,
|
||||
// with under(tx) being a (named) basic type matching core.typ. Such cases do
|
||||
// not match with inexact unification.
|
||||
if core.tilde && !isTypeParam(tx) {
|
||||
tx = under(tx)
|
||||
}
|
||||
if !u.unify(tx, core.typ) {
|
||||
// TODO(gri) improve error message by providing the type arguments
|
||||
// which we know already
|
||||
// Don't use term.String() as it always qualifies types, even if they
|
||||
// are in the current package.
|
||||
tilde := ""
|
||||
if core.tilde {
|
||||
tilde = "~"
|
||||
}
|
||||
check.errorf(pos, "%s does not match %s%s", tpar, tilde, core.typ)
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
case single && !core.tilde:
|
||||
// The corresponding type argument tx is unknown and there's a single
|
||||
// specific type and no tilde.
|
||||
// In this case the type argument must be that single type; set it.
|
||||
u.x.set(i, core.typ)
|
||||
|
||||
default:
|
||||
// Unification is not possible and no progress was made.
|
||||
continue
|
||||
}
|
||||
|
||||
// The number of known type arguments may have changed.
|
||||
nn = u.x.unknowns()
|
||||
if nn == 0 {
|
||||
break // all type arguments are known
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(nn <= n)
|
||||
if nn == n {
|
||||
break // no progress
|
||||
}
|
||||
n = nn
|
||||
}
|
||||
|
||||
// u.x.types() now contains the incoming type arguments plus any additional type
|
||||
// arguments which were inferred from core types. The newly inferred non-
|
||||
// nil entries may still contain references to other type parameters.
|
||||
// arguments which were inferred from core terms. The newly inferred non-nil
|
||||
// entries may still contain references to other type parameters.
|
||||
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
|
||||
// was given, unification produced the type list [int, []C, *A]. We eliminate the
|
||||
// remaining type parameters by substituting the type parameters in this type list
|
||||
@@ -591,26 +658,40 @@ func (check *Checker) inferB(pos syntax.Pos, tparams []*TypeParam, targs []Type)
|
||||
return
|
||||
}
|
||||
|
||||
// adjCoreType returns the core type of tpar unless the
|
||||
// type parameter embeds a single, possibly named type,
|
||||
// in which case it returns that single type instead.
|
||||
// (The core type is always the underlying type of that
|
||||
// single type.)
|
||||
func adjCoreType(tpar *TypeParam) Type {
|
||||
var single *term
|
||||
if tpar.is(func(t *term) bool {
|
||||
if single == nil && t != nil {
|
||||
single = t
|
||||
return true
|
||||
// If the type parameter has a single specific type S, coreTerm returns (S, true).
|
||||
// Otherwise, if tpar has a core type T, it returns a term corresponding to that
|
||||
// core type and false. In that case, if any term of tpar has a tilde, the core
|
||||
// term has a tilde. In all other cases coreTerm returns (nil, false).
|
||||
func coreTerm(tpar *TypeParam) (*term, bool) {
|
||||
n := 0
|
||||
var single *term // valid if n == 1
|
||||
var tilde bool
|
||||
tpar.is(func(t *term) bool {
|
||||
if t == nil {
|
||||
assert(n == 0)
|
||||
return false // no terms
|
||||
}
|
||||
return false // zero or more than one terms
|
||||
}) {
|
||||
n++
|
||||
single = t
|
||||
if t.tilde {
|
||||
tilde = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
if n == 1 {
|
||||
if debug {
|
||||
assert(under(single.typ) == coreType(tpar))
|
||||
assert(debug && under(single.typ) == coreType(tpar))
|
||||
}
|
||||
return single.typ
|
||||
return single, true
|
||||
}
|
||||
return coreType(tpar)
|
||||
if typ := coreType(tpar); typ != nil {
|
||||
// A core type is always an underlying type.
|
||||
// If any term of tpar has a tilde, we don't
|
||||
// have a precise core type and we must return
|
||||
// a tilde as well.
|
||||
return &term{tilde, typ}, false
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type cycleFinder struct {
|
||||
@@ -658,8 +739,6 @@ func (w *cycleFinder) typ(typ Type) {
|
||||
// in signatures where they are handled explicitly.
|
||||
|
||||
case *Signature:
|
||||
// There are no "method types" so we should never see a recv.
|
||||
assert(t.recv == nil)
|
||||
if t.params != nil {
|
||||
w.varList(t.params.vars)
|
||||
}
|
||||
|
||||
@@ -15,16 +15,17 @@ import (
|
||||
|
||||
// Instantiate instantiates the type orig with the given type arguments targs.
|
||||
// orig must be a *Named or a *Signature type. If there is no error, the
|
||||
// resulting Type is a new, instantiated (not parameterized) type of the same
|
||||
// kind (either a *Named or a *Signature). Methods attached to a *Named type
|
||||
// are also instantiated, and associated with a new *Func that has the same
|
||||
// position as the original method, but nil function scope.
|
||||
// resulting Type is an instantiated type of the same kind (either a *Named or
|
||||
// a *Signature). Methods attached to a *Named type are also instantiated, and
|
||||
// associated with a new *Func that has the same position as the original
|
||||
// method, but nil function scope.
|
||||
//
|
||||
// If ctxt is non-nil, it may be used to de-duplicate the instance against
|
||||
// previous instances with the same identity. As a special case, generic
|
||||
// *Signature origin types are only considered identical if they are pointer
|
||||
// equivalent, so that instantiating distinct (but possibly identical)
|
||||
// signatures will yield different instances.
|
||||
// signatures will yield different instances. The use of a shared context does
|
||||
// not guarantee that identical instances are deduplicated in all cases.
|
||||
//
|
||||
// If validate is set, Instantiate verifies that the number of type arguments
|
||||
// and parameters match, and that the type arguments satisfy their
|
||||
|
||||
@@ -637,3 +637,40 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue55030(t *testing.T) {
|
||||
// makeSig makes the signature func(typ...)
|
||||
makeSig := func(typ Type) {
|
||||
par := NewVar(nopos, nil, "", typ)
|
||||
params := NewTuple(par)
|
||||
NewSignatureType(nil, nil, nil, params, nil, true)
|
||||
}
|
||||
|
||||
// makeSig must not panic for the following (example) types:
|
||||
// []int
|
||||
makeSig(NewSlice(Typ[Int]))
|
||||
|
||||
// string
|
||||
makeSig(Typ[String])
|
||||
|
||||
// P where P's core type is string
|
||||
{
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P string]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
|
||||
}
|
||||
|
||||
// P where P's core type is an (unnamed) slice
|
||||
{
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P []int]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
|
||||
}
|
||||
|
||||
// P where P's core type is bytestring (i.e., string or []byte)
|
||||
{
|
||||
t1 := NewTerm(true, Typ[String]) // ~string
|
||||
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
|
||||
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
|
||||
P := NewTypeName(nopos, nil, "P", nil) // [P ~string | []byte]
|
||||
makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
// The last index entry is the field or method index in the (possibly embedded)
|
||||
// type where the entry was found, either:
|
||||
//
|
||||
// 1) the list of declared methods of a named type; or
|
||||
// 2) the list of all methods (method set) of an interface type; or
|
||||
// 3) the list of fields of a struct type.
|
||||
// 1. the list of declared methods of a named type; or
|
||||
// 2. the list of all methods (method set) of an interface type; or
|
||||
// 3. the list of fields of a struct type.
|
||||
//
|
||||
// The earlier index entries are the indices of the embedded struct fields
|
||||
// traversed to get to the found entry, starting at depth 0.
|
||||
@@ -35,13 +35,12 @@ import (
|
||||
// If no entry is found, a nil object is returned. In this case, the returned
|
||||
// index and indirect values have the following meaning:
|
||||
//
|
||||
// - If index != nil, the index sequence points to an ambiguous entry
|
||||
// (the same name appeared more than once at the same embedding level).
|
||||
//
|
||||
// - If indirect is set, a method with a pointer receiver type was found
|
||||
// but there was no pointer on the path from the actual receiver type to
|
||||
// the method's formal receiver base type, nor was the receiver addressable.
|
||||
// - If index != nil, the index sequence points to an ambiguous entry
|
||||
// (the same name appeared more than once at the same embedding level).
|
||||
//
|
||||
// - If indirect is set, a method with a pointer receiver type was found
|
||||
// but there was no pointer on the path from the actual receiver type to
|
||||
// the method's formal receiver base type, nor was the receiver addressable.
|
||||
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
||||
if T == nil {
|
||||
panic("LookupFieldOrMethod on nil type")
|
||||
@@ -70,7 +69,8 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
// see if there is a matching field (but not a method, those need to be declared
|
||||
// explicitly in the constraint). If the constraint is a named pointer type (see
|
||||
// above), we are ok here because only fields are accepted as results.
|
||||
if obj == nil && isTypeParam(T) {
|
||||
const enableTParamFieldLookup = false // see issue #51576
|
||||
if enableTParamFieldLookup && obj == nil && isTypeParam(T) {
|
||||
if t := coreType(T); t != nil {
|
||||
obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name, false)
|
||||
if _, ok := obj.(*Var); !ok {
|
||||
@@ -81,11 +81,6 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(gri) The named type consolidation and seen maps below must be
|
||||
// indexed by unique keys for a given type. Verify that named
|
||||
// types always have only one representation (even when imported
|
||||
// indirectly via different packages.)
|
||||
|
||||
// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
|
||||
// If foldCase is true, the lookup for methods will include looking for any method
|
||||
// which case-folds to the same as 'name' (used for giving helpful error messages).
|
||||
@@ -110,14 +105,12 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, fo
|
||||
// Start with typ as single entry at shallowest depth.
|
||||
current := []embeddedType{{typ, nil, isPtr, false}}
|
||||
|
||||
// Named types that we have seen already, allocated lazily.
|
||||
// seen tracks named types that we have seen already, allocated lazily.
|
||||
// Used to avoid endless searches in case of recursive types.
|
||||
// Since only Named types can be used for recursive types, we
|
||||
// only need to track those.
|
||||
// (If we ever allow type aliases to construct recursive types,
|
||||
// we must use type identity rather than pointer equality for
|
||||
// the map key comparison, as we do in consolidateMultiples.)
|
||||
var seen map[*Named]bool
|
||||
//
|
||||
// We must use a lookup on identity rather than a simple map[*Named]bool as
|
||||
// instantiated types may be identical but not equal.
|
||||
var seen instanceLookup
|
||||
|
||||
// search current depth
|
||||
for len(current) > 0 {
|
||||
@@ -130,7 +123,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, fo
|
||||
// If we have a named type, we may have associated methods.
|
||||
// Look for those first.
|
||||
if named, _ := typ.(*Named); named != nil {
|
||||
if seen[named] {
|
||||
if alt := seen.lookup(named); alt != nil {
|
||||
// We have seen this type before, at a more shallow depth
|
||||
// (note that multiples of this type at the current depth
|
||||
// were consolidated before). The type at that depth shadows
|
||||
@@ -138,10 +131,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, fo
|
||||
// this one.
|
||||
continue
|
||||
}
|
||||
if seen == nil {
|
||||
seen = make(map[*Named]bool)
|
||||
}
|
||||
seen[named] = true
|
||||
seen.add(named)
|
||||
|
||||
// look for a matching attached method
|
||||
named.resolve(nil)
|
||||
@@ -271,6 +261,27 @@ func lookupType(m map[Type]int, typ Type) (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
type instanceLookup struct {
|
||||
m map[*Named][]*Named
|
||||
}
|
||||
|
||||
func (l *instanceLookup) lookup(inst *Named) *Named {
|
||||
for _, t := range l.m[inst.Origin()] {
|
||||
if Identical(inst, t) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *instanceLookup) add(inst *Named) {
|
||||
if l.m == nil {
|
||||
l.m = make(map[*Named][]*Named)
|
||||
}
|
||||
insts := l.m[inst.Origin()]
|
||||
l.m[inst.Origin()] = append(insts, inst)
|
||||
}
|
||||
|
||||
// MissingMethod returns (nil, false) if V implements T, otherwise it
|
||||
// returns a missing method required by T and whether it is missing or
|
||||
// just has the wrong type.
|
||||
@@ -280,7 +291,6 @@ func lookupType(m map[Type]int, typ Type) (int, bool) {
|
||||
// is not set), MissingMethod only checks that methods of T which are also
|
||||
// present in V have matching types (e.g., for a type assertion x.(T) where
|
||||
// x is of interface type V).
|
||||
//
|
||||
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
|
||||
m, alt := (*Checker)(nil).missingMethod(V, T, static)
|
||||
// Only report a wrong type if the alternative method has the same name as m.
|
||||
|
||||
@@ -98,10 +98,10 @@ func (t *Named) cleanup() {
|
||||
}
|
||||
|
||||
// Obj returns the type name for the declaration defining the named type t. For
|
||||
// instantiated types, this is the type name of the base type.
|
||||
// instantiated types, this is same as the type name of the origin type.
|
||||
func (t *Named) Obj() *TypeName { return t.orig.obj } // for non-instances this is the same as t.obj
|
||||
|
||||
// Origin returns the parameterized type from which the named type t is
|
||||
// Origin returns the generic type from which the named type t is
|
||||
// instantiated. If t is not an instantiated type, the result is t.
|
||||
func (t *Named) Origin() *Named { return t.orig }
|
||||
|
||||
@@ -109,7 +109,7 @@ func (t *Named) Origin() *Named { return t.orig }
|
||||
// between parameterized instantiated and non-instantiated types.
|
||||
|
||||
// TypeParams returns the type parameters of the named type t, or nil.
|
||||
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
|
||||
// The result is non-nil for an (originally) generic type even if it is instantiated.
|
||||
func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
|
||||
|
||||
// SetTypeParams sets the type parameters of the named type t.
|
||||
@@ -122,7 +122,11 @@ func (t *Named) SetTypeParams(tparams []*TypeParam) {
|
||||
// TypeArgs returns the type arguments used to instantiate the named type t.
|
||||
func (t *Named) TypeArgs() *TypeList { return t.targs }
|
||||
|
||||
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||
// NumMethods returns the number of explicit methods defined for t.
|
||||
//
|
||||
// For an ordinary or instantiated type t, the receiver base type of these
|
||||
// methods will be the named type t. For an uninstantiated generic type t, each
|
||||
// method receiver will be instantiated with its receiver type parameters.
|
||||
func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() }
|
||||
|
||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||
@@ -186,7 +190,7 @@ func (t *Named) instantiateMethod(i int) *Func {
|
||||
rtyp = t
|
||||
}
|
||||
|
||||
sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
|
||||
sig.recv = substVar(origSig.recv, rtyp)
|
||||
return NewFunc(origm.pos, origm.pkg, origm.name, sig)
|
||||
}
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ func (check *Checker) collectObjects() {
|
||||
|
||||
case *syntax.ConstDecl:
|
||||
// iota is the index of the current constDecl within the group
|
||||
if first < 0 || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {
|
||||
if first < 0 || s.Group == nil || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {
|
||||
first = index
|
||||
last = nil
|
||||
}
|
||||
@@ -413,7 +413,7 @@ func (check *Checker) collectObjects() {
|
||||
|
||||
case *syntax.TypeDecl:
|
||||
if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) {
|
||||
check.softErrorf(s.TParamList[0], "type parameters require go1.18 or later")
|
||||
check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
|
||||
}
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
|
||||
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
|
||||
@@ -458,7 +458,7 @@ func (check *Checker) collectObjects() {
|
||||
check.recordDef(s.Name, obj)
|
||||
}
|
||||
if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
|
||||
check.softErrorf(s.TParamList[0], "type parameters require go1.18 or later")
|
||||
check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
|
||||
}
|
||||
info := &declInfo{file: fileScope, fdecl: s}
|
||||
// Methods are not package-level objects but we still track them in the
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
package types2
|
||||
|
||||
import "cmd/compile/internal/syntax"
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// API
|
||||
@@ -28,16 +31,18 @@ type Signature struct {
|
||||
// NewSignatureType creates a new function type for the given receiver,
|
||||
// receiver type parameters, type parameters, parameters, and results. If
|
||||
// variadic is set, params must hold at least one parameter and the last
|
||||
// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
|
||||
// be empty. If recvTypeParams is non-empty, recv must be non-nil.
|
||||
// parameter's core type must be of unnamed slice or bytestring type.
|
||||
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
|
||||
// non-empty, recv must be non-nil.
|
||||
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
|
||||
if variadic {
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
panic("variadic function must have at least one parameter")
|
||||
}
|
||||
if _, ok := params.At(n - 1).typ.(*Slice); !ok {
|
||||
panic("variadic parameter must be of unnamed slice type")
|
||||
core := coreString(params.At(n - 1).typ)
|
||||
if _, ok := core.(*Slice); !ok && !isString(core) {
|
||||
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as core type", core.String()))
|
||||
}
|
||||
}
|
||||
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
|
||||
|
||||
@@ -626,14 +626,15 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
|
||||
|
||||
case *syntax.ForStmt:
|
||||
inner |= breakOk | continueOk
|
||||
check.openScope(s, "for")
|
||||
defer check.closeScope()
|
||||
|
||||
if rclause, _ := s.Init.(*syntax.RangeClause); rclause != nil {
|
||||
check.rangeStmt(inner, s, rclause)
|
||||
break
|
||||
}
|
||||
|
||||
check.openScope(s, "for")
|
||||
defer check.closeScope()
|
||||
|
||||
check.simpleStmt(s.Init)
|
||||
if s.Cond != nil {
|
||||
var x operand
|
||||
@@ -809,8 +810,6 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
|
||||
}
|
||||
|
||||
func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *syntax.RangeClause) {
|
||||
// scope already opened
|
||||
|
||||
// determine lhs, if any
|
||||
sKey := rclause.Lhs // possibly nil
|
||||
var sValue, sExtra syntax.Expr
|
||||
@@ -866,6 +865,11 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
||||
}
|
||||
}
|
||||
|
||||
// Open the for-statement block scope now, after the range clause.
|
||||
// Iteration variables declared with := need to go in this scope (was issue #51437).
|
||||
check.openScope(s, "range")
|
||||
defer check.closeScope()
|
||||
|
||||
// check assignment to/declaration of iteration variables
|
||||
// (irregular assignment, cannot easily map to existing assignment checks)
|
||||
|
||||
@@ -874,9 +878,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
||||
rhs := [2]Type{key, val} // key, val may be nil
|
||||
|
||||
if rclause.Def {
|
||||
// short variable declaration; variable scope starts after the range clause
|
||||
// (the for loop opens a new scope, so variables on the lhs never redeclare
|
||||
// previously declared variables)
|
||||
// short variable declaration
|
||||
var vars []*Var
|
||||
for i, lhs := range lhs {
|
||||
if lhs == nil {
|
||||
@@ -913,12 +915,8 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
||||
|
||||
// declare variables
|
||||
if len(vars) > 0 {
|
||||
scopePos := syntax.EndPos(rclause.X) // TODO(gri) should this just be s.Body.Pos (spec clarification)?
|
||||
scopePos := s.Body.Pos()
|
||||
for _, obj := range vars {
|
||||
// spec: "The scope of a constant or variable identifier declared inside
|
||||
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
|
||||
// for short variable declarations) and ends at the end of the innermost
|
||||
// containing block."
|
||||
check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -290,14 +290,18 @@ func (subst *subster) typOrNil(typ Type) Type {
|
||||
func (subst *subster) var_(v *Var) *Var {
|
||||
if v != nil {
|
||||
if typ := subst.typ(v.typ); typ != v.typ {
|
||||
copy := *v
|
||||
copy.typ = typ
|
||||
return ©
|
||||
return substVar(v, typ)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func substVar(v *Var, typ Type) *Var {
|
||||
copy := *v
|
||||
copy.typ = typ
|
||||
return ©
|
||||
}
|
||||
|
||||
func (subst *subster) tuple(t *Tuple) *Tuple {
|
||||
if t != nil {
|
||||
if vars, copied := subst.varList(t.vars); copied {
|
||||
@@ -410,9 +414,8 @@ func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
|
||||
copied = true
|
||||
}
|
||||
newsig := *sig
|
||||
sig = &newsig
|
||||
sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new)
|
||||
out[i] = NewFunc(method.pos, method.pkg, method.name, sig)
|
||||
newsig.recv = substVar(sig.recv, new)
|
||||
out[i] = NewFunc(method.pos, method.pkg, method.name, &newsig)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -349,6 +349,25 @@ const _ = unsafe.Sizeof(func() {
|
||||
assert(iota == 0)
|
||||
})
|
||||
|
||||
// issue #52438
|
||||
const i1 = iota
|
||||
const i2 = iota
|
||||
const i3 = iota
|
||||
|
||||
func _() {
|
||||
assert(i1 == 0)
|
||||
assert(i2 == 0)
|
||||
assert(i3 == 0)
|
||||
|
||||
const i4 = iota
|
||||
const i5 = iota
|
||||
const i6 = iota
|
||||
|
||||
assert(i4 == 0)
|
||||
assert(i5 == 0)
|
||||
assert(i6 == 0)
|
||||
}
|
||||
|
||||
// untyped constants must not get arbitrarily large
|
||||
const prec = 512 // internal maximum precision for integers
|
||||
const maxInt = (1<<(prec/2) - 1) * (1<<(prec/2) + 1) // == 1<<prec - 1
|
||||
|
||||
@@ -8,21 +8,21 @@ import "strconv"
|
||||
|
||||
type any interface{}
|
||||
|
||||
func f0[A any, B interface{~*C}, C interface{~*D}, D interface{~*A}](A, B, C, D) {}
|
||||
func f0[A any, B interface{*C}, C interface{*D}, D interface{*A}](A, B, C, D) {}
|
||||
func _() {
|
||||
f := f0[string]
|
||||
f("a", nil, nil, nil)
|
||||
f0("a", nil, nil, nil)
|
||||
}
|
||||
|
||||
func f1[A any, B interface{~*A}](A, B) {}
|
||||
func f1[A any, B interface{*A}](A, B) {}
|
||||
func _() {
|
||||
f := f1[int]
|
||||
f(int(0), new(int))
|
||||
f1(int(0), new(int))
|
||||
}
|
||||
|
||||
func f2[A any, B interface{~[]A}](A, B) {}
|
||||
func f2[A any, B interface{[]A}](A, B) {}
|
||||
func _() {
|
||||
f := f2[byte]
|
||||
f(byte(0), []byte{})
|
||||
@@ -38,7 +38,7 @@ func _() {
|
||||
// f3(x, &x, &x)
|
||||
// }
|
||||
|
||||
func f4[A any, B interface{~[]C}, C interface{~*A}](A, B, C) {}
|
||||
func f4[A any, B interface{[]C}, C interface{*A}](A, B, C) {}
|
||||
func _() {
|
||||
f := f4[int]
|
||||
var x int
|
||||
@@ -46,7 +46,7 @@ func _() {
|
||||
f4(x, []*int{}, &x)
|
||||
}
|
||||
|
||||
func f5[A interface{~struct{b B; c C}}, B any, C interface{~*B}](x B) A { panic(0) }
|
||||
func f5[A interface{struct{b B; c C}}, B any, C interface{*B}](x B) A { panic(0) }
|
||||
func _() {
|
||||
x := f5(1.2)
|
||||
var _ float64 = x.b
|
||||
@@ -79,14 +79,14 @@ var _ = Double(MySlice{1})
|
||||
|
||||
type Setter[B any] interface {
|
||||
Set(string)
|
||||
~*B
|
||||
*B
|
||||
}
|
||||
|
||||
func FromStrings[T interface{}, PT Setter[T]](s []string) []T {
|
||||
result := make([]T, len(s))
|
||||
for i, v := range s {
|
||||
// The type of &result[i] is *T which is in the type list
|
||||
// of Setter2, so we can convert it to PT.
|
||||
// of Setter, so we can convert it to PT.
|
||||
p := PT(&result[i])
|
||||
// PT has a Set method.
|
||||
p.Set(v)
|
||||
|
||||
@@ -4,44 +4,46 @@
|
||||
|
||||
package typeInference
|
||||
|
||||
// As of issue #51527, type-type inference has been disabled.
|
||||
|
||||
// basic inference
|
||||
type Tb[P ~*Q, Q any] int
|
||||
func _() {
|
||||
var x Tb[*int]
|
||||
var x Tb /* ERROR got 1 arguments */ [*int]
|
||||
var y Tb[*int, int]
|
||||
x = y
|
||||
x = y /* ERROR cannot use y .* in assignment */
|
||||
_ = x
|
||||
}
|
||||
|
||||
// recursive inference
|
||||
type Tr[A any, B ~*C, C ~*D, D ~*A] int
|
||||
type Tr[A any, B *C, C *D, D *A] int
|
||||
func _() {
|
||||
var x Tr[string]
|
||||
var x Tr /* ERROR got 1 arguments */ [string]
|
||||
var y Tr[string, ***string, **string, *string]
|
||||
var z Tr[int, ***int, **int, *int]
|
||||
x = y
|
||||
x = y /* ERROR cannot use y .* in assignment */
|
||||
x = z // ERROR cannot use z .* as Tr
|
||||
_ = x
|
||||
}
|
||||
|
||||
// other patterns of inference
|
||||
type To0[A any, B ~[]A] int
|
||||
type To1[A any, B ~struct{a A}] int
|
||||
type To2[A any, B ~[][]A] int
|
||||
type To3[A any, B ~[3]*A] int
|
||||
type To4[A any, B any, C ~struct{a A; b B}] int
|
||||
type To0[A any, B []A] int
|
||||
type To1[A any, B struct{a A}] int
|
||||
type To2[A any, B [][]A] int
|
||||
type To3[A any, B [3]*A] int
|
||||
type To4[A any, B any, C struct{a A; b B}] int
|
||||
func _() {
|
||||
var _ To0[int]
|
||||
var _ To1[int]
|
||||
var _ To2[int]
|
||||
var _ To3[int]
|
||||
var _ To4[int, string]
|
||||
var _ To0 /* ERROR got 1 arguments */ [int]
|
||||
var _ To1 /* ERROR got 1 arguments */ [int]
|
||||
var _ To2 /* ERROR got 1 arguments */ [int]
|
||||
var _ To3 /* ERROR got 1 arguments */ [int]
|
||||
var _ To4 /* ERROR got 2 arguments */ [int, string]
|
||||
}
|
||||
|
||||
// failed inference
|
||||
type Tf0[A, B any] int
|
||||
type Tf1[A any, B ~struct{a A; c C}, C any] int
|
||||
func _() {
|
||||
var _ Tf0 /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type parameters */ [int]
|
||||
var _ Tf1 /* ERROR cannot infer B */ /* ERROR got 1 arguments but 3 type parameters */ [int]
|
||||
var _ Tf0 /* ERROR got 1 arguments but 2 type parameters */ [int]
|
||||
var _ Tf1 /* ERROR got 1 arguments but 3 type parameters */ [int]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ type (
|
||||
_ interface{int|any}
|
||||
_ interface{int|~string|union}
|
||||
_ interface{int|~string|interface{int}}
|
||||
_ interface{union|union /* ERROR overlapping terms p.union and p.union */ }
|
||||
_ interface{union|int} // interfaces (here: union) are ignored when checking for overlap
|
||||
_ interface{union|union} // ditto
|
||||
|
||||
// For now we do not permit interfaces with methods in unions.
|
||||
_ interface{~ /* ERROR invalid use of ~ */ any}
|
||||
|
||||
@@ -78,7 +78,7 @@ func _() {
|
||||
related1(si, "foo" /* ERROR cannot use "foo" */ )
|
||||
}
|
||||
|
||||
func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {}
|
||||
func related2[Elem any, Slice interface{[]Elem}](e Elem, s Slice) {}
|
||||
|
||||
func _() {
|
||||
// related2 can be called with explicit instantiation.
|
||||
@@ -109,16 +109,8 @@ func _() {
|
||||
related3[int, []int]()
|
||||
related3[byte, List[byte]]()
|
||||
|
||||
// Alternatively, the 2nd type argument can be inferred
|
||||
// from the first one through constraint type inference.
|
||||
related3[int]()
|
||||
|
||||
// The inferred type is the core type of the Slice
|
||||
// type parameter.
|
||||
var _ []int = related3[int]()
|
||||
|
||||
// It is not the defined parameterized type List.
|
||||
type anotherList []float32
|
||||
var _ anotherList = related3[float32]() // valid
|
||||
var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
|
||||
// The 2nd type argument cannot be inferred from the first
|
||||
// one because there's two possible choices: []Elem and
|
||||
// List[Elem].
|
||||
related3[int]( /* ERROR cannot infer Slice */ )
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func _() int {
|
||||
return deref(p)
|
||||
}
|
||||
|
||||
func addrOfCopy[V any, P ~*V](v V) P {
|
||||
func addrOfCopy[V any, P *V](v V) P {
|
||||
return &v
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ type _ struct{
|
||||
}
|
||||
|
||||
type _ struct{
|
||||
I3 // ERROR interface is .* comparable
|
||||
I3 // ERROR interface contains type constraints
|
||||
}
|
||||
|
||||
// General composite types.
|
||||
@@ -59,19 +59,19 @@ type (
|
||||
_ []I1 // ERROR interface is .* comparable
|
||||
_ []I2 // ERROR interface contains type constraints
|
||||
|
||||
_ *I3 // ERROR interface is .* comparable
|
||||
_ *I3 // ERROR interface contains type constraints
|
||||
_ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints
|
||||
_ chan I3 // ERROR interface is .* comparable
|
||||
_ chan I3 // ERROR interface contains type constraints
|
||||
_ func(I1 /* ERROR interface is .* comparable */ )
|
||||
_ func() I2 // ERROR interface contains type constraints
|
||||
)
|
||||
|
||||
// Other cases.
|
||||
|
||||
var _ = [...]I3 /* ERROR interface is .* comparable */ {}
|
||||
var _ = [...]I3 /* ERROR interface contains type constraints */ {}
|
||||
|
||||
func _(x interface{}) {
|
||||
_ = x.(I3 /* ERROR interface is .* comparable */ )
|
||||
_ = x.(I3 /* ERROR interface contains type constraints */ )
|
||||
}
|
||||
|
||||
type T1[_ any] struct{}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
package p
|
||||
|
||||
func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {}
|
||||
func f[F interface{*Q}, G interface{*R}, Q, R any](q Q, r R) {}
|
||||
|
||||
func _() {
|
||||
f[*float64, *int](1, 2)
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
package go1_17
|
||||
|
||||
type T[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ] struct{}
|
||||
type T[P /* ERROR type parameter requires go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ] struct{}
|
||||
|
||||
// for init (and main, but we're not in package main) we should only get one error
|
||||
func init[P /* ERROR func init must have no type parameters */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ]() {}
|
||||
func main[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ]() {}
|
||||
func main[P /* ERROR type parameter requires go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ]() {}
|
||||
|
||||
func f[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ](x P) {
|
||||
func f[P /* ERROR type parameter requires go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ](x P) {
|
||||
var _ T[ /* ERROR type instantiation requires go1\.18 or later */ int]
|
||||
var _ (T[ /* ERROR type instantiation requires go1\.18 or later */ int])
|
||||
_ = T[ /* ERROR type instantiation requires go1\.18 or later */ int]{}
|
||||
|
||||
@@ -10,9 +10,10 @@ type S[A, B any] struct {
|
||||
|
||||
func (S[A, B]) m() {}
|
||||
|
||||
// TODO(gri) We should only report one error below. See issue #50588.
|
||||
// TODO(gri): with type-type inference enabled we should only report one error
|
||||
// below. See issue #50588.
|
||||
|
||||
func _[A any](s S /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type parameters */ [A]) {
|
||||
func _[A any](s S /* ERROR got 1 arguments but 2 type parameters */ [A]) {
|
||||
// we should see no follow-on errors below
|
||||
s.f = 1
|
||||
s.m()
|
||||
@@ -21,7 +22,7 @@ func _[A any](s S /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type
|
||||
// another test case from the issue
|
||||
|
||||
func _() {
|
||||
X(Interface[*F /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type parameters */ [string]](Impl{}))
|
||||
X(Interface[*F /* ERROR got 1 arguments but 2 type parameters */ [string]](Impl{}))
|
||||
}
|
||||
|
||||
func X[Q Qer](fs Interface[Q]) {
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Field accesses through type parameters are disabled
|
||||
// until we have a more thorough understanding of the
|
||||
// implications on the spec. See issue #51576.
|
||||
|
||||
package p
|
||||
|
||||
type Sf struct {
|
||||
@@ -9,13 +13,13 @@ type Sf struct {
|
||||
}
|
||||
|
||||
func f0[P Sf](p P) {
|
||||
_ = p.f
|
||||
p.f = 0
|
||||
_ = p.f // ERROR p\.f undefined
|
||||
p.f /* ERROR p\.f undefined */ = 0
|
||||
}
|
||||
|
||||
func f0t[P ~struct{f int}](p P) {
|
||||
_ = p.f
|
||||
p.f = 0
|
||||
_ = p.f // ERROR p\.f undefined
|
||||
p.f /* ERROR p\.f undefined */ = 0
|
||||
}
|
||||
|
||||
var _ = f0[Sf]
|
||||
@@ -25,8 +29,8 @@ var _ = f0[Sm /* ERROR does not implement */ ]
|
||||
var _ = f0t[Sm /* ERROR does not implement */ ]
|
||||
|
||||
func f1[P interface{ Sf; m() }](p P) {
|
||||
_ = p.f
|
||||
p.f = 0
|
||||
_ = p.f // ERROR p\.f undefined
|
||||
p.f /* ERROR p\.f undefined */ = 0
|
||||
p.m()
|
||||
}
|
||||
|
||||
@@ -44,8 +48,8 @@ type Sfm struct {
|
||||
func (Sfm) m() {}
|
||||
|
||||
func f2[P interface{ Sfm; m() }](p P) {
|
||||
_ = p.f
|
||||
p.f = 0
|
||||
_ = p.f // ERROR p\.f undefined
|
||||
p.f /* ERROR p\.f undefined */ = 0
|
||||
p.m()
|
||||
}
|
||||
|
||||
@@ -56,8 +60,8 @@ var _ = f2[Sfm]
|
||||
type PSfm *Sfm
|
||||
|
||||
func f3[P interface{ PSfm }](p P) {
|
||||
_ = p.f
|
||||
p.f = 0
|
||||
_ = p.f // ERROR p\.f undefined
|
||||
p.f /* ERROR p\.f undefined */ = 0
|
||||
p.m /* ERROR type P has no field or method m */ ()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Field accesses through type parameters are disabled
|
||||
// until we have a more thorough understanding of the
|
||||
// implications on the spec. See issue #51576.
|
||||
|
||||
package p
|
||||
|
||||
// The first example from the issue.
|
||||
@@ -18,9 +22,12 @@ type numericAbs[T Numeric] interface {
|
||||
// AbsDifference computes the absolute value of the difference of
|
||||
// a and b, where the absolute value is determined by the Abs method.
|
||||
func absDifference[T numericAbs[T /* ERROR T does not implement Numeric */]](a, b T) T {
|
||||
// TODO: the error below should probably be positioned on the '-'.
|
||||
d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value
|
||||
return d.Abs()
|
||||
// Field accesses are not permitted for now. Keep an error so
|
||||
// we can find and fix this code once the situation changes.
|
||||
return a.Value // ERROR a\.Value undefined
|
||||
// TODO: The error below should probably be positioned on the '-'.
|
||||
// d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value
|
||||
// return d.Abs()
|
||||
}
|
||||
|
||||
// The second example from the issue.
|
||||
|
||||
@@ -16,7 +16,7 @@ func G[A, B any](F[A, B]) {
|
||||
|
||||
func _() {
|
||||
// TODO(gri) only report one error below (issue #50932)
|
||||
var x F /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type parameters */ [int]
|
||||
var x F /* ERROR got 1 arguments but 2 type parameters */ [int]
|
||||
G(x /* ERROR does not match */)
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ func NSG[G any](c RSC[G]) {
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
||||
func MMD[Rc RC /* ERROR cannot infer RG */ /* ERROR got 1 arguments */ [RG], RG any, G any]() M /* ERROR got 2 arguments */ /* ERROR Rc does not match */ [Rc, RG] {
|
||||
func MMD[Rc RC /* ERROR got 1 arguments */ [RG], RG any, G any]() M /* ERROR got 2 arguments */ [Rc, RG] {
|
||||
|
||||
var nFn NFn /* ERROR got 2 arguments */ /* ERROR Rc does not match */ [Rc, RG]
|
||||
var nFn NFn /* ERROR got 2 arguments */ [Rc, RG]
|
||||
|
||||
var empty Rc
|
||||
switch any(empty).(type) {
|
||||
|
||||
164
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51229.go2
vendored
Normal file
164
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51229.go2
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
// Constraint type inference should be independent of the
|
||||
// ordering of the type parameter declarations. Try all
|
||||
// permutations in the test case below.
|
||||
// Permutations produced by https://go.dev/play/p/PHcZNGJTEBZ.
|
||||
|
||||
func f00[S1 ~[]E1, S2 ~[]E2, E1 ~byte, E2 ~byte](S1, S2) {}
|
||||
func f01[S2 ~[]E2, S1 ~[]E1, E1 ~byte, E2 ~byte](S1, S2) {}
|
||||
func f02[E1 ~byte, S1 ~[]E1, S2 ~[]E2, E2 ~byte](S1, S2) {}
|
||||
func f03[S1 ~[]E1, E1 ~byte, S2 ~[]E2, E2 ~byte](S1, S2) {}
|
||||
func f04[S2 ~[]E2, E1 ~byte, S1 ~[]E1, E2 ~byte](S1, S2) {}
|
||||
func f05[E1 ~byte, S2 ~[]E2, S1 ~[]E1, E2 ~byte](S1, S2) {}
|
||||
func f06[E2 ~byte, S2 ~[]E2, S1 ~[]E1, E1 ~byte](S1, S2) {}
|
||||
func f07[S2 ~[]E2, E2 ~byte, S1 ~[]E1, E1 ~byte](S1, S2) {}
|
||||
func f08[S1 ~[]E1, E2 ~byte, S2 ~[]E2, E1 ~byte](S1, S2) {}
|
||||
func f09[E2 ~byte, S1 ~[]E1, S2 ~[]E2, E1 ~byte](S1, S2) {}
|
||||
func f10[S2 ~[]E2, S1 ~[]E1, E2 ~byte, E1 ~byte](S1, S2) {}
|
||||
func f11[S1 ~[]E1, S2 ~[]E2, E2 ~byte, E1 ~byte](S1, S2) {}
|
||||
func f12[S1 ~[]E1, E1 ~byte, E2 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f13[E1 ~byte, S1 ~[]E1, E2 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f14[E2 ~byte, S1 ~[]E1, E1 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f15[S1 ~[]E1, E2 ~byte, E1 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f16[E1 ~byte, E2 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
|
||||
func f17[E2 ~byte, E1 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
|
||||
func f18[E2 ~byte, E1 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
|
||||
func f19[E1 ~byte, E2 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
|
||||
func f20[S2 ~[]E2, E2 ~byte, E1 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
func f21[E2 ~byte, S2 ~[]E2, E1 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
func f22[E1 ~byte, S2 ~[]E2, E2 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
func f23[S2 ~[]E2, E1 ~byte, E2 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
|
||||
type myByte byte
|
||||
|
||||
func _(a []byte, b []myByte) {
|
||||
f00(a, b)
|
||||
f01(a, b)
|
||||
f02(a, b)
|
||||
f03(a, b)
|
||||
f04(a, b)
|
||||
f05(a, b)
|
||||
f06(a, b)
|
||||
f07(a, b)
|
||||
f08(a, b)
|
||||
f09(a, b)
|
||||
f10(a, b)
|
||||
f11(a, b)
|
||||
f12(a, b)
|
||||
f13(a, b)
|
||||
f14(a, b)
|
||||
f15(a, b)
|
||||
f16(a, b)
|
||||
f17(a, b)
|
||||
f18(a, b)
|
||||
f19(a, b)
|
||||
f20(a, b)
|
||||
f21(a, b)
|
||||
f22(a, b)
|
||||
f23(a, b)
|
||||
}
|
||||
|
||||
// Constraint type inference may have to iterate.
|
||||
// Again, the order of the type parameters shouldn't matter.
|
||||
|
||||
func g0[S ~[]E, M ~map[string]S, E any](m M) {}
|
||||
func g1[M ~map[string]S, S ~[]E, E any](m M) {}
|
||||
func g2[E any, S ~[]E, M ~map[string]S](m M) {}
|
||||
func g3[S ~[]E, E any, M ~map[string]S](m M) {}
|
||||
func g4[M ~map[string]S, E any, S ~[]E](m M) {}
|
||||
func g5[E any, M ~map[string]S, S ~[]E](m M) {}
|
||||
|
||||
func _(m map[string][]byte) {
|
||||
g0(m)
|
||||
g1(m)
|
||||
g2(m)
|
||||
g3(m)
|
||||
g4(m)
|
||||
g5(m)
|
||||
}
|
||||
|
||||
// Worst-case scenario.
|
||||
// There are 10 unknown type parameters. In each iteration of
|
||||
// constraint type inference we infer one more, from right to left.
|
||||
// Each iteration looks repeatedly at all 11 type parameters,
|
||||
// requiring a total of 10*11 = 110 iterations with the current
|
||||
// implementation. Pathological case.
|
||||
|
||||
func h[K any, J ~*K, I ~*J, H ~*I, G ~*H, F ~*G, E ~*F, D ~*E, C ~*D, B ~*C, A ~*B](x A) {}
|
||||
|
||||
func _(x **********int) {
|
||||
h(x)
|
||||
}
|
||||
|
||||
// Examples with channel constraints and tilde.
|
||||
|
||||
func ch1[P chan<- int]() (_ P) { return } // core(P) == chan<- int (single type, no tilde)
|
||||
func ch2[P ~chan int]() { return } // core(P) == ~chan<- int (tilde)
|
||||
func ch3[P chan E, E any](E) { return } // core(P) == chan<- E (single type, no tilde)
|
||||
func ch4[P chan E | ~chan<- E, E any](E) { return } // core(P) == ~chan<- E (tilde)
|
||||
func ch5[P chan int | chan<- int]() { return } // core(P) == chan<- int (not a single type)
|
||||
|
||||
func _() {
|
||||
// P can be inferred as there's a single specific type and no tilde.
|
||||
var _ chan int = ch1 /* ERROR cannot use ch1.*value of type chan<- int */ ()
|
||||
var _ chan<- int = ch1()
|
||||
|
||||
// P cannot be inferred as there's a tilde.
|
||||
ch2( /* ERROR cannot infer P */ )
|
||||
type myChan chan int
|
||||
ch2[myChan]()
|
||||
|
||||
// P can be inferred as there's a single specific type and no tilde.
|
||||
var e int
|
||||
ch3(e)
|
||||
|
||||
// P cannot be inferred as there's more than one specific type and a tilde.
|
||||
ch4( /* ERROR cannot infer P */ e)
|
||||
_ = ch4[chan int]
|
||||
|
||||
// P cannot be inferred as there's more than one specific type.
|
||||
ch5( /* ERROR cannot infer P */ )
|
||||
ch5[chan<- int]()
|
||||
}
|
||||
|
||||
// test case from issue
|
||||
|
||||
func equal[M1 ~map[K1]V1, M2 ~map[K2]V2, K1, K2 ~uint32, V1, V2 ~string](m1 M1, m2 M2) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[K2(k)]; !ok || V2(v1) != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalFixed[K1, K2 ~uint32, V1, V2 ~string](m1 map[K1]V1, m2 map[K2]V2) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[K2(k)]; !ok || v1 != V1(v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type (
|
||||
someNumericID uint32
|
||||
someStringID string
|
||||
)
|
||||
|
||||
func _() {
|
||||
foo := map[uint32]string{10: "bar"}
|
||||
bar := map[someNumericID]someStringID{10: "bar"}
|
||||
equal(foo, bar)
|
||||
}
|
||||
@@ -11,19 +11,20 @@ type RC[RG any] interface {
|
||||
type Fn[RCT RC[RG], RG any] func(RCT)
|
||||
|
||||
type F[RCT RC[RG], RG any] interface {
|
||||
Fn() Fn[RCT]
|
||||
Fn() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
type concreteF[RCT RC[RG], RG any] struct {
|
||||
makeFn func() Fn[RCT]
|
||||
makeFn func() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
|
||||
func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR got 1 arguments */ [RCT] {
|
||||
return c.makeFn()
|
||||
}
|
||||
|
||||
func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] {
|
||||
return &concreteF[RCT]{
|
||||
func NewConcrete[RCT RC[RG], RG any](Rc RCT) F /* ERROR got 1 arguments */ [RCT] {
|
||||
// TODO(rfindley): eliminate the duplicate error below.
|
||||
return & /* ERROR cannot use .* as F\[RCT\] */ concreteF /* ERROR got 1 arguments */ [RCT]{
|
||||
makeFn: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@
|
||||
|
||||
package p
|
||||
|
||||
// As of issue #51527, type-type inference has been disabled.
|
||||
|
||||
type RC[RG any] interface {
|
||||
~[]RG
|
||||
}
|
||||
|
||||
type Fn[RCT RC[RG], RG any] func(RCT)
|
||||
|
||||
type FFn[RCT RC[RG], RG any] func() Fn[RCT]
|
||||
type FFn[RCT RC[RG], RG any] func() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
|
||||
type F[RCT RC[RG], RG any] interface {
|
||||
Fn() Fn[RCT]
|
||||
Fn() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
type concreteF[RCT RC[RG], RG any] struct {
|
||||
makeFn FFn[RCT]
|
||||
makeFn FFn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
|
||||
func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR got 1 arguments */ [RCT] {
|
||||
return c.makeFn()
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ package p
|
||||
type T[P any, B *P] struct{}
|
||||
|
||||
func (T /* ERROR cannot use generic type */ ) m0() {}
|
||||
func (T /* ERROR got 1 type parameter, but receiver base type declares 2 */ [_]) m1() {}
|
||||
|
||||
// TODO(rfindley): eliminate the duplicate errors here.
|
||||
func (T /* ERROR got 1 type parameter, but receiver base type declares 2 */ /* ERROR got 1 arguments but 2 type parameters */ [_]) m1() {}
|
||||
func (T[_, _]) m2() {}
|
||||
// TODO(gri) this error is unfortunate (issue #51343)
|
||||
func (T /* ERROR got 3 arguments but 2 type parameters */ [_, _, _]) m3() {}
|
||||
|
||||
24
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51376.go2
vendored
Normal file
24
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51376.go2
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
type Map map[string]int
|
||||
|
||||
func f[M ~map[K]V, K comparable, V any](M) {}
|
||||
func g[M map[K]V, K comparable, V any](M) {}
|
||||
|
||||
func _[M1 ~map[K]V, M2 map[K]V, K comparable, V any]() {
|
||||
var m1 M1
|
||||
f(m1)
|
||||
g( /* ERROR M1 does not implement map\[K\]V */ m1) // M1 has tilde
|
||||
|
||||
var m2 M2
|
||||
f(m2)
|
||||
g(m2) // M1 does not have tilde
|
||||
|
||||
var m3 Map
|
||||
f(m3)
|
||||
g( /* ERROR Map does not implement map\[string\]int */ m3) // M in g does not have tilde
|
||||
}
|
||||
17
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51437.go
vendored
Normal file
17
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51437.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) m() []int { return nil }
|
||||
|
||||
func f(x T) {
|
||||
for _, x := range func() []int {
|
||||
return x.m() // x declared in parameter list of f
|
||||
}() {
|
||||
_ = x // x declared by range clause
|
||||
}
|
||||
}
|
||||
54
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51472.go2
vendored
Normal file
54
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51472.go2
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
func _[T comparable](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{interface{comparable}}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; interface{comparable}}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; ~int}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; ~[]byte}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
// TODO(gri) The error message here should be better. See issue #51525.
|
||||
func _[T interface{comparable; ~int; ~string}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
// TODO(gri) The error message here should be better. See issue #51525.
|
||||
func _[T interface{~int; ~string}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; interface{~int}; interface{int|float64}}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{interface{comparable; ~int}; interface{~float64; comparable; m()}}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
// test case from issue
|
||||
|
||||
func f[T interface{comparable; []byte|string}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _(s []byte) {
|
||||
f( /* ERROR \[\]byte does not implement interface{comparable; \[\]byte\|string} */ s)
|
||||
_ = f[[ /* ERROR does not implement */ ]byte]
|
||||
}
|
||||
7
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51509.go
vendored
Normal file
7
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51509.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
type T /* ERROR illegal cycle */ T.x
|
||||
17
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51578.go2
vendored
Normal file
17
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51578.go2
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
var _ = (*interface /* ERROR interface contains type constraints */ {int})(nil)
|
||||
|
||||
// abbreviated test case from issue
|
||||
|
||||
type TypeSet interface{ int | string }
|
||||
|
||||
func _() {
|
||||
f((*TypeSet /* ERROR interface contains type constraints */)(nil))
|
||||
}
|
||||
|
||||
func f(any) {}
|
||||
13
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51593.go2
vendored
Normal file
13
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51593.go2
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
func f[P interface{ m(R) }, R any]() {}
|
||||
|
||||
type T = interface { m(int) }
|
||||
|
||||
func _() {
|
||||
_ = f[ /* ERROR cannot infer R */ T] // don't crash in type inference
|
||||
}
|
||||
65
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51607.go2
vendored
Normal file
65
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51607.go2
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
// Interface types must be ignored during overlap test.
|
||||
|
||||
type (
|
||||
T1 interface{int}
|
||||
T2 interface{~int}
|
||||
T3 interface{T1 | bool | string}
|
||||
T4 interface{T2 | ~bool | ~string}
|
||||
)
|
||||
|
||||
type (
|
||||
// overlap errors for non-interface terms
|
||||
// (like the interface terms, but explicitly inlined)
|
||||
_ interface{int | int /* ERROR overlapping terms int and int */ }
|
||||
_ interface{int | ~ /* ERROR overlapping terms ~int and int */ int}
|
||||
_ interface{~int | int /* ERROR overlapping terms int and ~int */ }
|
||||
_ interface{~int | ~ /* ERROR overlapping terms ~int and ~int */ int}
|
||||
|
||||
_ interface{T1 | bool | string | T1 | bool /* ERROR overlapping terms bool and bool */ | string /* ERROR overlapping terms string and string */ }
|
||||
_ interface{T1 | bool | string | T2 | ~ /* ERROR overlapping terms ~bool and bool */ bool | ~ /* ERROR overlapping terms ~string and string */ string}
|
||||
|
||||
// no errors for interface terms
|
||||
_ interface{T1 | T1}
|
||||
_ interface{T1 | T2}
|
||||
_ interface{T2 | T1}
|
||||
_ interface{T2 | T2}
|
||||
|
||||
_ interface{T3 | T3 | int}
|
||||
_ interface{T3 | T4 | bool }
|
||||
_ interface{T4 | T3 | string }
|
||||
_ interface{T4 | T4 | float64 }
|
||||
)
|
||||
|
||||
func _[_ T1 | bool | string | T1 | bool /* ERROR overlapping terms */ ]() {}
|
||||
func _[_ T1 | bool | string | T2 | ~ /* ERROR overlapping terms */ bool ]() {}
|
||||
func _[_ T2 | ~bool | ~string | T1 | bool /* ERROR overlapping terms */ ]() {}
|
||||
func _[_ T2 | ~bool | ~string | T2 | ~ /* ERROR overlapping terms */ bool ]() {}
|
||||
|
||||
func _[_ T3 | T3 | int]() {}
|
||||
func _[_ T3 | T4 | bool]() {}
|
||||
func _[_ T4 | T3 | string]() {}
|
||||
func _[_ T4 | T4 | float64]() {}
|
||||
|
||||
// test cases from issue
|
||||
|
||||
type _ interface {
|
||||
interface {bool | int} | interface {bool | string}
|
||||
}
|
||||
|
||||
type _ interface {
|
||||
interface {bool | int} ; interface {bool | string}
|
||||
}
|
||||
|
||||
type _ interface {
|
||||
interface {bool; int} ; interface {bool; string}
|
||||
}
|
||||
|
||||
type _ interface {
|
||||
interface {bool; int} | interface {bool; string}
|
||||
}
|
||||
39
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51658.go2
vendored
Normal file
39
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51658.go2
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
type F { // ERROR syntax error
|
||||
float64
|
||||
} // ERROR syntax error
|
||||
|
||||
func _[T F | int](x T) {
|
||||
_ = x == 0 // don't crash when recording type of 0
|
||||
}
|
||||
|
||||
// test case from issue
|
||||
|
||||
type FloatType { // ERROR syntax error
|
||||
float32 | float64
|
||||
} // ERROR syntax error
|
||||
|
||||
type IntegerType interface {
|
||||
int8 | int16 | int32 | int64 | int |
|
||||
uint8 | uint16 | uint32 | uint64 | uint
|
||||
}
|
||||
|
||||
type ComplexType interface {
|
||||
complex64 | complex128
|
||||
}
|
||||
|
||||
type Number interface {
|
||||
FloatType | IntegerType | ComplexType
|
||||
}
|
||||
|
||||
func GetDefaultNumber[T Number](value, defaultValue T) T {
|
||||
if value == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
15
src/cmd/compile/internal/types2/testdata/fixedbugs/issue52529.go2
vendored
Normal file
15
src/cmd/compile/internal/types2/testdata/fixedbugs/issue52529.go2
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
type Foo[P any] struct {
|
||||
_ *Bar[P]
|
||||
}
|
||||
|
||||
type Bar[Q any] Foo[Q]
|
||||
|
||||
func (v *Bar[R]) M() {
|
||||
_ = (*Foo[R])(v)
|
||||
}
|
||||
@@ -31,7 +31,8 @@ func (t *TypeParam) Obj() *TypeName { return t.obj }
|
||||
// or Signature type by calling SetTypeParams. Setting a type parameter on more
|
||||
// than one type will result in a panic.
|
||||
//
|
||||
// The constraint argument can be nil, and set later via SetConstraint.
|
||||
// The constraint argument can be nil, and set later via SetConstraint. If the
|
||||
// constraint is non-nil, it must be fully defined.
|
||||
func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
|
||||
return (*Checker)(nil).newTypeParam(obj, constraint)
|
||||
}
|
||||
@@ -71,8 +72,10 @@ func (t *TypeParam) Constraint() Type {
|
||||
|
||||
// SetConstraint sets the type constraint for t.
|
||||
//
|
||||
// SetConstraint should not be called concurrently, but once SetConstraint
|
||||
// returns the receiver t is safe for concurrent use.
|
||||
// It must be called by users of NewTypeParam after the bound's underlying is
|
||||
// fully defined, and before using the type parameter in any way other than to
|
||||
// form other types. Once SetConstraint returns the receiver, t is safe for
|
||||
// concurrent use.
|
||||
func (t *TypeParam) SetConstraint(bound Type) {
|
||||
if bound == nil {
|
||||
panic("nil constraint")
|
||||
|
||||
@@ -15,20 +15,25 @@ import (
|
||||
// API
|
||||
|
||||
// A _TypeSet represents the type set of an interface.
|
||||
// Because of existing language restrictions, methods can be "factored out"
|
||||
// from the terms. The actual type set is the intersection of the type set
|
||||
// implied by the methods and the type set described by the terms and the
|
||||
// comparable bit. To test whether a type is included in a type set
|
||||
// ("implements" relation), the type must implement all methods _and_ be
|
||||
// an element of the type set described by the terms and the comparable bit.
|
||||
// If the term list describes the set of all types and comparable is true,
|
||||
// only comparable types are meant; in all other cases comparable is false.
|
||||
type _TypeSet struct {
|
||||
comparable bool // if set, the interface is or embeds comparable
|
||||
// TODO(gri) consider using a set for the methods for faster lookup
|
||||
methods []*Func // all methods of the interface; sorted by unique ID
|
||||
terms termlist // type terms of the type set
|
||||
methods []*Func // all methods of the interface; sorted by unique ID
|
||||
terms termlist // type terms of the type set
|
||||
comparable bool // invariant: !comparable || terms.isAll()
|
||||
}
|
||||
|
||||
// IsEmpty reports whether type set s is the empty set.
|
||||
func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() }
|
||||
|
||||
// IsAll reports whether type set s is the set of all types (corresponding to the empty interface).
|
||||
func (s *_TypeSet) IsAll() bool {
|
||||
return !s.comparable && len(s.methods) == 0 && s.terms.isAll()
|
||||
}
|
||||
func (s *_TypeSet) IsAll() bool { return s.IsMethodSet() && len(s.methods) == 0 }
|
||||
|
||||
// IsMethodSet reports whether the interface t is fully described by its method set.
|
||||
func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
|
||||
@@ -43,13 +48,6 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one.
|
||||
|
||||
// IsTypeSet reports whether the type set s is represented by a finite set of underlying types.
|
||||
func (s *_TypeSet) IsTypeSet() bool {
|
||||
return !s.comparable && len(s.methods) == 0
|
||||
}
|
||||
|
||||
// NumMethods returns the number of methods available.
|
||||
func (s *_TypeSet) NumMethods() int { return len(s.methods) }
|
||||
|
||||
@@ -215,12 +213,12 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
|
||||
var todo []*Func
|
||||
var seen objset
|
||||
var methods []*Func
|
||||
var allMethods []*Func
|
||||
mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages
|
||||
addMethod := func(pos syntax.Pos, m *Func, explicit bool) {
|
||||
switch other := seen.insert(m); {
|
||||
case other == nil:
|
||||
methods = append(methods, m)
|
||||
allMethods = append(allMethods, m)
|
||||
mpos[m] = pos
|
||||
case explicit:
|
||||
if check == nil {
|
||||
@@ -259,7 +257,8 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
}
|
||||
|
||||
// collect embedded elements
|
||||
var allTerms = allTermlist
|
||||
allTerms := allTermlist
|
||||
allComparable := false
|
||||
for i, typ := range ityp.embeddeds {
|
||||
// The embedding position is nil for imported interfaces
|
||||
// and also for interface copies after substitution (but
|
||||
@@ -268,6 +267,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
if ityp.embedPos != nil {
|
||||
pos = (*ityp.embedPos)[i]
|
||||
}
|
||||
var comparable bool
|
||||
var terms termlist
|
||||
switch u := under(typ).(type) {
|
||||
case *Interface:
|
||||
@@ -279,9 +279,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
check.versionErrorf(pos, "go1.18", "embedding constraint interface %s", typ)
|
||||
continue
|
||||
}
|
||||
if tset.comparable {
|
||||
ityp.tset.comparable = true
|
||||
}
|
||||
comparable = tset.comparable
|
||||
for _, m := range tset.methods {
|
||||
addMethod(pos, m, false) // use embedding position pos rather than m.pos
|
||||
}
|
||||
@@ -295,6 +293,8 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
if tset == &invalidTypeSet {
|
||||
continue // ignore invalid unions
|
||||
}
|
||||
assert(!tset.comparable)
|
||||
assert(len(tset.methods) == 0)
|
||||
terms = tset.terms
|
||||
default:
|
||||
if u == Typ[Invalid] {
|
||||
@@ -306,11 +306,11 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
}
|
||||
terms = termlist{{false, typ}}
|
||||
}
|
||||
// The type set of an interface is the intersection
|
||||
// of the type sets of all its elements.
|
||||
// Intersection cannot produce longer termlists and
|
||||
// thus cannot overflow.
|
||||
allTerms = allTerms.intersect(terms)
|
||||
|
||||
// The type set of an interface is the intersection of the type sets of all its elements.
|
||||
// Due to language restrictions, only embedded interfaces can add methods, they are handled
|
||||
// separately. Here we only need to intersect the term lists and comparable bits.
|
||||
allTerms, allComparable = intersectTermLists(allTerms, allComparable, terms, comparable)
|
||||
}
|
||||
ityp.embedPos = nil // not needed anymore (errors have been reported)
|
||||
|
||||
@@ -323,15 +323,46 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
||||
}
|
||||
}
|
||||
|
||||
if methods != nil {
|
||||
sortMethods(methods)
|
||||
ityp.tset.methods = methods
|
||||
ityp.tset.comparable = allComparable
|
||||
if len(allMethods) != 0 {
|
||||
sortMethods(allMethods)
|
||||
ityp.tset.methods = allMethods
|
||||
}
|
||||
ityp.tset.terms = allTerms
|
||||
|
||||
return ityp.tset
|
||||
}
|
||||
|
||||
// TODO(gri) The intersectTermLists function belongs to the termlist implementation.
|
||||
// The comparable type set may also be best represented as a term (using
|
||||
// a special type).
|
||||
|
||||
// intersectTermLists computes the intersection of two term lists and respective comparable bits.
|
||||
// xcomp, ycomp are valid only if xterms.isAll() and yterms.isAll() respectively.
|
||||
func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool) (termlist, bool) {
|
||||
terms := xterms.intersect(yterms)
|
||||
// If one of xterms or yterms is marked as comparable,
|
||||
// the result must only include comparable types.
|
||||
comp := xcomp || ycomp
|
||||
if comp && !terms.isAll() {
|
||||
// only keep comparable terms
|
||||
i := 0
|
||||
for _, t := range terms {
|
||||
assert(t.typ != nil)
|
||||
if Comparable(t.typ) {
|
||||
terms[i] = t
|
||||
i++
|
||||
}
|
||||
}
|
||||
terms = terms[:i]
|
||||
if !terms.isAll() {
|
||||
comp = false
|
||||
}
|
||||
}
|
||||
assert(!comp || terms.isAll()) // comparable invariant
|
||||
return terms, comp
|
||||
}
|
||||
|
||||
func sortMethods(list []*Func) {
|
||||
sort.Sort(byUniqueMethodName(list))
|
||||
}
|
||||
@@ -375,7 +406,7 @@ func computeUnionTypeSet(check *Checker, unionSets map[*Union]*_TypeSet, pos syn
|
||||
// For now we don't permit type parameters as constraints.
|
||||
assert(!isTypeParam(t.typ))
|
||||
terms = computeInterfaceTypeSet(check, pos, ui).terms
|
||||
} else if t.typ == Typ[Invalid] {
|
||||
} else if u == Typ[Invalid] {
|
||||
continue
|
||||
} else {
|
||||
if t.tilde && !Identical(t.typ, u) {
|
||||
|
||||
@@ -25,9 +25,9 @@ func TestTypeSetString(t *testing.T) {
|
||||
"{int; string}": "∅",
|
||||
|
||||
"{comparable}": "{comparable}",
|
||||
"{comparable; int}": "{comparable; int}",
|
||||
"{~int; comparable}": "{comparable; ~int}",
|
||||
"{int|string; comparable}": "{comparable; int ∪ string}",
|
||||
"{comparable; int}": "{int}",
|
||||
"{~int; comparable}": "{~int}",
|
||||
"{int|string; comparable}": "{int ∪ string}",
|
||||
"{comparable; int; string}": "∅",
|
||||
|
||||
"{m()}": "{func (p.T).m()}",
|
||||
@@ -37,8 +37,8 @@ func TestTypeSetString(t *testing.T) {
|
||||
"{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}",
|
||||
"{comparable; error}": "{comparable; func (error).Error() string}",
|
||||
|
||||
"{m(); comparable; int|float32|string}": "{comparable; func (p.T).m(); int ∪ float32 ∪ string}",
|
||||
"{m1(); int; m2(); comparable }": "{comparable; func (p.T).m1(); func (p.T).m2(); int}",
|
||||
"{m(); comparable; int|float32|string}": "{func (p.T).m(); int ∪ float32 ∪ string}",
|
||||
"{m1(); int; m2(); comparable }": "{func (p.T).m1(); func (p.T).m2(); int}",
|
||||
|
||||
"{E}; type E interface{}": "𝓤",
|
||||
"{E}; type E interface{int;string}": "∅",
|
||||
|
||||
@@ -147,10 +147,16 @@ func (check *Checker) typ(e syntax.Expr) Type {
|
||||
// constraint interface.
|
||||
func (check *Checker) varType(e syntax.Expr) Type {
|
||||
typ := check.definedType(e, nil)
|
||||
check.validVarType(e, typ)
|
||||
return typ
|
||||
}
|
||||
|
||||
// validVarType reports an error if typ is a constraint interface.
|
||||
// The expression e is used for error reporting, if any.
|
||||
func (check *Checker) validVarType(e syntax.Expr, typ Type) {
|
||||
// If we have a type parameter there's nothing to do.
|
||||
if isTypeParam(typ) {
|
||||
return typ
|
||||
return
|
||||
}
|
||||
|
||||
// We don't want to call under() or complete interfaces while we are in
|
||||
@@ -169,8 +175,6 @@ func (check *Checker) varType(e syntax.Expr) Type {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return typ
|
||||
}
|
||||
|
||||
// definedType is like typ but also accepts a type name def.
|
||||
@@ -256,7 +260,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
|
||||
|
||||
case *syntax.SelectorExpr:
|
||||
var x operand
|
||||
check.selector(&x, e)
|
||||
check.selector(&x, e, def)
|
||||
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
@@ -430,10 +434,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
||||
// evaluate arguments
|
||||
targs := check.typeList(xlist)
|
||||
if targs == nil {
|
||||
def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
|
||||
def.setUnderlying(Typ[Invalid]) // avoid errors later due to lazy instantiation
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
// enableTypeTypeInference controls whether to infer missing type arguments
|
||||
// using constraint type inference. See issue #51527.
|
||||
const enableTypeTypeInference = false
|
||||
|
||||
// create the instance
|
||||
ctxt := check.bestContext(nil)
|
||||
h := ctxt.instanceHash(orig, targs)
|
||||
@@ -453,19 +461,18 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
||||
def.setUnderlying(inst)
|
||||
|
||||
inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
|
||||
tparams := orig.TypeParams().list()
|
||||
tparams := n.orig.TypeParams().list()
|
||||
|
||||
inferred := targs
|
||||
if len(targs) < len(tparams) {
|
||||
targs := n.targs.list()
|
||||
if enableTypeTypeInference && len(targs) < len(tparams) {
|
||||
// If inference fails, len(inferred) will be 0, and inst.underlying will
|
||||
// be set to Typ[Invalid] in expandNamed.
|
||||
inferred = check.infer(x.Pos(), tparams, targs, nil, nil)
|
||||
inferred := check.infer(x.Pos(), tparams, targs, nil, nil)
|
||||
if len(inferred) > len(targs) {
|
||||
inst.targs = newTypeList(inferred)
|
||||
n.targs = newTypeList(inferred)
|
||||
}
|
||||
}
|
||||
|
||||
check.recordInstance(x, inferred, inst)
|
||||
return expandNamed(ctxt, n, x.Pos())
|
||||
}
|
||||
|
||||
@@ -478,6 +485,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
|
||||
// Since check is non-nil, we can still mutate inst. Unpinning the resolver
|
||||
// frees some memory.
|
||||
inst.resolver = nil
|
||||
check.recordInstance(x, inst.TypeArgs().list(), inst)
|
||||
|
||||
if check.validateTArgLen(x.Pos(), inst.tparams.Len(), inst.targs.Len()) {
|
||||
if i, err := check.verify(x.Pos(), inst.tparams.list(), inst.targs.list()); err != nil {
|
||||
|
||||
@@ -247,6 +247,17 @@ func (d *tparamsList) set(i int, typ Type) {
|
||||
}
|
||||
}
|
||||
|
||||
// unknowns returns the number of type parameters for which no type has been set yet.
|
||||
func (d *tparamsList) unknowns() int {
|
||||
n := 0
|
||||
for _, ti := range d.indices {
|
||||
if ti <= 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// types returns the list of inferred types (via unification) for the type parameters
|
||||
// described by d, and an index. If all types were inferred, the returned index is < 0.
|
||||
// Otherwise, it is the index of the first type parameter which couldn't be inferred;
|
||||
@@ -349,12 +360,16 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
|
||||
if enableCoreTypeUnification && !u.exact {
|
||||
if isTypeParam(x) && !hasName(y) {
|
||||
// When considering the type parameter for unification
|
||||
// we look at the adjusted core type (adjCoreType).
|
||||
// we look at the adjusted core term (adjusted core type
|
||||
// with tilde information).
|
||||
// If the adjusted core type is a named type N; the
|
||||
// corresponding core type is under(N). Since !u.exact
|
||||
// and y doesn't have a name, unification will end up
|
||||
// comparing under(N) to y, so we can just use the core
|
||||
// type instead. Optimization.
|
||||
// type instead. And we can ignore the tilde because we
|
||||
// already look at the underlying types on both sides
|
||||
// and we have known types on both sides.
|
||||
// Optimization.
|
||||
if cx := coreType(x); cx != nil {
|
||||
if traceInference {
|
||||
u.tracef("core %s ≡ %s", x, y)
|
||||
|
||||
@@ -100,25 +100,25 @@ func parseUnion(check *Checker, uexpr syntax.Expr) Type {
|
||||
|
||||
if !Identical(u, t.typ) {
|
||||
check.errorf(tlist[i], "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
|
||||
continue // don't report another error for t
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Stand-alone embedded interfaces are ok and are handled by the single-type case
|
||||
// in the beginning. Embedded interfaces with tilde are excluded above. If we reach
|
||||
// here, we must have at least two terms in the union.
|
||||
if f != nil && !f.typeSet().IsTypeSet() {
|
||||
// here, we must have at least two terms in the syntactic term list (but not necessarily
|
||||
// in the term list of the union's type set).
|
||||
if f != nil {
|
||||
tset := f.typeSet()
|
||||
switch {
|
||||
case f.typeSet().NumMethods() != 0:
|
||||
case tset.NumMethods() != 0:
|
||||
check.errorf(tlist[i], "cannot use %s in union (%s contains methods)", t, t)
|
||||
case t.typ == universeComparable.Type():
|
||||
check.error(tlist[i], "cannot use comparable in union")
|
||||
case f.typeSet().comparable:
|
||||
case tset.comparable:
|
||||
check.errorf(tlist[i], "cannot use %s in union (%s embeds comparable)", t, t)
|
||||
default:
|
||||
panic("not a type set but no methods and not comparable")
|
||||
}
|
||||
continue // don't report another error for t
|
||||
continue // terms with interface types are not subject to the no-overlap rule
|
||||
}
|
||||
|
||||
// Report overlapping (non-disjoint) terms such as
|
||||
@@ -158,10 +158,16 @@ func parseTilde(check *Checker, tx syntax.Expr) *Term {
|
||||
|
||||
// overlappingTerm reports the index of the term x in terms which is
|
||||
// overlapping (not disjoint) from y. The result is < 0 if there is no
|
||||
// such term.
|
||||
// such term. The type of term y must not be an interface, and terms
|
||||
// with an interface type are ignored in the terms list.
|
||||
func overlappingTerm(terms []*Term, y *Term) int {
|
||||
assert(!IsInterface(y.typ))
|
||||
for i, x := range terms {
|
||||
// disjoint requires non-nil, non-top arguments
|
||||
if IsInterface(x.typ) {
|
||||
continue
|
||||
}
|
||||
// disjoint requires non-nil, non-top arguments,
|
||||
// and non-interface types as term types.
|
||||
if debug {
|
||||
if x == nil || x.typ == nil || y == nil || y.typ == nil {
|
||||
panic("empty or top union term")
|
||||
|
||||
@@ -111,7 +111,7 @@ func defPredeclaredTypes() {
|
||||
typ := NewNamed(obj, nil, nil)
|
||||
|
||||
// interface{} // marked as comparable
|
||||
ityp := &Interface{obj: obj, complete: true, tset: &_TypeSet{true, nil, allTermlist}}
|
||||
ityp := &Interface{obj: obj, complete: true, tset: &_TypeSet{nil, allTermlist, true}}
|
||||
|
||||
typ.SetUnderlying(ityp)
|
||||
def(obj)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user