From 99393624e3bf001a6c20c7615ac88ef4201a93f8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 15 Dec 2013 12:26:04 +0100 Subject: Fixes related to SAM types. 1. Changes to SAMType extractor 2. Self names are no longer members of enclosing class 3. SAM-Type closures now print with their result type. 4. refactoring newSkolemSingleon ==> narrow --- src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- src/dotty/tools/dotc/core/TypeOps.scala | 3 -- src/dotty/tools/dotc/core/Types.scala | 39 ++++++++++++++++------ src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 +-- src/dotty/tools/dotc/typer/Namer.scala | 5 +-- src/dotty/tools/dotc/typer/Typer.scala | 21 ++++++++---- 6 files changed, 50 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 7a8df114b..b441da6b4 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -413,7 +413,7 @@ class TypeComparer(initctx: Context) extends DotClass { val tparams = tp1.typeParams val hkArgs = tp2.typeArgs (hkArgs.length == tparams.length) && { - val base = ctx.newSkolemSingleton(tp1) + val base = tp1.narrow (tparams, hkArgs).zipped.forall { (tparam, hkArg) => base.memberInfo(tparam) <:< hkArg.bounds // TODO: base.memberInfo needed? } && diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index d9e5d7f96..cce6cc350 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -6,9 +6,6 @@ import util.SimpleMap trait TypeOps { this: Context => - /** A prefix-less termRef to a new skolem symbol that has the given type as info */ - def newSkolemSingleton(underlying: Type) = TermRef(NoPrefix, newSkolem(underlying)) - final def asSeenFrom(tp: Type, pre: Type, cls: Symbol, theMap: AsSeenFromMap): Type = { def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = ctx.debugTraceIndented(s"toPrefix($pre, $cls, $thiscls)") { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 1b05594e6..22d84c4e1 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -596,6 +596,9 @@ object Types { def underlyingIfRepeated(implicit ctx: Context): Type = this.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) + /** A prefix-less termRef to a new skolem symbol that has the given type as info */ + def narrow(implicit ctx: Context): TermRef = TermRef(NoPrefix, ctx.newSkolem(this)) + // ----- Normalizing typerefs over refined types ---------------------------- /** If this is a refinement type that has a refinement for `name` (which might be followed @@ -1987,7 +1990,7 @@ object Types { /** An extractor for single abstract method types. * A type is a SAM type if it is a reference to a class or trait, which * - * - has a single abstract method with a non-dependent method type (ExprType + * - has a single abstract method with a method type (ExprType * and PolyType not allowed!) * - can be instantiated without arguments or with just () as argument. * @@ -1995,34 +1998,50 @@ object Types { * denotation of the single abstract method as a member of the type. */ object SAMType { - def isInstantiatable(tp: Type)(implicit ctx: Context): Boolean = tp match { - case tp: TypeRef => - isInstantiatable(tp.info) + def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match { case tp: ClassInfo => def zeroParams(tp: Type): Boolean = tp match { - case pt: PolyType => zeroParams(pt) + case pt: PolyType => zeroParams(pt.resultType) case mt: MethodType => mt.paramTypes.isEmpty && !mt.resultType.isInstanceOf[MethodType] case et: ExprType => true case _ => false } - val noParamsNeeded = (tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info) // !!! needs to be adapted once traits have parameters - val selfTypeFeasible = tp.typeRef <:< tp.selfType - noParamsNeeded && selfTypeFeasible + if ((tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info)) tp // !!! needs to be adapted once traits have parameters + else NoType + case tp: TypeRef => + zeroParamClass(tp.underlying) case tp: RefinedType => - isInstantiatable(tp.underlying) + zeroParamClass(tp.underlying) case tp: TypeVar => - isInstantiatable(tp.underlying) + zeroParamClass(tp.underlying) + case _ => + NoType + } + def isInstantiatable(tp: Type)(implicit ctx: Context): Boolean = zeroParamClass(tp) match { + case cinfo: ClassInfo => + val tref = tp.narrow + val selfType = cinfo.selfType.asSeenFrom(tref, cinfo.cls) + tref <:< selfType case _ => false } def unapply(tp: Type)(implicit ctx: Context): Option[SingleDenotation] = if (isInstantiatable(tp)) { val absMems = tp.abstractTermMembers + // println(s"absMems: ${absMems map (_.show) mkString ", "}") if (absMems.size == 1) absMems.head.info match { case mt: MethodType if !mt.isDependent => Some(absMems.head) case _=> None } + else if (tp isRef defn.PartialFunctionClass) + // To maintain compatibility with 2.x, we treat PartialFunction specially, + // pretending it is a SAM type. In the future it would be better to merge + // Function and PartialFunction, have Function1 contain a isDefinedAt method + // def isDefinedAt(x: T) = true + // and overwrite that method whenever the function body is a sequence of + // case clauses. + absMems.find(_.symbol.name == nme.apply) else None } else None diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9fc8cb513..d652feb51 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -201,9 +201,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec(GlobalPrec) { "if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _) } - case Closure(env, ref, _) => + case Closure(env, ref, target) => "closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~ - toTextGlobal(ref) ~ ")" + toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")" case Match(sel, cases) => if (sel.isEmpty) blockText(cases) else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 3ddd3980b..9627065b2 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -229,7 +229,8 @@ class Namer { typer: Typer => def inClassContext(selfInfo: DotClass /* Should be Type | Symbol*/)(implicit ctx: Context): Context = { val localCtx: Context = ctx.fresh.withNewScope selfInfo match { - case sym: Symbol if sym.exists && sym.name != nme.WILDCARD => localCtx.enter(sym) + case sym: Symbol if sym.exists && sym.name != nme.WILDCARD => + localCtx.scope.asInstanceOf[MutableScope].enter(sym) case _ => } localCtx @@ -409,7 +410,7 @@ class Namer { typer: Typer => tp & itpe } } - // println(s"final inherited for $sym: ${inherited.toString}") !!! + // println(s"final inherited for $sym: ${inherited.toString}") !!! // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") def rhsType = adapt(typedAheadExpr(mdef.rhs), WildcardType).tpe.widen def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index b2796f62f..dda50a323 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -506,16 +506,18 @@ class Typer extends Namer with Applications with Implicits { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) typed(cpy.AppliedTypeTree(tree, - ref(defn.FunctionClass(args.length).typeRef), args :+ body), pt) + untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) else { val params = args.asInstanceOf[List[ValDef]] val protoFormals: List[Type] = pt match { case _ if pt isRef defn.FunctionClass(params.length) => pt.typeArgs take params.length case SAMType(meth) => + // println(s"SAMType $pt") val MethodType(_, paramTypes) = meth.info paramTypes case _ => + // println(s"Neither fucntion nor SAM type $pt") params map alwaysWildcardType } val inferredParams: List[untpd.ValDef] = @@ -527,7 +529,7 @@ class Typer extends Namer with Applications with Implicits { else { val ofFun = if (nme.syntheticParamNames(args.length + 1) contains param.name) - s" for expanded function ${tree.show}" + s" of expanded function ${tree.show}" else "" errorType(s"missing parameter type for parameter ${param.name}$ofFun, expected = ${pt.show}", param.pos) } @@ -540,14 +542,21 @@ class Typer extends Namer with Applications with Implicits { def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = track("typedClosure") { val env1 = tree.env mapconserve (typed(_)) val meth1 = typedUnadapted(tree.meth) - val ownType = meth1.tpe.widen match { + val (ownType, target) = meth1.tpe.widen match { case mt: MethodType => - if (!mt.isDependent) mt.toFunctionType - else throw new Error(s"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}") // !!! DEBUG. Eventually, convert to an error? + pt match { + case SAMType(meth) if !defn.isFunctionType(pt) && mt <:< meth.info => + if (!isFullyDefined(pt, ForceDegree.all)) + ctx.error(i"result type of closure is an underspecified SAM type $pt", tree.pos) + (pt, TypeTree(pt)) + case _ => + if (!mt.isDependent) (mt.toFunctionType, EmptyTree) + else throw new Error(s"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}") // !!! DEBUG. Eventually, convert to an error? + } case tp => throw new Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}") } - cpy.Closure(tree, env1, meth1, EmptyTree).withType(ownType) + cpy.Closure(tree, env1, meth1, target).withType(ownType) } def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { -- cgit v1.2.3