8000 Simplify type bounds. by paulp · Pull Request #2405 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Simplify type bounds. #2405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Simplify type bounds.
I started out looking to limit the noise from empty type
bounds, i.e. the endless repetition of

  class A[T >: _root_.scala.Nothing <: _root_.scala.Any]

This led me to be reminded of all the unnecessary and
in fact damaging overreaches which are performed during parsing.
Why should a type parameter for which no bounds are
specified be immediately encoded with this giant tree:

  TypeBounds(
    Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Nothing),
    Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Any)
  )

...which must then be manually recognized as empty type bounds?
Truly, this is madness.

 - It deftly eliminates the possibility of recognizing
   whether the user wrote "class A[T]" or "class A[T >: Nothing]"
   or "class A[T <: Any]" or specified both bounds. The fact
   that these work out the same internally does not imply the
   information should be exterminated even before parsing completes.
 - It burdens everyone who must recognize type bounds trees,
   such as this author
 - It is far less efficient than the obvious encoding
 - It offers literally no advantage whatsoever

Encode empty type bounds as

  TypeBounds(EmptyTree, EmptyTree)

What could be simpler.
  • Loading branch information
paulp committed Apr 17, 2013
commit a196376f3fbc355e33048e0cc0083c7434b041d0
15 changes: 7 additions & 8 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2281,16 +2281,15 @@ self =>
* }}}
*/
def typeBounds(): TypeBoundsTree = {
val t = TypeBoundsTree(
bound(SUPERTYPE, tpnme.Nothing),
bound(SUBTYPE, tpnme.Any)
)
t setPos wrappingPos(List(t.hi, t.lo))
val start = in.offset
val lo = bound(SUPERTYPE)
val hi = bound(SUBTYPE)
val end = in.offset
val point = if (!lo.isEmpty) lo.pos.startOrPoint else if (!hi.isEmpty) hi.pos.startOrPoint else start
TypeBoundsTree(lo, hi) setPos r2p(start, point, end)
}

def bound(tok: Int, default: TypeName): Tree =
if (in.token == tok) { in.nextToken(); typ() }
else atPos(o2p(in.lastOffset)) { rootScalaDot(default) }
def bound(tok: Int): Tree = if (in.token == tok) { in.nextToken(); typ() } else EmptyTree

/* -------- DEFS ------------------------------------------- */

Expand Down
22 changes: 4 additions & 18 deletions src/compiler/scala/tools/nsc/javac/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
if (in.token == QMARK) {
val pos = in.currentPos
in.nextToken
var lo: Tree = TypeTree(NothingClass.tpe)
var hi: Tree = TypeTree(AnyClass.tpe)
if (in.token == EXTENDS) {
in.nextToken
hi = typ()
} else if (in.token == SUPER) {
in.nextToken
lo = typ()
}
val hi = if (in.token == EXTENDS) { in.nextToken ; typ() } else EmptyTree
val lo = if (in.token == SUPER) { in.nextToken ; typ() } else EmptyTree
val tdef = atPos(pos) {
TypeDef(
Modifiers(Flags.JAVA | Flags.DEFERRED),
Expand Down Expand Up @@ -453,15 +446,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
def typeParam(): TypeDef =
atPos(in.currentPos) {
val name = identForType()
val hi =
if (in.token == EXTENDS) {
in.nextToken
bound()
} else {
scalaDot(tpnme.Any)
}
TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM), name, List(),
TypeBoundsTree(scalaDot(tpnme.Nothing), hi))
val hi = if (in.token == EXTENDS) { in.nextToken ; bound() } else EmptyTree
TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM), name, Nil, TypeBoundsTree(EmptyTree, hi))
}

def bound(): Tree =
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5473,8 +5473,8 @@ trait Typers extends Modes with Adaptations with Tags {
}

def typedTypeBoundsTree(tree: TypeBoundsTree) = {
val lo1 = typedType(tree.lo, mode)
val hi1 = typedType(tree.hi, mode)
val lo1 = if (tree.lo.isEmpty) TypeTree(NothingTpe) else typedType(tree.lo, mode)
val hi1 = if (tree.hi.isEmpty) TypeTree(AnyTpe) else typedType(tree.hi, mode)
treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe)
}

Expand Down
9 changes: 8 additions & 1 deletion src/reflect/scala/reflect/internal/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,14 @@ trait Printers extends api.Printers { self: SymbolTable =>
print(tp); printRow(args, "[", ", ", "]")

case TypeBoundsTree(lo, hi) =>
printOpt(" >: ", lo); printOpt(" <: ", hi)
// Avoid printing noisy empty typebounds everywhere
// Untyped empty bounds are not printed by printOpt,
// but after they are typed we have to exclude Nothing/Any.
if ((lo.tpe eq null) || !(lo.tpe =:= definitions.NothingTpe))
printOpt(" >: ", lo)

if ((hi.tpe eq null) || !(hi.tpe =:= definitions.AnyTpe))
printOpt(" <: ", hi)

case ExistentialTypeTree(tpt, whereClauses) =>
print(tpt);
Expand Down
4 changes: 2 additions & 2 deletions test/files/presentation/callcc-interpreter.check
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ retrieved 64 members
askType at CallccInterpreter.scala(14,21)
================================================================================
[response] askTypeAt at (14,21)
def unitM[A >: Nothing <: Any](a: A): callccInterpreter.M[A] = callccInterpreter.this.M.apply[A](((c: A => callccInterpreter.Answer) => c.apply(a)))
def unitM[A](a: A): callccInterpreter.M[A] = callccInterpreter.this.M.apply[A](((c: A => callccInterpreter.Answer) => c.apply(a)))
================================================================================

askType at CallccInterpreter.scala(16,12)
================================================================================
[response] askTypeAt at (16,12)
def id[A >: Nothing <: Any]: A => A = ((x: A) => x)
def id[A]: A => A = ((x: A) => x)
================================================================================

askType at CallccInterpreter.scala(17,25)
Expand Down
8 changes: 2 additions & 6 deletions test/files/run/analyzerPlugins.check
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ pluginsPt(?, Trees$Assign) [7]
pluginsPt(?, Trees$Block) [7]
pluginsPt(?, Trees$ClassDef) [2]
pluginsPt(?, Trees$DefDef) [14]
pluginsPt(?, Trees$Ident) [49]
pluginsPt(?, Trees$Ident) [48]
pluginsPt(?, Trees$If) [2]
pluginsPt(?, Trees$Literal) [20]
pluginsPt(?, Trees$New) [5]
pluginsPt(?, Trees$PackageDef) [1]
pluginsPt(?, Trees$Return) [1]
pluginsPt(?, Trees$Select) [51]
pluginsPt(?, Trees$Select) [47]
pluginsPt(?, Trees$Super) [2]
pluginsPt(?, Trees$This) [20]
pluginsPt(?, Trees$TypeApply) [3]
Expand Down Expand Up @@ -111,7 +111,6 @@ pluginsTyped(<notype>, Trees$DefDef) [14]
pluginsTyped(<notype>, Trees$PackageDef) [1]
pluginsTyped(<notype>, Trees$TypeDef) [1]
pluginsTyped(<notype>, Trees$ValDef) [23]
pluginsTyped(<root>, Trees$Ident) [1]
pluginsTyped(=> Boolean @testAnn, Trees$Select) [1]
pluginsTyped(=> Double, Trees$Select) [4]
pluginsTyped(=> Int, Trees$Select) [5]
Expand Down Expand Up @@ -150,7 +149,6 @@ pluginsTyped(List[Any], Trees$Apply) [1]
pluginsTyped(List[Any], Trees$Select) [1]
pluginsTyped(List[Any], Trees$TypeTree) [3]
pluginsTyped(Nothing, Trees$Return) [1]
pluginsTyped(Nothing, Trees$Select) [2]
pluginsTyped(Object, Trees$Apply) [1]
pluginsTyped(String @testAnn, Trees$Ident) [1]
pluginsTyped(String @testAnn, Trees$Select) [1]
Expand Down Expand Up @@ -183,8 +181,6 @@ pluginsTyped(scala.annotation.TypeConstraint, Trees$Select) [4]
pluginsTyped(scala.collection.immutable.List.type, Trees$Select) [2]
pluginsTyped(scala.collection.immutable.StringOps, Trees$ApplyImplicitView) [2]
pluginsTyped(scala.collection.mutable.WrappedArray[Any], Trees$Apply) [1]
pluginsTyped(scala.type, Trees$Ident) [1]
pluginsTyped(scala.type, Trees$Select) [1]
pluginsTyped(str.type, Trees$Ident) [3]
pluginsTyped(testAnn, Trees$Apply) [5]
pluginsTyped(testAnn, Trees$Ident) [5]
Expand Down
4 changes: 2 additions & 2 deletions test/files/run/macro-typecheck-macrosdisabled.check
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
$treecreator1.super.<init>();
()
};
def apply[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Tree = {
def apply[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Tree = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
$u.Literal.apply($u.Constant.apply(2))
Expand All @@ -20,7 +20,7 @@
$typecreator2.super.<init>();
()
};
def apply[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Type = {
def apply[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Type = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
$u.ConstantType.apply($u.Constant.apply(2))
Expand Down
4 changes: 2 additions & 2 deletions test/files/run/macro-typecheck-macrosdisabled2.check
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
$treecreator1.super.<init>();
()
};
def apply[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Tree = {
def apply[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Tree = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
$u.Apply.apply($u.Select.apply($u.build.Ident($m.staticModule("scala.Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2))))
Expand All @@ -20,7 +20,7 @@
$typecreator2.super.<init>();
()
};
def apply[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Type = {
def apply[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Type = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
$u.TypeRef.apply($u.ThisType.apply($m.staticPackage("scala").asModule.moduleClass), $m.staticClass("scala.Array"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asType.toTypeConstructor))
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/reify_ann1a.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
@new ann(List.apply("1a")) @new ann(List.apply("1b")) class C[@new ann(List.apply("2a")) @new ann(List.apply("2b")) T >: Nothing <: Any] extends AnyRef {
@new ann(List.apply("1a")) @new ann(List.apply("1b")) class C[@new ann(List.apply("2a")) @new ann(List.apply("2b")) T] extends AnyRef {
@new ann(List.apply("3a")) @new ann(List.apply("3b")) <paramaccessor> private[this] val x: T @ann(List.apply("4a")) @ann(List.apply("4b")) = _;
def <init>(@new ann(List.apply("3a")) @new ann(List.apply("3b")) x: T @ann(List.apply("4a")) @ann(List.apply("4b"))) = {
super.<init>();
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/reify_ann1b.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
@new ann(bar = "1a") @new ann(bar = "1b") class C[@new ann(bar = "2a") @new ann(bar = "2b") T >: Nothing <: Any] extends AnyRef {
@new ann(bar = "1a") @new ann(bar = "1b") class C[@new ann(bar = "2a") @new ann(bar = "2b") T] extends AnyRef {
@new ann(bar = "3a") @new ann(bar = "3b") <paramaccessor> private[this] val x: T @ann(bar = "4a") @ann(bar = "4b") = _;
def <init>(@new ann(bar = "3a") @new ann(bar = "3b") x: T @ann(bar = "4a") @ann(bar = "4b")) = {
super.<init>();
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/reify_ann2a.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
()
}
};
@new ann(List.apply("1a")) @new ann(List.apply("1b")) class C[@new ann(List.apply("2a")) @new ann(List.apply("2b")) T >: Nothing <: Any] extends AnyRef {
@new ann(List.apply("1a")) @new ann(List.apply("1b")) class C[@new ann(List.apply("2a")) @new ann(List.apply("2b")) T] extends AnyRef {
@new ann(List.apply("3a")) @new ann(List.apply("3b")) <paramaccessor> private[this] val x: T @ann(List.apply("4a")) @ann(List.apply("4b")) = _;
def <init>(@new ann(List.apply("3a")) @new ann(List.apply("3b")) x: T @ann(List.apply("4a")) @ann(List.apply("4b"))) = {
super.<init>();
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/reify_ann3.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
class Tree[A >: Nothing <: Any, B >: Nothing <: Any] extends AnyRef {
class Tree[A, B] extends AnyRef {
@new inline @getter() final <paramaccessor> val key: A = _;
def <init>(key: A) = {
super.<init>();
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/t5527.check
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ package <empty> {
()
};
/** T */
type T >: _root_.scala.Nothing <: _root_.scala.Any;
type T;
/** f */
def f(i: Int): scala.Unit;
/** v */
Expand Down
0