mirror of
https://github.com/golang/go.git
synced 2026-01-30 15:42:04 +03:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
|
||||
@@ -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() }
|
||||
@@ -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 {
|
||||
|
||||
@@ -163,4 +163,5 @@ func markHiddenClosureDead(n ir.Node) {
|
||||
if clo.Func.IsHiddenClosure() {
|
||||
clo.Func.SetIsDeadcodeClosure(true)
|
||||
}
|
||||
ir.VisitList(clo.Func.Body, markHiddenClosureDead)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
@@ -543,8 +542,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 +642,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 +896,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 +1177,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().IsInterface() && 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().IsInterface() && x.Type().HasShape() {
|
||||
c.List[i] = assignconvfn(x, types.Types[types.TINTER])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -1608,7 +1626,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 +1705,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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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,7 +229,7 @@ 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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
@@ -1697,7 +1718,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 +2288,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
|
||||
|
||||
@@ -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,10 +15,10 @@ 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
|
||||
|
||||
@@ -70,7 +70,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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -235,15 +235,7 @@ func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
|
||||
saveLineNo := base.Pos
|
||||
ir.CurFunc = nil
|
||||
|
||||
// Set line number equal to the line number where the method is declared.
|
||||
if pos := dot.Selection.Pos; pos.IsKnown() {
|
||||
base.Pos = pos
|
||||
}
|
||||
// Note: !dot.Selection.Pos.IsKnown() happens for method expressions where
|
||||
// the method is implicitly declared. The Error method of the
|
||||
// built-in error type is one such method. We leave the line
|
||||
// number at the use of the method expression in this
|
||||
// case. See issue 29389.
|
||||
base.Pos = base.AutogeneratedPos
|
||||
|
||||
tfn := ir.NewFuncType(base.Pos, nil,
|
||||
typecheck.NewFuncParams(t0.Params(), true),
|
||||
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
golang.org/x/mod v0.6.0-dev.0.20211102181907-3a5865c02020
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/tools v0.1.9-0.20220124164225-97de9ec46646
|
||||
golang.org/x/tools v0.1.9-0.20220329150752-294080fd2f5a
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -18,7 +18,7 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7q
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/tools v0.1.9-0.20220124164225-97de9ec46646 h1:f8aekWvlQQ8ZhD8SL7lOu18dtWslZYl029PN2F0VnS4=
|
||||
golang.org/x/tools v0.1.9-0.20220124164225-97de9ec46646/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.9-0.20220329150752-294080fd2f5a h1:IlVBSvpiWIkpqADzaiismkf1xcqsZjBDIXzO+7kZEpY=
|
||||
golang.org/x/tools v0.1.9-0.20220329150752-294080fd2f5a/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -1356,7 +1356,7 @@
|
||||
//
|
||||
// Workspace maintenance
|
||||
//
|
||||
// Go workspace provides access to operations on workspaces.
|
||||
// Work provides access to operations on workspaces.
|
||||
//
|
||||
// Note that support for workspaces is built into many other commands, not
|
||||
// just 'go work'.
|
||||
@@ -1364,6 +1364,12 @@
|
||||
// See 'go help modules' for information about Go's module system of which
|
||||
// workspaces are a part.
|
||||
//
|
||||
// See https://go.dev/ref/mod#workspaces for an in-depth reference on
|
||||
// workspaces.
|
||||
//
|
||||
// See https://go.dev/doc/tutorial/workspaces for an introductory
|
||||
// tutorial on workspaces.
|
||||
//
|
||||
// A workspace is specified by a go.work file that specifies a set of
|
||||
// module directories with the "use" directive. These modules are used as
|
||||
// root modules by the go command for builds and related operations. A
|
||||
@@ -1485,9 +1491,8 @@
|
||||
// Version string
|
||||
// }
|
||||
//
|
||||
// See the workspaces design proposal at
|
||||
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
|
||||
// more information.
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Initialize workspace file
|
||||
@@ -1507,6 +1512,9 @@
|
||||
// Each argument path is added to a use directive in the go.work file. The
|
||||
// current go version will also be listed in the go.work file.
|
||||
//
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Sync workspace build list to modules
|
||||
//
|
||||
@@ -1530,12 +1538,15 @@
|
||||
// build list's version of each module is always the same or higher than
|
||||
// that in each workspace module.
|
||||
//
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Add modules to workspace file
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go work use [-r] [moddirs]
|
||||
// go work use [-r] moddirs
|
||||
//
|
||||
// Use provides a command-line interface for adding
|
||||
// directories, optionally recursively, to a go.work file.
|
||||
@@ -1549,6 +1560,9 @@
|
||||
// were specified as arguments: namely, use directives will be added for
|
||||
// directories that exist, and removed for directories that do not exist.
|
||||
//
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Compile and run Go program
|
||||
//
|
||||
|
||||
@@ -503,6 +503,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
||||
pkgOpts := load.PackageOpts{
|
||||
IgnoreImports: *listFind,
|
||||
ModResolveTests: *listTest,
|
||||
LoadVCS: cfg.BuildBuildvcs,
|
||||
}
|
||||
pkgs := load.PackagesAndErrors(ctx, pkgOpts, args)
|
||||
if !*listE {
|
||||
|
||||
@@ -193,6 +193,18 @@ func (p *Package) Desc() string {
|
||||
return p.ImportPath
|
||||
}
|
||||
|
||||
// IsTestOnly reports whether p is a test-only package.
|
||||
//
|
||||
// A “test-only” package is one that:
|
||||
// - is a test-only variant of an ordinary package, or
|
||||
// - is a synthesized "main" package for a test binary, or
|
||||
// - contains only _test.go files.
|
||||
func (p *Package) IsTestOnly() bool {
|
||||
return p.ForTest != "" ||
|
||||
p.Internal.TestmainGo != nil ||
|
||||
len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 && len(p.GoFiles)+len(p.CgoFiles) == 0
|
||||
}
|
||||
|
||||
type PackageInternal struct {
|
||||
// Unexported fields are not part of the public API.
|
||||
Build *build.Package
|
||||
@@ -1926,8 +1938,12 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
|
||||
}
|
||||
p.Internal.Imports = imports
|
||||
p.collectDeps()
|
||||
if p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 {
|
||||
p.setBuildInfo()
|
||||
if p.Error == nil && p.Name == "main" && !p.Internal.ForceLibrary && len(p.DepsErrors) == 0 {
|
||||
// TODO(bcmills): loading VCS metadata can be fairly slow.
|
||||
// Consider starting this as a background goroutine and retrieving the result
|
||||
// asynchronously when we're actually ready to build the package, or when we
|
||||
// actually need to evaluate whether the package's metadata is stale.
|
||||
p.setBuildInfo(opts.LoadVCS)
|
||||
}
|
||||
|
||||
// unsafe is a fake package.
|
||||
@@ -2216,7 +2232,7 @@ var vcsStatusCache par.Cache
|
||||
//
|
||||
// Note that the GoVersion field is not set here to avoid encoding it twice.
|
||||
// It is stored separately in the binary, mostly for historical reasons.
|
||||
func (p *Package) setBuildInfo() {
|
||||
func (p *Package) setBuildInfo(includeVCS bool) {
|
||||
// TODO: build and vcs information is not embedded for executables in GOROOT.
|
||||
// cmd/dist uses -gcflags=all= -ldflags=all= by default, which means these
|
||||
// executables always appear stale unless the user sets the same flags.
|
||||
@@ -2346,8 +2362,8 @@ func (p *Package) setBuildInfo() {
|
||||
// Add VCS status if all conditions are true:
|
||||
//
|
||||
// - -buildvcs is enabled.
|
||||
// - p is contained within a main module (there may be multiple main modules
|
||||
// in a workspace, but local replacements don't count).
|
||||
// - p is a non-test contained within a main module (there may be multiple
|
||||
// main modules in a workspace, but local replacements don't count).
|
||||
// - Both the current directory and p's module's root directory are contained
|
||||
// in the same local repository.
|
||||
// - We know the VCS commands needed to get the status.
|
||||
@@ -2359,7 +2375,7 @@ func (p *Package) setBuildInfo() {
|
||||
var vcsCmd *vcs.Cmd
|
||||
var err error
|
||||
const allowNesting = true
|
||||
if cfg.BuildBuildvcs && p.Module != nil && p.Module.Version == "" && !p.Standard {
|
||||
if includeVCS && p.Module != nil && p.Module.Version == "" && !p.Standard && !p.IsTestOnly() {
|
||||
repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
setVCSError(err)
|
||||
@@ -2648,6 +2664,9 @@ type PackageOpts struct {
|
||||
// are not be matched, and their dependencies may not be loaded. A warning
|
||||
// may be printed for non-literal arguments that match no main packages.
|
||||
MainOnly bool
|
||||
|
||||
// LoadVCS controls whether we also load version-control metadata for main packages.
|
||||
LoadVCS bool
|
||||
}
|
||||
|
||||
// PackagesAndErrors returns the packages named by the command line arguments
|
||||
|
||||
@@ -368,9 +368,9 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
|
||||
if err != nil && pmain.Error == nil {
|
||||
pmain.Error = &PackageError{Err: err}
|
||||
}
|
||||
if data != nil {
|
||||
pmain.Internal.TestmainGo = &data
|
||||
}
|
||||
// Set TestmainGo even if it is empty: the presence of a TestmainGo
|
||||
// indicates that this package is, in fact, a test main.
|
||||
pmain.Internal.TestmainGo = &data
|
||||
|
||||
return pmain, ptest, pxtest
|
||||
}
|
||||
|
||||
@@ -288,6 +288,11 @@ func BinDir() string {
|
||||
// operate in workspace mode. It should not be called by other commands,
|
||||
// for example 'go mod tidy', that don't operate in workspace mode.
|
||||
func InitWorkfile() {
|
||||
if RootMode == NoRoot {
|
||||
workFilePath = ""
|
||||
return
|
||||
}
|
||||
|
||||
switch gowork := cfg.Getenv("GOWORK"); gowork {
|
||||
case "off":
|
||||
workFilePath = ""
|
||||
|
||||
@@ -802,7 +802,7 @@ var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersi
|
||||
// an absolute path or a relative path starting with a '.' or '..'
|
||||
// path component.
|
||||
func ToDirectoryPath(path string) string {
|
||||
if modfile.IsDirectoryPath(path) {
|
||||
if path == "." || modfile.IsDirectoryPath(path) {
|
||||
return path
|
||||
}
|
||||
// The path is not a relative path or an absolute path, so make it relative
|
||||
|
||||
@@ -73,8 +73,6 @@ func printStderr(args ...any) (int, error) {
|
||||
}
|
||||
|
||||
func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
|
||||
if shouldUseOutsideModuleMode(args) {
|
||||
// Set global module flags for 'go run cmd@version'.
|
||||
// This must be done before modload.Init, but we need to call work.BuildInit
|
||||
@@ -84,7 +82,10 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.RootMode = modload.NoRoot
|
||||
modload.AllowMissingModuleImports()
|
||||
modload.Init()
|
||||
} else {
|
||||
modload.InitWorkfile()
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
|
||||
@@ -312,7 +312,7 @@ func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) {
|
||||
// uncommitted files and skip tagging revision / committime.
|
||||
var rev string
|
||||
var commitTime time.Time
|
||||
out, err = vcsGit.runOutputVerboseOnly(rootDir, "show -s --no-show-signature --format=%H:%ct")
|
||||
out, err = vcsGit.runOutputVerboseOnly(rootDir, "-c log.showsignature=false show -s --format=%H:%ct")
|
||||
if err != nil && !uncommitted {
|
||||
return Status{}, err
|
||||
} else if err == nil {
|
||||
|
||||
@@ -379,7 +379,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
|
||||
var b Builder
|
||||
b.Init()
|
||||
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args)
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: cfg.BuildBuildvcs}, args)
|
||||
load.CheckPackageErrors(pkgs)
|
||||
|
||||
explicitO := len(cfg.BuildO) > 0
|
||||
@@ -603,7 +603,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) {
|
||||
|
||||
modload.InitWorkfile()
|
||||
BuildInit()
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args)
|
||||
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: cfg.BuildBuildvcs}, args)
|
||||
if cfg.ModulesEnabled && !modload.HasModRoot() {
|
||||
haveErrors := false
|
||||
allMissingErrors := true
|
||||
|
||||
@@ -84,9 +84,8 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
Version string
|
||||
}
|
||||
|
||||
See the workspaces design proposal at
|
||||
https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
|
||||
more information.
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ modules will be created.
|
||||
Each argument path is added to a use directive in the go.work file. The
|
||||
current go version will also be listed in the go.work file.
|
||||
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
Run: runInit,
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ if the dependency module's version is not already the same as the build
|
||||
list's version. Note that Minimal Version Selection guarantees that the
|
||||
build list's version of each module is always the same or higher than
|
||||
that in each workspace module.
|
||||
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
Run: runSync,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -20,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
var cmdUse = &base.Command{
|
||||
UsageLine: "go work use [-r] [moddirs]",
|
||||
UsageLine: "go work use [-r] moddirs",
|
||||
Short: "add modules to workspace file",
|
||||
Long: `Use provides a command-line interface for adding
|
||||
directories, optionally recursively, to a go.work file.
|
||||
@@ -33,6 +32,9 @@ The -r flag searches recursively for modules in the argument
|
||||
directories, and the use command operates as if each of the directories
|
||||
were specified as arguments: namely, use directives will be added for
|
||||
directories that exist, and removed for directories that do not exist.
|
||||
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -82,13 +84,14 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
|
||||
lookDir := func(dir string) {
|
||||
absDir, dir := pathRel(workDir, dir)
|
||||
|
||||
fi, err := os.Stat(filepath.Join(absDir, "go.mod"))
|
||||
fi, err := fsys.Stat(filepath.Join(absDir, "go.mod"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
keepDirs[absDir] = ""
|
||||
return
|
||||
} else {
|
||||
base.Errorf("go: %v", err)
|
||||
}
|
||||
base.Errorf("go: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !fi.Mode().IsRegular() {
|
||||
@@ -101,14 +104,37 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
|
||||
keepDirs[absDir] = dir
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
base.Fatalf("go: 'go work use' requires one or more directory arguments")
|
||||
}
|
||||
for _, useDir := range args {
|
||||
absArg, _ := pathRel(workDir, useDir)
|
||||
|
||||
info, err := fsys.Stat(absArg)
|
||||
if err != nil {
|
||||
// Errors raised from os.Stat are formatted to be more user-friendly.
|
||||
if os.IsNotExist(err) {
|
||||
base.Errorf("go: directory %v does not exist", absArg)
|
||||
} else {
|
||||
base.Errorf("go: %v", err)
|
||||
}
|
||||
continue
|
||||
} else if !info.IsDir() {
|
||||
base.Errorf("go: %s is not a directory", absArg)
|
||||
continue
|
||||
}
|
||||
|
||||
if !*useR {
|
||||
lookDir(useDir)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add or remove entries for any subdirectories that still exist.
|
||||
err := fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
|
||||
fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
if info.Mode()&fs.ModeSymlink != 0 {
|
||||
if target, err := fsys.Stat(path); err == nil && target.IsDir() {
|
||||
@@ -120,13 +146,9 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
|
||||
lookDir(path)
|
||||
return nil
|
||||
})
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
base.Errorf("go: %v", err)
|
||||
}
|
||||
|
||||
// Remove entries for subdirectories that no longer exist.
|
||||
// Because they don't exist, they will be skipped by Walk.
|
||||
absArg, _ := pathRel(workDir, useDir)
|
||||
for absDir, _ := range haveDirs {
|
||||
if str.HasFilePathPrefix(absDir, absArg) {
|
||||
if _, ok := keepDirs[absDir]; !ok {
|
||||
@@ -186,5 +208,5 @@ func pathRel(workDir, dir string) (abs, canonical string) {
|
||||
|
||||
// Normalize relative paths to use slashes, so that checked-in go.work
|
||||
// files with relative paths within the repo are platform-independent.
|
||||
return abs, filepath.ToSlash(rel)
|
||||
return abs, modload.ToDirectoryPath(rel)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
var CmdWork = &base.Command{
|
||||
UsageLine: "go work",
|
||||
Short: "workspace maintenance",
|
||||
Long: `Go workspace provides access to operations on workspaces.
|
||||
Long: `Work provides access to operations on workspaces.
|
||||
|
||||
Note that support for workspaces is built into many other commands, not
|
||||
just 'go work'.
|
||||
@@ -20,6 +20,12 @@ just 'go work'.
|
||||
See 'go help modules' for information about Go's module system of which
|
||||
workspaces are a part.
|
||||
|
||||
See https://go.dev/ref/mod#workspaces for an in-depth reference on
|
||||
workspaces.
|
||||
|
||||
See https://go.dev/doc/tutorial/workspaces for an introductory
|
||||
tutorial on workspaces.
|
||||
|
||||
A workspace is specified by a go.work file that specifies a set of
|
||||
module directories with the "use" directive. These modules are used as
|
||||
root modules by the go command for builds and related operations. A
|
||||
|
||||
16
src/cmd/go/testdata/script/run_work_versioned.txt
vendored
Normal file
16
src/cmd/go/testdata/script/run_work_versioned.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
[short] skip
|
||||
go run example.com/printversion@v0.1.0
|
||||
stdout '^main is example.com/printversion v0.1.0$'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
)
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.18
|
||||
|
||||
require example.com/printversion v1.0.0
|
||||
92
src/cmd/go/testdata/script/test_buildvcs.txt
vendored
Normal file
92
src/cmd/go/testdata/script/test_buildvcs.txt
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# https://go.dev/issue/51723: 'go test' should not stamp VCS metadata
|
||||
# in the build settings. (It isn't worth the latency hit, given that
|
||||
# test binaries are almost never distributed to users.)
|
||||
|
||||
[short] skip
|
||||
[!exec:git] skip
|
||||
|
||||
exec git init
|
||||
|
||||
# The test binaries should not have VCS settings stamped.
|
||||
# (The test itself verifies that.)
|
||||
go test . ./testonly
|
||||
|
||||
|
||||
# Remove 'git' from $PATH. The test should still build.
|
||||
# This ensures that we aren't loading VCS metadata that
|
||||
# we subsequently throw away.
|
||||
env PATH=''
|
||||
env path=''
|
||||
|
||||
# Compiling the test should not require the VCS tool.
|
||||
go test -c -o $devnull .
|
||||
|
||||
|
||||
# When listing a main package, in general we need its VCS metadata to determine
|
||||
# the .Stale and .StaleReason fields.
|
||||
! go list .
|
||||
stderr '^go: missing Git command\. See https://golang\.org/s/gogetcmd\nerror obtaining VCS status: .*\n\tUse -buildvcs=false to disable VCS stamping.'
|
||||
|
||||
# Adding the -test flag should be strictly additive — it should not suppress the error.
|
||||
! go list -test .
|
||||
stderr '^go: missing Git command\. See https://golang\.org/s/gogetcmd\nerror obtaining VCS status: .*\n\tUse -buildvcs=false to disable VCS stamping.'
|
||||
|
||||
# Adding the suggested flag should suppress the error.
|
||||
go list -test -buildvcs=false .
|
||||
! stderr .
|
||||
|
||||
|
||||
# Since the ./testonly package can't produce an actual binary, we shouldn't
|
||||
# invoke a VCS tool to compute a build stamp when listing it.
|
||||
go list ./testonly
|
||||
! stderr .
|
||||
go list -test ./testonly
|
||||
! stderr .
|
||||
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.18
|
||||
-- example.go --
|
||||
package main
|
||||
-- example_test.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDetail(t *testing.T) {
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
t.Fatal("BuildInfo not present")
|
||||
}
|
||||
for _, s := range bi.Settings {
|
||||
if strings.HasPrefix(s.Key, "vcs.") {
|
||||
t.Fatalf("unexpected VCS setting: %s=%s", s.Key, s.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
-- testonly/main_test.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDetail(t *testing.T) {
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
t.Fatal("BuildInfo not present")
|
||||
}
|
||||
for _, s := range bi.Settings {
|
||||
if strings.HasPrefix(s.Key, "vcs.") {
|
||||
t.Fatalf("unexpected VCS setting: %s=%s", s.Key, s.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
vendored
Normal file
84
src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# Test that minimization doesn't use dirty coverage snapshots when it
|
||||
# is unable to actually minimize the input. We do this by checking that
|
||||
# a expected value appears in the cache. If a dirty coverage map is used
|
||||
# (i.e. the coverage map generated during the last minimization step,
|
||||
# rather than the map provided with the initial input) then this value
|
||||
# is unlikely to appear in the cache, since the map generated during
|
||||
# the last minimization step should not increase the coverage.
|
||||
|
||||
[short] skip
|
||||
[!fuzz-instrumented] skip
|
||||
|
||||
env GOCACHE=$WORK/gocache
|
||||
go test -fuzz=FuzzCovMin -fuzztime=25s -test.fuzzcachedir=$GOCACHE/fuzz
|
||||
go run check_file/main.go $GOCACHE/fuzz/FuzzCovMin abcd
|
||||
|
||||
-- go.mod --
|
||||
module test
|
||||
|
||||
-- covmin_test.go --
|
||||
package covmin
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzCovMin(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
if len(data) >= 4 && data[0] == 'a' && data[1] == 'b' && data[2] == 'c' && data[3] == 'd' {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
-- check_file/main.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func checkFile(name, expected string) (bool, error) {
|
||||
data, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, line := range bytes.Split(data, []byte("\n")) {
|
||||
m := valRe.FindSubmatch(line)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println(strconv.Unquote(string(m[1])))
|
||||
if s, err := strconv.Unquote(string(m[1])); err != nil {
|
||||
return false, err
|
||||
} else if s == expected {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
|
||||
|
||||
func main() {
|
||||
dir, expected := os.Args[1], os.Args[2]
|
||||
ents, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, ent := range ents {
|
||||
name := filepath.Join(dir, ent.Name())
|
||||
if good, err := checkFile(name, expected); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
} else if good {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "input over minimized")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -127,19 +127,8 @@ func FuzzMinCache(f *testing.F) {
|
||||
if bytes.Equal(buf, seed) {
|
||||
return
|
||||
}
|
||||
if n := sum(buf); n < 0 {
|
||||
t.Error("sum cannot be negative")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func sum(buf []byte) int {
|
||||
n := 0
|
||||
for _, b := range buf {
|
||||
n += int(b)
|
||||
}
|
||||
return n
|
||||
}
|
||||
-- check_testdata/check_testdata.go --
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
@@ -111,7 +111,7 @@ rm $GOBIN/d$GOEXE
|
||||
go list -x ./...
|
||||
stdout -count=3 '^example.com'
|
||||
stderr -count=1 '^git status'
|
||||
stderr -count=1 '^git show'
|
||||
stderr -count=1 '^git -c log.showsignature=false show'
|
||||
|
||||
-- $WORK/fakebin/git --
|
||||
#!/bin/sh
|
||||
|
||||
17
src/cmd/go/testdata/script/work_init_path.txt
vendored
Normal file
17
src/cmd/go/testdata/script/work_init_path.txt
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Regression test for https://go.dev/issue/51448.
|
||||
# 'go work init . foo/bar' should produce a go.work file
|
||||
# with the same paths as 'go work init; go work use -r .'.
|
||||
|
||||
go work init . foo/bar
|
||||
mv go.work go.work.init
|
||||
|
||||
go work init
|
||||
go work use -r .
|
||||
cmp go.work go.work.init
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
go 1.18
|
||||
-- foo/bar/go.mod --
|
||||
module example
|
||||
go 1.18
|
||||
10
src/cmd/go/testdata/script/work_use.txt
vendored
10
src/cmd/go/testdata/script/work_use.txt
vendored
@@ -14,16 +14,16 @@ use (
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
foo
|
||||
foo/bar/baz
|
||||
./foo
|
||||
./foo/bar/baz
|
||||
)
|
||||
-- go.want_work_other --
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
foo
|
||||
foo/bar/baz
|
||||
other
|
||||
./foo
|
||||
./foo/bar/baz
|
||||
./other
|
||||
)
|
||||
-- foo/go.mod --
|
||||
module foo
|
||||
|
||||
@@ -6,13 +6,13 @@ go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
sub
|
||||
sub/dir/deleted
|
||||
./sub
|
||||
./sub/dir/deleted
|
||||
)
|
||||
-- go.work.want --
|
||||
go 1.18
|
||||
|
||||
use sub/dir
|
||||
use ./sub/dir
|
||||
-- sub/README.txt --
|
||||
A go.mod file has been deleted from this directory.
|
||||
In addition, the entire subdirectory sub/dir/deleted
|
||||
|
||||
4
src/cmd/go/testdata/script/work_use_dot.txt
vendored
4
src/cmd/go/testdata/script/work_use_dot.txt
vendored
@@ -31,7 +31,7 @@ grep '^use ["]?'$PWD'["]?$' ../../go.work
|
||||
# resulting workspace would contain a duplicate module.
|
||||
cp ../../go.work.orig ../../go.work
|
||||
! go work use $PWD .
|
||||
stderr '^go: already added "bar/baz" as "'$PWD'"$'
|
||||
stderr '^go: already added "\./bar/baz" as "'$PWD'"$'
|
||||
cmp ../../go.work ../../go.work.orig
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ go 1.18
|
||||
-- go.work.rel --
|
||||
go 1.18
|
||||
|
||||
use bar/baz
|
||||
use ./bar/baz
|
||||
-- bar/baz/go.mod --
|
||||
module example/bar/baz
|
||||
go 1.18
|
||||
|
||||
11
src/cmd/go/testdata/script/work_use_noargs.txt
vendored
Normal file
11
src/cmd/go/testdata/script/work_use_noargs.txt
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# For now, 'go work use' requires arguments.
|
||||
# (Eventually, we may may it implicitly behave like 'go work use .'.
|
||||
|
||||
! go work use
|
||||
stderr '^go: ''go work use'' requires one or more directory arguments'
|
||||
|
||||
! go work use -r
|
||||
stderr '^go: ''go work use'' requires one or more directory arguments'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
17
src/cmd/go/testdata/script/work_use_only_dirs.txt
vendored
Normal file
17
src/cmd/go/testdata/script/work_use_only_dirs.txt
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
! go work use foo bar baz
|
||||
|
||||
stderr '^go: '$WORK'[/\\]gopath[/\\]src[/\\]foo is not a directory'
|
||||
stderr '^go: directory '$WORK'[/\\]gopath[/\\]src[/\\]baz does not exist'
|
||||
cmp go.work go.work_want
|
||||
|
||||
! go work use -r qux
|
||||
stderr '^go: '$WORK'[/\\]gopath[/\\]src[/\\]qux is not a directory'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
-- go.work_want --
|
||||
go 1.18
|
||||
-- foo --
|
||||
-- qux --
|
||||
-- bar/go.mod --
|
||||
module bar
|
||||
@@ -355,7 +355,7 @@ func deadcode(ctxt *Link) {
|
||||
// in the last pass.
|
||||
rem := d.markableMethods[:0]
|
||||
for _, m := range d.markableMethods {
|
||||
if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
|
||||
if (d.reflectSeen && (m.isExported() || d.dynlink)) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
|
||||
d.markMethod(m)
|
||||
} else {
|
||||
rem = append(rem, m)
|
||||
|
||||
56
src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go
generated
vendored
56
src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go
generated
vendored
@@ -254,18 +254,18 @@ func For(obj types.Object) (Path, error) {
|
||||
|
||||
if tname.IsAlias() {
|
||||
// type alias
|
||||
if r := find(obj, T, path); r != nil {
|
||||
if r := find(obj, T, path, nil); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
} else {
|
||||
if named, _ := T.(*types.Named); named != nil {
|
||||
if r := findTypeParam(obj, typeparams.ForNamed(named), path); r != nil {
|
||||
if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil {
|
||||
// generic named type
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
// defined (named) type
|
||||
if r := find(obj, T.Underlying(), append(path, opUnderlying)); r != nil {
|
||||
if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
@@ -279,7 +279,7 @@ func For(obj types.Object) (Path, error) {
|
||||
if _, ok := o.(*types.TypeName); !ok {
|
||||
if o.Exported() {
|
||||
// exported non-type (const, var, func)
|
||||
if r := find(obj, o.Type(), append(path, opType)); r != nil {
|
||||
if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
@@ -299,7 +299,7 @@ func For(obj types.Object) (Path, error) {
|
||||
if m == obj {
|
||||
return Path(path2), nil // found declared method
|
||||
}
|
||||
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
|
||||
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
@@ -316,41 +316,44 @@ func appendOpArg(path []byte, op byte, arg int) []byte {
|
||||
}
|
||||
|
||||
// find finds obj within type T, returning the path to it, or nil if not found.
|
||||
func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
//
|
||||
// The seen map is used to short circuit cycles through type parameters. If
|
||||
// nil, it will be allocated as necessary.
|
||||
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
|
||||
switch T := T.(type) {
|
||||
case *types.Basic, *types.Named:
|
||||
// Named types belonging to pkg were handled already,
|
||||
// so T must belong to another package. No path.
|
||||
return nil
|
||||
case *types.Pointer:
|
||||
return find(obj, T.Elem(), append(path, opElem))
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
case *types.Slice:
|
||||
return find(obj, T.Elem(), append(path, opElem))
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
case *types.Array:
|
||||
return find(obj, T.Elem(), append(path, opElem))
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
case *types.Chan:
|
||||
return find(obj, T.Elem(), append(path, opElem))
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
case *types.Map:
|
||||
if r := find(obj, T.Key(), append(path, opKey)); r != nil {
|
||||
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
|
||||
return r
|
||||
}
|
||||
return find(obj, T.Elem(), append(path, opElem))
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
case *types.Signature:
|
||||
if r := findTypeParam(obj, typeparams.ForSignature(T), path); r != nil {
|
||||
if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := find(obj, T.Params(), append(path, opParams)); r != nil {
|
||||
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
|
||||
return r
|
||||
}
|
||||
return find(obj, T.Results(), append(path, opResults))
|
||||
return find(obj, T.Results(), append(path, opResults), seen)
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
f := T.Field(i)
|
||||
fld := T.Field(i)
|
||||
path2 := appendOpArg(path, opField, i)
|
||||
if f == obj {
|
||||
if fld == obj {
|
||||
return path2 // found field var
|
||||
}
|
||||
if r := find(obj, f.Type(), append(path2, opType)); r != nil {
|
||||
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -362,7 +365,7 @@ func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
if v == obj {
|
||||
return path2 // found param/result var
|
||||
}
|
||||
if r := find(obj, v.Type(), append(path2, opType)); r != nil {
|
||||
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -374,7 +377,7 @@ func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
if m == obj {
|
||||
return path2 // found interface method
|
||||
}
|
||||
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
|
||||
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -384,7 +387,14 @@ func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
if name == obj {
|
||||
return append(path, opObj)
|
||||
}
|
||||
if r := find(obj, T.Constraint(), append(path, opConstraint)); r != nil {
|
||||
if seen[name] {
|
||||
return nil
|
||||
}
|
||||
if seen == nil {
|
||||
seen = make(map[*types.TypeName]bool)
|
||||
}
|
||||
seen[name] = true
|
||||
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
@@ -392,11 +402,11 @@ func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
panic(T)
|
||||
}
|
||||
|
||||
func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte) []byte {
|
||||
func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
tparam := list.At(i)
|
||||
path2 := appendOpArg(path, opTypeParam, i)
|
||||
if r := find(obj, tparam, path2); r != nil {
|
||||
if r := find(obj, tparam, path2, seen); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
@@ -51,7 +51,7 @@ golang.org/x/sys/windows
|
||||
# golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
## explicit; go 1.17
|
||||
golang.org/x/term
|
||||
# golang.org/x/tools v0.1.9-0.20220124164225-97de9ec46646
|
||||
# golang.org/x/tools v0.1.9-0.20220329150752-294080fd2f5a
|
||||
## explicit; go 1.17
|
||||
golang.org/x/tools/cover
|
||||
golang.org/x/tools/go/analysis
|
||||
|
||||
@@ -51,7 +51,7 @@ func p256GetScalar(out *[32]byte, in []byte) {
|
||||
n := new(big.Int).SetBytes(in)
|
||||
var scalarBytes []byte
|
||||
|
||||
if n.Cmp(p256Params.N) >= 0 {
|
||||
if n.Cmp(p256Params.N) >= 0 || len(in) > len(out) {
|
||||
n.Mod(n, p256Params.N)
|
||||
scalarBytes = n.Bytes()
|
||||
} else {
|
||||
|
||||
@@ -136,3 +136,17 @@ func TestP256CombinedMult(t *testing.T) {
|
||||
t.Errorf("1×G + (-1)×G = (%d, %d), should be ∞", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue52075(t *testing.T) {
|
||||
Gx, Gy := P256().Params().Gx, P256().Params().Gy
|
||||
scalar := make([]byte, 33)
|
||||
scalar[32] = 1
|
||||
x, y := P256().ScalarBaseMult(scalar)
|
||||
if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 {
|
||||
t.Errorf("unexpected output (%v,%v)", x, y)
|
||||
}
|
||||
x, y = P256().ScalarMult(Gx, Gy, scalar)
|
||||
if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 {
|
||||
t.Errorf("unexpected output (%v,%v)", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
|
||||
certs := macOS.CFArrayCreateMutable()
|
||||
defer macOS.ReleaseCFArray(certs)
|
||||
leaf := macOS.SecCertificateCreateWithData(c.Raw)
|
||||
if leaf == 0 {
|
||||
return nil, errors.New("invalid leaf certificate")
|
||||
}
|
||||
macOS.CFArrayAppendValue(certs, leaf)
|
||||
if opts.Intermediates != nil {
|
||||
for _, lc := range opts.Intermediates.lazyCerts {
|
||||
@@ -21,7 +24,9 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
|
||||
return nil, err
|
||||
}
|
||||
sc := macOS.SecCertificateCreateWithData(c.Raw)
|
||||
macOS.CFArrayAppendValue(certs, sc)
|
||||
if sc != 0 {
|
||||
macOS.CFArrayAppendValue(certs, sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@ package x509_test
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"internal/testenv"
|
||||
"net"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -17,10 +21,19 @@ func TestPlatformVerifier(t *testing.T) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
getChain := func(host string) []*x509.Certificate {
|
||||
getChain := func(t *testing.T, host string) []*x509.Certificate {
|
||||
t.Helper()
|
||||
c, err := tls.Dial("tcp", host+":443", &tls.Config{InsecureSkipVerify: true})
|
||||
if err != nil {
|
||||
// From https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2,
|
||||
// matching the error string observed in https://go.dev/issue/52094.
|
||||
const WSATRY_AGAIN syscall.Errno = 11002
|
||||
var errDNS *net.DNSError
|
||||
if strings.HasSuffix(host, ".badssl.com") && errors.As(err, &errDNS) && strings.HasSuffix(errDNS.Err, WSATRY_AGAIN.Error()) {
|
||||
t.Log(err)
|
||||
testenv.SkipFlaky(t, 52094)
|
||||
}
|
||||
|
||||
t.Fatalf("tls connection failed: %s", err)
|
||||
}
|
||||
return c.ConnectionState().PeerCertificates
|
||||
@@ -74,7 +87,7 @@ func TestPlatformVerifier(t *testing.T) {
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
chain := getChain(tc.host)
|
||||
chain := getChain(t, tc.host)
|
||||
var opts x509.VerifyOptions
|
||||
if len(chain) > 1 {
|
||||
opts.Intermediates = x509.NewCertPool()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user