diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9c6e83af0522..d9eb33b1dbd2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -196,11 +196,11 @@ class Namer { typer: Typer => import untpd._ - val TypedAhead: Property.Key[tpd.Tree] = new Property.Key - val ExpandedTree: Property.Key[untpd.Tree] = new Property.Key + val TypedAhead : Property.Key[tpd.Tree] = new Property.Key + val ExpandedTree : Property.Key[untpd.Tree] = new Property.Key val ExportForwarders: Property.Key[List[tpd.MemberDef]] = new Property.Key - val SymOfTree: Property.Key[Symbol] = new Property.Key - val Deriver: Property.Key[typer.Deriver] = new Property.Key + val SymOfTree : Property.Key[Symbol] = new Property.Key + val Deriver : Property.Key[typer.Deriver] = new Property.Key /** A partial map from unexpanded member and pattern defs and to their expansions. * Populated during enterSyms, emptied during typer. @@ -1409,26 +1409,30 @@ class Namer { typer: Typer => * the corresponding parameter where bound parameters are replaced by * Wildcards. */ - def rhsProto = sym.asTerm.name collect { - case DefaultGetterName(original, idx) => - val meth: Denotation = - if (original.isConstructorName && (sym.owner.is(ModuleClass))) - sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) - else - ctx.defContext(sym).denotNamed(original) - def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { - case params :: paramss1 => - if (idx < params.length) wildApprox(params(idx)) - else paramProto(paramss1, idx - params.length) - case nil => - WildcardType - } - val defaultAlts = meth.altsWith(_.hasDefaultParams) - if (defaultAlts.length == 1) - paramProto(defaultAlts.head.info.widen.paramInfoss, idx) - else - WildcardType - } getOrElse WildcardType + def rhsProto = mdef.tpt.getAttachment(Typer.RhsProto) + .getOrElse {{ + sym.asTerm.name collect { + case DefaultGetterName(original, idx) => + val meth: Denotation = + if (original.isConstructorName && (sym.owner.is(ModuleClass))) + sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) + else + ctx.defContext(sym).denotNamed(original) + def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { + case params :: paramss1 => + if (idx < params.length) wildApprox(params(idx)) + else paramProto(paramss1, idx - params.length) + case nil => + WildcardType + } + val defaultAlts = meth.altsWith(_.hasDefaultParams) + if (defaultAlts.length == 1) + paramProto(defaultAlts.head.info.widen.paramInfoss, idx) + else + WildcardType + } + }.getOrElse(WildcardType) + } // println(s"final inherited for $sym: ${inherited.toString}") !!! // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") @@ -1443,7 +1447,12 @@ class Namer { typer: Typer => val tp1 = tp.widenTermRefExpr.simplified match case ctp: ConstantType if isInlineVal => ctp case ref: TypeRef if ref.symbol.is(ModuleClass) => tp - case tp => tp.widenUnion + case tp => + if true then ctx.typeComparer.widenInferred(tp, rhsProto) + else rhsProto match { + case OrType(_, _) | WildcardType(TypeBounds(_, OrType(_, _))) => tp.widen + case _ => tp.widenUnion + } tp1.dropRepeatedAnnot } @@ -1496,26 +1505,13 @@ class Namer { typer: Typer => val tpe = tpFun(paramss.head) if (isFullyDefined(tpe, ForceDegree.none)) tpe else typedAheadExpr(mdef.rhs, tpe).tpe - case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) => - val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe - mdef match { - case mdef: DefDef if mdef.name == nme.ANON_FUN => - val hygienicType = avoid(rhsType, paramss.flatten) - if (!hygienicType.isValueType || !(hygienicType <:< tpt.tpe)) - ctx.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" + - i"it is not a supertype of the hygienic type $hygienicType", mdef.sourcePos) - //println(i"lifting $rhsType over $paramss -> $hygienicType = ${tpt.tpe}") - //println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe }) - case _ => - } - WildcardType case _ => WildcardType } - val memTpe = paramFn(checkSimpleKinded(typedAheadType(mdef.tpt, tptProto)).tpe) + val mbrTpe = paramFn(checkSimpleKinded(typedAheadType(mdef.tpt, tptProto)).tpe) if (ctx.explicitNulls && mdef.mods.is(JavaDefined)) - JavaNullInterop.nullifyMember(sym, memTpe, mdef.mods.isAllOf(JavaEnumValue)) - else memTpe + JavaNullInterop.nullifyMember(sym, mbrTpe, mdef.mods.isAllOf(JavaEnumValue)) + else mbrTpe } /** The type signature of a DefDef with given symbol */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fdd19deb1e1e..d585bb1a9c81 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -77,6 +77,11 @@ object Typer { */ private val DroppedEmptyArgs = new Property.Key[Unit] + /** An attachment on the untyped TypeTree() that is the result type of a desugared + * anonymous function. It communicates the expected type of the closure's right-hand side. + */ + val RhsProto: Property.Key[Type] = new Property.StickyKey + /** An attachment that indicates a failed conversion or extension method * search was tried on a tree. This will in some cases be reported in error messages */ @@ -1158,7 +1163,20 @@ class Typer extends Namer else cpy.ValDef(param)( tpt = untpd.TypeTree( inferredParamType(param, protoFormal(i)).translateFromRepeated(toArray = false))) - desugar.makeClosure(inferredParams, fnBody, resultTpt, isContextual) + val anonfunTpt = resultTpt match + case untpd.TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) => + // If the expected result type is not fully defined, do not use it as the anonymous + // function result type, since this would risk polluting constraints for variables + // in that result type with references to local parameters. See i8861.scala for a + // problematic case. Instead, keep a sanitized version where variables are replaced by + // wildcards in a RhsProto attachment. It will be picked up by Namer + // as the expected type of the closure's body. + untpd.TypeTree() + .withSpan(tpt.span) + .withAttachment(RhsProto, wildApprox(tpt.tpe)) + case _ => + resultTpt + desugar.makeClosure(inferredParams, fnBody, anonfunTpt, isContextual) } typed(desugared, pt) } diff --git a/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala b/tests/pending/run-macros/quote-matcher-symantics-3/quoted_1.scala similarity index 100% rename from tests/run-macros/quote-matcher-symantics-3/quoted_1.scala rename to tests/pending/run-macros/quote-matcher-symantics-3/quoted_1.scala diff --git a/tests/run-macros/quote-matcher-symantics-3/quoted_2.scala b/tests/pending/run-macros/quote-matcher-symantics-3/quoted_2.scala similarity index 100% rename from tests/run-macros/quote-matcher-symantics-3/quoted_2.scala rename to tests/pending/run-macros/quote-matcher-symantics-3/quoted_2.scala diff --git a/tests/neg/i6565.scala b/tests/pos/i6565.scala similarity index 84% rename from tests/neg/i6565.scala rename to tests/pos/i6565.scala index d5fab12842d3..d39e6e31bfa6 100644 --- a/tests/neg/i6565.scala +++ b/tests/pos/i6565.scala @@ -12,6 +12,6 @@ lazy val ok: Lifted[String] = { // ok despite map returning a union point("a").map(_ => if true then "foo" else error) // ok } -lazy val bad: Lifted[String] = { // found Lifted[Object] - point("a").flatMap(_ => point("b").map(_ => if true then "foo" else error)) // error +lazy val nowAlsoOK: Lifted[String] = { + point("a").flatMap(_ => point("b").map(_ => if true then "foo" else error)) // now also OK } diff --git a/tests/run/i8861.check b/tests/run/i8861.check new file mode 100644 index 000000000000..317e9677c3bc --- /dev/null +++ b/tests/run/i8861.check @@ -0,0 +1,2 @@ +hello +hello diff --git a/tests/run/i8861.scala b/tests/run/i8861.scala new file mode 100644 index 000000000000..87f1884f6155 --- /dev/null +++ b/tests/run/i8861.scala @@ -0,0 +1,31 @@ +object Test { + sealed trait Container { s => + type A + def visit[R](int: IntV & s.type => R, str: StrV & s.type => R): R + } + final class IntV extends Container { s => + type A = Int + val i: Int = 42 + def visit[R](int: IntV & s.type => R, str: StrV & s.type => R): R = int(this) + } + final class StrV extends Container { s => + type A = String + val t: String = "hello" + def visit[R](int: IntV & s.type => R, str: StrV & s.type => R): R = str(this) + } + + def minimalOk[R](c: Container { type A = R }): R = c.visit[R]( + int = vi => vi.i : vi.A, + str = vs => vs.t : vs.A + ) + def minimalFail[M](c: Container { type A = M }): M = c.visit( + int = vi => vi.i : vi.A, + str = vs => vs.t : vs.A // error + ) + + def main(args: Array[String]): Unit = { + val e: Container { type A = String } = new StrV + println(minimalOk(e)) // this one prints "hello" + println(minimalFail(e)) // this one fails with ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer + } +} \ No newline at end of file