summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/reference/ScalaReference.tex65
-rw-r--r--sources/scala/tools/scalac/ast/parser/Parser.scala2
-rw-r--r--sources/scala/tools/scalac/typechecker/Analyzer.scala15
-rw-r--r--sources/scalac/symtab/Type.java62
-rw-r--r--test/files/neg/bug44.check4
-rw-r--r--test/files/neg/refine.check4
-rw-r--r--test/files/neg/vincent1.check2
-rw-r--r--test/files/pos/MailBox.scala2
-rw-r--r--test/neg/bug44.check4
-rw-r--r--test/neg/refine.check4
-rw-r--r--test/neg/vincent1.check2
-rw-r--r--test/pos/MailBox.scala2
12 files changed, 126 insertions, 42 deletions
diff --git a/doc/reference/ScalaReference.tex b/doc/reference/ScalaReference.tex
index 5c106a3e58..586daf401c 100644
--- a/doc/reference/ScalaReference.tex
+++ b/doc/reference/ScalaReference.tex
@@ -304,7 +304,7 @@ referenced entity in $T$.
Type ::= Type1 `=>' Type
| `(' [Types] `)' `=>' Type
| Type1
- Type1 ::= SimpleType {with SimpleType}
+ Type1 ::= SimpleType {with SimpleType} [Refinement]
SimpleType ::= StableId
| SimpleType `#' id
| Path `.' type
@@ -318,12 +318,14 @@ take type parameters and yield types. A subset of first-order types
called {\em value types} represents sets of (first-class) values.
Value types are either {\em concrete} or {\em abstract}. Every
concrete value type can be represented as a {\em class type}, i.e.\ a
-type designator (\sref{sec:type-desig}) that refers to a
-class\footnote{We assume that objects and packages also implicitly
-define a class (of the same name as the object or package, but
-inaccessible to user programs).} (\sref{sec:classes}), or as a {\em
-compound type} (\sref{sec:compound-types}) of class types.
-\todo{verify whether operands need to be class types}
+type designator (\sref{sec:type-desig}) that refers to a
+class\footnote{We assume that objects and packages also
+implicitly define a class (of the same name as the object or package,
+but inaccessible to user programs).} (\sref{sec:classes}),
+or as a {\em compound type} (\sref{sec:compound-types})
+consisting of class types and possibly
+also a refinement (\sref{sec:refinements}) that further constrains the
+types of its members.
A shorthand exists for denoting function types
(\sref{sec:function-types}). Abstract value types are introduced by
@@ -472,14 +474,25 @@ the following types are ill-formed:
\label{sec:compound-types}
\syntax\begin{lstlisting}
- Type ::= SimpleType {with SimpleType}
+ Type ::= SimpleType {with SimpleType} [Refinement]
+ Refinement ::= `{' [RefineStat {`;' RefineStat}] `}'
+ RefineStat ::= Dcl
+ | type TypeDef {`,' TypeDef}
+ |
\end{lstlisting}
-A compound type ~\lstinline@$T_1$ with $\ldots$ with $T_n$ {$R\,$}@~
-represents objects with members as given in the component types $T_1
-\commadots T_n$. Each component type $T_i$ must be a class type and
-the base class sequence generated by types $T_1 \commadots T_n$ must
-be well-formed (\sref{sec:basetypes-wf}).
+A compound type ~\lstinline@$T_1$ with $\ldots$ with $T_n$ {$R\,$}@~ represents
+objects with members as given in the component types $T_1 \commadots
+T_n$ and the refinement \lstinline@{$R\,$}@. Each component type $T_i$ must be a
+class type and the base class sequence generated by types $T_1
+\commadots T_n$ must be well-formed (\sref{sec:basetypes-wf}). A
+refinement \lstinline@{$R\,$}@ contains declarations and type
+definitions. Each declaration or definition in a refinement must
+override a declaration or definition in one of the component types
+$T_1 \commadots T_n$. The usual rules for overriding (\sref{})
+apply. If no refinement is given, the empty refinement is implicitly
+added, i.e. ~\lstinline@$T_1$ with $\ldots$ with $T_n$@~ is a shorthand for
+~\lstinline@$T_1$ with $\ldots$ with $T_n$ {}@.
\subsection{Function Types}
\label{sec:function-types}
@@ -745,7 +758,10 @@ consisting only of package or object selectors and ending in $O$, then
~\lstinline@$O$.this.type $\equiv p$.type@.
\item
Two compound types are equivalent if their component types are
-pairwise equivalent.
+pairwise equivalent and their refinements are equivalent. Two
+refinements are equivalent if they bind the same names and the
+modifiers, types and bounds of every declared entity are equivalent in
+both refinements.
\item
Two method types are equivalent if they have equivalent result
types, both have the same number of parameters, and corresponding
@@ -922,8 +938,9 @@ the expression is typed and evaluated is if it was
\end{lstlisting}
A {\em declaration} introduces names and assigns them types. It can
-only appear as one of the statements of a class definition
-(\sref{sec:templates}).
+appear as one of the statements of a class definition
+(\sref{sec:templates}) or as part of a refinement in a compound
+type (\sref{sec:refinements}).
A {\em definition} introduces names that denote terms or types. It can
form part of an object or class definition or it can be local to a
@@ -2703,7 +2720,13 @@ constructor invocations (of types $S, T_1 \commadots T_n$, say) and
$stats$ is a statement sequence containing initializer statements and
member definitions (\sref{sec:members}). The type of such an instance
creation expression is then the compound type
-\lstinline@$S$ with $T_1$ with $\ldots$ with $T_n$@.
+\lstinline@$S$ with $T_1$ with $\ldots$ with $T_n$ {$R\,$}@,
+where \lstinline@{$R\,$}@ is
+a refinement (\sref{sec:compound-types}) which declares exactly those
+members of $stats$ that override a member of $S$ or $T_1 \commadots
+T_n$. \todo{what about methods and overloaded defs?} For this type to
+be well-formed, $R$ may not reference types defined in $stats$ which
+do not themselves form part of $R$.
The instance creation expression is evaluated by creating a fresh
object, which is initialized by evaluating the expression template.
@@ -3375,7 +3398,7 @@ Concretely, we distinguish the following kinds of patterns.
A {\em wild-card pattern} \_ matches any value.
A {\em typed pattern} $\_: T$ matches values of type $T$. The type $T$ may be
- a class type or a compound type.
+ a class type or a compound type; it may not contain a refinement (\sref{sec:refinements}).
This pattern matches any non-null value of type $T$. $T$ must conform to the pattern's expected
type. A pattern $x:T$ is treated the same way as $x @ (_:T)$
@@ -4238,7 +4261,7 @@ grammar.
Type ::= Type1 `=>' Type
| `(' [Types] `)' `=>' Type
| Type1
- Type1 ::= SimpleType {with SimpleType}
+ Type1 ::= SimpleType {with SimpleType} [Refinement]
SimpleType ::= SimpleType TypeArgs
| SimpleType `#' id
| StableId
@@ -4246,6 +4269,10 @@ grammar.
| `(' Type ')'
TypeArgs ::= `[' Types `]'
Types ::= Type {`,' Type}
+ Refinement ::= `{' [RefineStat {`;' RefineStat}] `}'
+ RefineStat ::= Dcl
+ | type TypeDef {`,' TypeDef}
+ |
Exprs ::= Expr {`,' Expr}
Expr ::= Bindings `=>' Expr
diff --git a/sources/scala/tools/scalac/ast/parser/Parser.scala b/sources/scala/tools/scalac/ast/parser/Parser.scala
index 830c507e44..6cd6effa88 100644
--- a/sources/scala/tools/scalac/ast/parser/Parser.scala
+++ b/sources/scala/tools/scalac/ast/parser/Parser.scala
@@ -690,7 +690,7 @@ class Parser(unit: Unit) {
s.nextToken();
ts.append(simpleType());
}
- val rs = /*if (s.token == LBRACE) refinement() else*/ Tree.EMPTY_ARRAY;
+ val rs = if (s.token == LBRACE) refinement() else Tree.EMPTY_ARRAY;
make.CompoundType(pos, ts.toArray(), rs)
} else {
t
diff --git a/sources/scala/tools/scalac/typechecker/Analyzer.scala b/sources/scala/tools/scalac/typechecker/Analyzer.scala
index 5fd7997e9d..76355d1a6e 100644
--- a/sources/scala/tools/scalac/typechecker/Analyzer.scala
+++ b/sources/scala/tools/scalac/typechecker/Analyzer.scala
@@ -2283,7 +2283,6 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer(
val parentTypes = clazz.info().parents();
val refinement: Scope = new Scope();
val base: Type = Type.compoundTypeWithOwner(context.enclClass.owner, parentTypes, Scope.EMPTY);
- /*
val it: Scope$SymbolIterator = clazz.members().iterator();
while (it.hasNext()) {
val sym1: Symbol = it.next();
@@ -2293,7 +2292,6 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer(
.isSameAs(sym1.getType()))
refinement.enter(sym1);
}
- */
val owntype =
if (refinement.isEmpty() && parentTypes.length == 1)
parentTypes(0)
@@ -2764,16 +2762,23 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer(
case Tree$CompoundType(parents, refinements) =>
val parents1 = transform(parents, TYPEmode);
- val ptypes = Tree.typeOf(parents);
+ val ptypes = new Array[Type](parents1.length);
+ { var i = 0; while (i < parents1.length) {
+ val tp = parents(i).getType();
+ if (i > 0 || tp.unalias().symbol().kind != TYPE)
+ checkClassType(parents(i).pos, tp);
+ ptypes(i) = tp;
+ i = i + 1
+ }}
val members: Scope = new Scope();
val self: Type = Type.compoundTypeWithOwner(context.enclClass.owner, ptypes, members);
val clazz: Symbol = self.symbol();
pushContext(tree, clazz, members);
- var i = 0; while (i < refinements.length) {
+ { var i = 0; while (i < refinements.length) {
val m = enterSym(refinements(i));
m.flags = m.flags | OVERRIDE;
i = i + 1
- }
+ }}
val refinements1 = transformStatSeq(refinements, Symbol.NONE);
popContext();
copy.CompoundType(tree, parents1, refinements1)
diff --git a/sources/scalac/symtab/Type.java b/sources/scalac/symtab/Type.java
index b8b03a9ffb..e9665e13d8 100644
--- a/sources/scalac/symtab/Type.java
+++ b/sources/scalac/symtab/Type.java
@@ -1658,6 +1658,11 @@ public class Type implements Modifiers, Kinds, TypeTags, EntryTags {
case ThisType(Symbol sym):
if (sym == from) return to;
else return t;
+ case TypeRef(Type pre, Symbol sym, Type[] args):
+ Type pre1 = apply(pre);
+ Type[] args1 = map(args);
+ if (pre1 == pre && args1 == args) return t;
+ else return typeRef(pre1, pre1.rebind(sym), args1);
default:
return map(t);
}
@@ -2579,9 +2584,36 @@ public class Type implements Modifiers, Kinds, TypeTags, EntryTags {
}
}
+ static int recCount = 0;
+ static boolean giveUp = false;
+ static int recLimit = 10;
+
+ public static Type lub(Type[] tps) {
+ if (recCount == recLimit) {
+ giveUp = true;
+ return Global.instance.definitions.ANY_TYPE();
+ } else {
+ recCount++;
+ Type result = lub0(tps);
+ recCount--;
+ if (recCount == 0) {
+ if (giveUp) {
+ giveUp = false;
+ throw new Error("failure to compute least upper bound of types " +
+ ArrayApply.toString(tps, "", " and ", ";\n") +
+ "an approximation is: " + result + ";\n" +
+ "additional type annotations are needed");
+ } else {
+ giveUp = false;
+ }
+ }
+ return result;
+ }
+ }
+
/** Return the least upper bound of non-empty array of types `tps'.
*/
- public static Type lub(Type[] tps) {
+ public static Type lub0(Type[] tps) {
//System.out.println("lub" + ArrayApply.toString(tps));//DEBUG
if (tps.length == 0) return Global.instance.definitions.ALL_TYPE();
@@ -2754,6 +2786,29 @@ public class Type implements Modifiers, Kinds, TypeTags, EntryTags {
}
public static Type glb(Type[] tps) {
+ if (recCount == recLimit) {
+ giveUp = true;
+ return Global.instance.definitions.ALL_TYPE();
+ } else {
+ recCount++;
+ Type result = glb0(tps);
+ recCount--;
+ if (recCount == 0) {
+ if (giveUp) {
+ giveUp = false;
+ throw new Error("failure to compute greatest lower bound of types " +
+ ArrayApply.toString(tps, "", " and ", ";\n") +
+ "an approximation is: " + result + ";\n" +
+ "additional type annotations are needed");
+ } else {
+ giveUp = false;
+ }
+ }
+ return result;
+ }
+ }
+
+ public static Type glb0(Type[] tps) {
if (tps.length == 0) return Global.instance.definitions.ANY_TYPE();
// step one: eliminate redunandant types; return if one one is left
@@ -2769,10 +2824,8 @@ public class Type implements Modifiers, Kinds, TypeTags, EntryTags {
treftl = new Type.List(tps[i], treftl);
break;
case CompoundType(Type[] parents, Scope members):
- /*
if (!members.isEmpty())
comptl = new Type.List(tps[i], comptl);
- */
for (int j = 0; j < parents.length; j++)
treftl = new Type.List(parents[j], treftl);
break;
@@ -2787,7 +2840,7 @@ public class Type implements Modifiers, Kinds, TypeTags, EntryTags {
Type glbThisType = glbType.narrow();
// step 3: compute glb of all refinements.
- /*
+ Scope members = Scope.EMPTY;
if (comptl != List.EMPTY) {
Type[] comptypes = comptl.toArrayReverse();
Scope[] refinements = new Scope[comptypes.length];
@@ -2801,7 +2854,6 @@ public class Type implements Modifiers, Kinds, TypeTags, EntryTags {
Global.instance.definitions.ALLREF_TYPE(), treftl);
}
}
- */
// eliminate redudant typerefs
Type[] treftypes = elimRedundant(treftl.toArrayReverse(), false);
diff --git a/test/files/neg/bug44.check b/test/files/neg/bug44.check
index 9347758ac9..d40af82749 100644
--- a/test/files/neg/bug44.check
+++ b/test/files/neg/bug44.check
@@ -1,6 +1,6 @@
bug44.scala:2: type mismatch;
- found : scala.Object
- required: scala.Object { def t: scala.Int }
+ found : java.lang.Object
+ required: java.lang.Object { def t: scala.Int }
var x: Object { def t: Int; } = new Object() {
^
one error found
diff --git a/test/files/neg/refine.check b/test/files/neg/refine.check
index 271451e65e..18f4fad76a 100644
--- a/test/files/neg/refine.check
+++ b/test/files/neg/refine.check
@@ -1,6 +1,6 @@
refine.scala:3: type mismatch;
- found : scala.Object
- required: scala.Object { def t(): java.lang.String }
+ found : java.lang.Object
+ required: java.lang.Object { def t(): java.lang.String }
val x: Object { def t(): String } = new Object {
^
one error found
diff --git a/test/files/neg/vincent1.check b/test/files/neg/vincent1.check
index a9f60e9ad2..aa65a52341 100644
--- a/test/files/neg/vincent1.check
+++ b/test/files/neg/vincent1.check
@@ -1,4 +1,4 @@
-vincent1.scala:7: type x.type escapes its defining scope as part of scala.Object { type T = x.T }
+vincent1.scala:7: type x.type escapes its defining scope as part of java.lang.Object with scala.ScalaObject { type T = x.T }
class Functor(x: A) { type T = x.T }
^
vincent1.scala:9: type x.type escapes its defining scope as part of test.B { type T = x.T }
diff --git a/test/files/pos/MailBox.scala b/test/files/pos/MailBox.scala
index 9094b73399..3a633e3fbe 100644
--- a/test/files/pos/MailBox.scala
+++ b/test/files/pos/MailBox.scala
@@ -2,7 +2,7 @@ package test;
import scala.concurrent._;
-class MailBox with Monitor {
+class MailBox {
private class LinkedList[a] {
var elem: a = _;
var next: LinkedList[a] = null;
diff --git a/test/neg/bug44.check b/test/neg/bug44.check
index 9347758ac9..d40af82749 100644
--- a/test/neg/bug44.check
+++ b/test/neg/bug44.check
@@ -1,6 +1,6 @@
bug44.scala:2: type mismatch;
- found : scala.Object
- required: scala.Object { def t: scala.Int }
+ found : java.lang.Object
+ required: java.lang.Object { def t: scala.Int }
var x: Object { def t: Int; } = new Object() {
^
one error found
diff --git a/test/neg/refine.check b/test/neg/refine.check
index 271451e65e..18f4fad76a 100644
--- a/test/neg/refine.check
+++ b/test/neg/refine.check
@@ -1,6 +1,6 @@
refine.scala:3: type mismatch;
- found : scala.Object
- required: scala.Object { def t(): java.lang.String }
+ found : java.lang.Object
+ required: java.lang.Object { def t(): java.lang.String }
val x: Object { def t(): String } = new Object {
^
one error found
diff --git a/test/neg/vincent1.check b/test/neg/vincent1.check
index a9f60e9ad2..aa65a52341 100644
--- a/test/neg/vincent1.check
+++ b/test/neg/vincent1.check
@@ -1,4 +1,4 @@
-vincent1.scala:7: type x.type escapes its defining scope as part of scala.Object { type T = x.T }
+vincent1.scala:7: type x.type escapes its defining scope as part of java.lang.Object with scala.ScalaObject { type T = x.T }
class Functor(x: A) { type T = x.T }
^
vincent1.scala:9: type x.type escapes its defining scope as part of test.B { type T = x.T }
diff --git a/test/pos/MailBox.scala b/test/pos/MailBox.scala
index 9094b73399..3a633e3fbe 100644
--- a/test/pos/MailBox.scala
+++ b/test/pos/MailBox.scala
@@ -2,7 +2,7 @@ package test;
import scala.concurrent._;
-class MailBox with Monitor {
+class MailBox {
private class LinkedList[a] {
var elem: a = _;
var next: LinkedList[a] = null;