From 93a326e160ea9ba822467377f87d798146925367 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Feb 2012 09:59:08 +0100 Subject: Changed erasure boxing/unboxing scheme to support value classes that wrap reference classes. --- src/compiler/scala/tools/nsc/ast/Trees.scala | 20 +++++++++ .../scala/tools/nsc/transform/Erasure.scala | 48 ++++++++++++++-------- .../scala/tools/nsc/typechecker/Typers.scala | 5 +-- 3 files changed, 53 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 83b6252b26..733c5a6fb1 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -40,6 +40,12 @@ trait Trees extends reflect.internal.Trees { self: Global => case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type) extends TermTree with RefTree + /** Derived value class injection (equivalent to: new C(arg) after easure); only used during erasure + * The class C is stored as the symbol of the tree node. + */ + case class InjectDerivedValue(arg: Tree) + extends SymTree + /** emitted by typer, eliminated by refchecks */ case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree @@ -159,6 +165,8 @@ trait Trees extends reflect.internal.Trees { self: Global => traverser.traverse(lhs); traverser.traverse(rhs) case SelectFromArray(qualifier, selector, erasure) => traverser.traverse(qualifier) + case InjectDerivedValue(arg) => + traverser.traverse(arg) case ReferenceToBoxed(idt) => traverser.traverse(idt) case TypeTreeWithDeferredRefCheck() => @@ -170,6 +178,7 @@ trait Trees extends reflect.internal.Trees { self: Global => def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray + def InjectDerivedValue(tree: Tree, arg: Tree): InjectDerivedValue def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck } @@ -184,6 +193,8 @@ trait Trees extends reflect.internal.Trees { self: Global => new AssignOrNamedArg(lhs, rhs).copyAttrs(tree) def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree) + def InjectDerivedValue(tree: Tree, arg: Tree) = + new InjectDerivedValue(arg) def ReferenceToBoxed(tree: Tree, idt: Ident) = new ReferenceToBoxed(idt).copyAttrs(tree) def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match { @@ -207,6 +218,11 @@ trait Trees extends reflect.internal.Trees { self: Global => if (qualifier0 == qualifier) && (selector0 == selector) => t case _ => this.treeCopy.SelectFromArray(tree, qualifier, selector, erasure) } + def InjectDerivedValue(tree: Tree, arg: Tree) = tree match { + case t @ InjectDerivedValue(arg0) + if (arg0 == arg) => t + case _ => this.treeCopy.InjectDerivedValue(tree, arg) + } def ReferenceToBoxed(tree: Tree, idt: Ident) = tree match { case t @ ReferenceToBoxed(idt0) if (idt0 == idt) => t @@ -237,6 +253,9 @@ trait Trees extends reflect.internal.Trees { self: Global => case SelectFromArray(qualifier, selector, erasure) => transformer.treeCopy.SelectFromArray( tree, transformer.transform(qualifier), selector, erasure) + case InjectDerivedValue(arg) => + transformer.treeCopy.InjectDerivedValue( + tree, transformer.transform(arg)) case ReferenceToBoxed(idt) => transformer.treeCopy.ReferenceToBoxed( tree, transformer.transform(idt) match { case idt1: Ident => idt1 }) @@ -336,6 +355,7 @@ trait Trees extends reflect.internal.Trees { self: Global => case AssignOrNamedArg(lhs, rhs) => (eliminated by typer) case TypeTreeWithDeferredRefCheck() => (created and eliminated by typer) case SelectFromArray(_, _, _) => (created and eliminated by erasure) + case InjectDerivedValue(_) => (created and eliminated by erasure) */ diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 65b4890c8f..e0086dd81b 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -410,12 +410,14 @@ abstract class Erasure extends AddInterfaces /** The modifier typer which retypes with erased types. */ class Eraser(_context: Context) extends Typer(_context) { - private def isUnboxedType(tpe: Type) = tpe match { - case ErasedValueType(_) => true - case _ => isPrimitiveValueClass(tpe.typeSymbol) - } + private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) + + private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType] - private def isUnboxedValueMember(sym: Symbol) = + private def isDifferentErasedValueType(tpe: Type, other: Type) = + isErasedValueType(tpe) && (tpe ne other) + + private def isPrimitiveValueMember(sym: Symbol) = sym != NoSymbol && isPrimitiveValueClass(sym.owner) private def box(tree: Tree, target: => String): Tree = { @@ -534,14 +536,18 @@ abstract class Erasure extends AddInterfaces log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug if (tree.tpe <:< pt) tree - else if (isUnboxedType(tree.tpe) && !isUnboxedType(pt)) { + else if (isDifferentErasedValueType(tree.tpe, pt)) + adaptToType(box(tree, pt.toString), pt) + else if (isDifferentErasedValueType(pt, tree.tpe)) + adaptToType(unbox(tree, pt), pt) + else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { adaptToType(box(tree, pt.toString), pt) } else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) { assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) - } else if (pt <:< tree.tpe) - cast(tree, pt) - else if (isUnboxedType(pt) && !isUnboxedType(tree.tpe)) +// } else if (pt <:< tree.tpe) +// cast(tree, pt) + } else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe)) adaptToType(unbox(tree, pt), pt) else cast(tree, pt) @@ -564,7 +570,7 @@ abstract class Erasure extends AddInterfaces //Console.println("adaptMember: " + tree); tree match { case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) - if tree.symbol == Any_asInstanceOf || tree.symbol == Object_asInstanceOf => + if tree.symbol == Any_asInstanceOf => val qual1 = typedQualifier(qual, NOmode, ObjectClass.tpe) // need to have an expected type, see #3037 val qualClass = qual1.tpe.typeSymbol /* @@ -575,10 +581,10 @@ abstract class Erasure extends AddInterfaces atPos(tree.pos)(Apply(Select(qual1, "to" + targClass.name), List())) else */ - if (isUnboxedType(targ.tpe)) unbox(qual1, targ.tpe) + if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) unbox(qual1, targ.tpe) else tree case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) - if tree.symbol == Any_isInstanceOf || tree.symbol == Object_asInstanceOf => + if tree.symbol == Any_isInstanceOf => targ.tpe match { case ErasedValueType(clazz) => targ.setType(clazz.tpe) case _ => @@ -598,12 +604,13 @@ abstract class Erasure extends AddInterfaces adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) else { var qual1 = typedQualifier(qual) - if ((isUnboxedType(qual1.tpe) && !isUnboxedValueMember(tree.symbol))) + if ((isPrimitiveValueType(qual1.tpe) && !isPrimitiveValueMember(tree.symbol)) || + isErasedValueType(qual1.tpe)) qual1 = box(qual1, "owner "+tree.symbol.owner) - else if (!isUnboxedType(qual1.tpe) && isUnboxedValueMember(tree.symbol)) + else if (!isPrimitiveValueType(qual1.tpe) && isPrimitiveValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) - if (isUnboxedValueMember(tree.symbol) && !isUnboxedType(qual1.tpe)) + if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) tree.symbol = NoSymbol else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) { assert(qual1.symbol.isStable, qual1.symbol); @@ -632,7 +639,14 @@ abstract class Erasure extends AddInterfaces */ override protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = { val tree1 = try { - super.typed1(adaptMember(tree), mode, pt) + tree match { + case InjectDerivedValue(arg) => + val clazz = tree.symbol + util.trace("transforming inject "+arg+":"+underlyingOfValueClass(clazz)+"/"+ErasedValueType(clazz))( + typed1(arg, mode, underlyingOfValueClass(clazz)) setType ErasedValueType(clazz)) + case _ => + super.typed1(adaptMember(tree), mode, pt) + } } catch { case er: TypeError => Console.println("exception when typing " + tree) @@ -968,7 +982,7 @@ abstract class Erasure extends AddInterfaces tree case Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)) if (tpt.tpe.typeSymbol.isDerivedValueClass) => - arg + InjectDerivedValue(arg) setSymbol tpt.tpe.typeSymbol case Apply(fn, args) => def qualifier = fn match { case Select(qual, _) => qual diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 02b7dae544..2b9880ff65 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1209,12 +1209,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.error(acc.pos, "Type of parameter of value class may not be a type variable") for (stat <- body) if (!treeInfo.isAllowedInUniversalTrait(stat) && !isUnderlyingAcc(stat.symbol)) - unit.error(stat.pos, + unit.error(stat.pos, if (stat.symbol hasFlag PARAMACCESSOR) "Illegal parameter for value class" else "This statement is not allowed in value class: "+stat) case x => - println(clazz.info.decls.toList) - unit.error(clazz.pos, "Value class needs to have exactly one public val parameter, found: "+x.mkString(", ")) + unit.error(clazz.pos, "Value class needs to have exactly one public val parameter") } for (tparam <- clazz.typeParams) if (tparam hasAnnotation definitions.SpecializedClass) -- cgit v1.2.3