From 421f29573190fca94e595bbfe30619a23b052aad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 23 May 2015 10:20:53 +0200 Subject: Introduce harmonization of numeric arguments Harmonization is Dotty's alternative to Scala 2's notion of weak conformance. It is less powerful but also less entangled with the core type system. The idea is that in some specific contexts trees that all have primitive numeric types will be converted as necessary so that they all have the same numeric type. These tree sets are: - the two branches of an if - the alternatives of a match - the body together with the catch blocks of a try - the arguments of a vararg parameter Examples are in the test file, harmonize.scala. --- src/dotty/tools/dotc/typer/Applications.scala | 47 ++++++++++++++++++++------- src/dotty/tools/dotc/typer/Typer.scala | 10 ++++-- 2 files changed, 42 insertions(+), 15 deletions(-) (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index e1d3d243d..dadd2afdc 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -127,6 +127,9 @@ trait Applications extends Compatibility { self: Typer => */ protected def makeVarArg(n: Int, elemFormal: Type): Unit + /** If all `args` have primitive numeric types, make sure it's the same one */ + protected def harmonizeArgs(args: List[TypedArg]): List[TypedArg] + /** Signal failure with given message at position of given argument */ protected def fail(msg: => String, arg: Arg): Unit @@ -334,7 +337,14 @@ trait Applications extends Compatibility { self: Typer => addTyped(arg, formal) case _ => val elemFormal = formal.widenExpr.argTypesLo.head - args foreach (addTyped(_, elemFormal)) + val origConstraint = ctx.typerState.constraint + var typedArgs = args.map(typedArg(_, elemFormal)) + val harmonizedArgs = harmonizeArgs(typedArgs) + if (harmonizedArgs ne typedArgs) { + ctx.typerState.constraint = origConstraint + typedArgs = harmonizedArgs + } + typedArgs.foreach(addArg(_, elemFormal)) makeVarArg(args.length, elemFormal) } else args match { @@ -389,6 +399,7 @@ trait Applications extends Compatibility { self: Typer => def argType(arg: Tree, formal: Type): Type = normalize(arg.tpe, formal) def treeToArg(arg: Tree): Tree = arg def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg) + def harmonizeArgs(args: List[Tree]) = harmonize(args) } /** Subclass of Application for applicability tests with type arguments and value @@ -405,6 +416,7 @@ trait Applications extends Compatibility { self: Typer => def argType(arg: Type, formal: Type): Type = arg def treeToArg(arg: Tree): Type = arg.tpe def isVarArg(arg: Type): Boolean = arg.isRepeatedParam + def harmonizeArgs(args: List[Type]) = harmonizeTypes(args) } /** Subclass of Application for type checking an Apply node, where @@ -430,6 +442,8 @@ trait Applications extends Compatibility { self: Typer => typedArgBuf += seqToRepeated(seqLit) } + def harmonizeArgs(args: List[TypedArg]) = harmonize(args) + override def appPos = app.pos def fail(msg: => String, arg: Trees.Tree[T]) = { @@ -1025,25 +1039,34 @@ trait Applications extends Compatibility { self: Typer => } } - def harmonize(trees: List[Tree])(implicit ctx: Context): List[Tree] = { - def numericClasses(trees: List[Tree], acc: Set[Symbol]): Set[Symbol] = trees match { - case tree :: trees1 => - val sym = tree.tpe.typeSymbol - if (sym.isNumericValueClass && tree.tpe.isRef(sym)) - numericClasses(trees1, acc + sym) - else - Set() + private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = { + def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match { + case t :: ts1 => + val sym = tpe(t).widen.classSymbol + if (sym.isNumericValueClass) numericClasses(ts1, acc + sym) + else Set() case Nil => acc } - val clss = numericClasses(trees, Set()) + val clss = numericClasses(ts, Set()) if (clss.size > 1) { val lub = defn.ScalaNumericValueClassList.find(lubCls => clss.forall(defn.isValueSubClass(_, lubCls))).get.typeRef - trees.mapConserve(tree => adaptInterpolated(tree, lub, tree)) + ts.mapConserve(adapt(_, lub)) } - else trees + else ts } + + def harmonize(trees: List[Tree])(implicit ctx: Context): List[Tree] = { + def adapt(tree: Tree, pt: Type): Tree = tree match { + case cdef: CaseDef => tpd.cpy.CaseDef(cdef)(body = adapt(cdef.body, pt)) + case _ => adaptInterpolated(tree, pt, tree) + } + harmonizeWith(trees)(_.tpe, adapt) + } + + def harmonizeTypes(tpes: List[Type])(implicit ctx: Context): List[Type] = + harmonizeWith(tpes)(identity, (tp, pt) => pt) } /* diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 9fecc9742..ea19dd1c9 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -493,7 +493,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val cond1 = typed(tree.cond, defn.BooleanType) val thenp1 = typed(tree.thenp, pt) val elsep1 = typed(tree.elsep orElse untpd.unitLiteral withPos tree.pos, pt) - assignType(cpy.If(tree)(cond1, thenp1, elsep1), thenp1, elsep1) + val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) + assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { @@ -629,7 +630,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit fullyDefinedType(sel1.tpe, "pattern selector", tree.pos)) val cases1 = typedCases(tree.cases, selType, pt) - assignType(cpy.Match(tree)(sel1, cases1), cases1) + val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]] + assignType(cpy.Match(tree)(sel1, cases2), cases2) } } @@ -737,7 +739,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val expr1 = typed(tree.expr, pt) val cases1 = typedCases(tree.cases, defn.ThrowableType, pt) val finalizer1 = typed(tree.finalizer, defn.UnitType) - assignType(cpy.Try(tree)(expr1, cases1, finalizer1), expr1, cases1) + val expr2 :: cases2x = harmonize(expr1 :: cases1) + val cases2 = cases2x.asInstanceOf[List[CaseDef]] + assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2) } def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Tree = track("typedThrow") { -- cgit v1.2.3