Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #5136 from mppf/spec-changes-specialize-generic-me…
Browse files Browse the repository at this point in the history
…thod

Spec changes specialize generic method

This is a continuation of PR #5057.

*  Add a new test case for specific-instantiation 
*  Spec changes describing overload-specific method

reviewed by @vasslitvinov - thanks!
  • Loading branch information
mppf committed Jan 16, 2017
2 parents 3db2751 + f51571e commit 05844a0
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 1 deletion.
11 changes: 10 additions & 1 deletion spec/Classes.tex
Expand Up @@ -214,17 +214,26 @@ \subsection{Class Methods}

type-binding:
identifier .
`(' expr `)' .

\end{syntax}
Methods defined within the lexical scope of a class, record, or union
are referred to as \emph{primary methods}. For such methods,
the \sntx{type-binding} can be omitted and is taken to be the
the \sntx{type-binding} is omitted and is taken to be the
innermost class, record, or union in which the method is defined.
Methods defined outside of such scopes are known as \emph{secondary
methods} and must have a \sntx{type-binding} (otherwise, they would
simply be standalone functions rather than methods). Note that
secondary methods can be defined not only for classes, records, and
unions, but also for any other type (e.g., integers, reals, strings).

Secondary methods can be declared with a type expression instead of a
type identifier. In particular, if the \sntx{type-binding} is a
parenthesized expression, the compiler will evaluate that expression to
find the receiver type for the method. In that case, the method applies
only to receivers of that type. See also
\rsec{Creating_General_and_Specialized_Versions_of_a_Function}.

Method calls are described in \rsec{Class_Method_Calls}.

The use of \sntx{this-intent} is described in \rsec{The_em_this_Reference}.
Expand Down
80 changes: 80 additions & 0 deletions spec/Generics.tex
Expand Up @@ -868,6 +868,86 @@ \section{User-Defined Compiler Diagnostics}

\end{chapelexample}

\section{Creating General and Specialized Versions of a Function}
\label{Creating_General_and_Specialized_Versions_of_a_Function}
\index{specific instantiations}
\index{generic specialization}
\index{generic functions and special versions}

The Chapel language facility supports three mechanisms for using generic
functions along with concrete functions. These mechanisms allow users to
create a general generic implementation and also a special implementation
for specific concrete types.

The first mechanism applies to functions.
According to the function resolution rules described in
\rsec{Function_Resolution}, functions accepting concrete arguments are
selected in preference to those with a totally generic argument. So,
creating a second version of a generic function that declares a concrete
type will cause the concrete function to be used where possible:

\begin{chapelexample}{specializeGenericFunction.chpl}
\begin{chapel}
proc foo(x) {
writeln("in generic foo(x)");
}
proc foo(x:int) {
writeln("in specific foo(x:int)");
}

var myReal:real;
foo(myReal); // outputs "in generic foo(x)"
var myInt:int;
foo(myInt); // outputs "in specific foo(x:int)"
\end{chapel}
\begin{chapeloutput}
in generic foo(x)
in specific foo(x:int)
\end{chapeloutput}
\end{chapelexample}

This program will run the generic foo function if the argument is a real,
but it runs the specific version for int if the argument is an int.

The second mechanism applies when working with methods on generic types.
When declaring a secondary method, the receiver type can be a
parenthesized expression. In that case, the compiler will evaluate the
parenthesized expression at compile time in order to find the concrete
receiver type. Then, the resolution rules described above will cause the
concrete method to be selected when applicable. For example:

\begin{chapelexample}{specializeGenericMethod.chpl}
\begin{chapel}

record MyNode {
var field; // since no type is specified here, MyNode is a generic type
}

proc MyNode.foo() {
writeln("in generic MyNode.foo()");
}
proc (MyNode(int)).foo() {
writeln("in specific MyNode(int).foo()");
}

var myRealNode = new MyNode(1.0);
myRealNode.foo(); // outputs "in generic MyNode.foo()"
var myIntNode = new MyNode(1);
myIntNode.foo(); // outputs "in specific MyNode(int).foo()"
\end{chapel}
\begin{chapeloutput}
in generic MyNode.foo()
in specific MyNode(int).foo()
\end{chapeloutput}
\end{chapelexample}

The third mechanism is to use a where clause. Where clauses limit a
generic method to particular cases. Unlike the previous two cases, a
where clause can be used to declare special implementation of a function
that works with some set of types - in other words, the special
implementation can still be a generic function. See also
\rsec{Where_Expressions}.

\section{Example: A Generic Stack}
\label{Example_Generic_Stack}
\index{generics!examples!stack}
Expand Down
32 changes: 32 additions & 0 deletions test/functions/ferguson/spec-insn-method-custom-typefn.chpl
@@ -0,0 +1,32 @@

record R {
var x;
}

proc R.foo() {
writeln("in generic R.foo");
}

proc getR() type {
return R(int);
}

proc (getR()).foo() {
writeln("in specific R(int).foo");
}

proc R.foo()
where this.type == R(string)
{
writeln("in specific R(string).foo");
}

var x = new R(1.0);
x.foo();

var y = new R("test");
y.foo();

var z = new R(2);
z.foo();

3 changes: 3 additions & 0 deletions test/functions/ferguson/spec-insn-method-custom-typefn.good
@@ -0,0 +1,3 @@
in generic R.foo
in specific R(string).foo
in specific R(int).foo

0 comments on commit 05844a0

Please sign in to comment.