From c0864dad777f91dae39c510781326f5f96e10d56 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 16 Jan 2024 19:04:52 +0000 Subject: [PATCH 1/2] Make apply proxies work with overloaded ctors [Cherry-picked 72fa0438b9181dca78f2663f14a0583342a95f69] --- .../src/dotty/tools/dotc/typer/Typer.scala | 25 +++++++++++++------ tests/pos/i19201.scala | 12 +++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 tests/pos/i19201.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7728ab06d089..9bfac06f15af 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4238,7 +4238,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } /** Convert constructor proxy reference to a new expression */ - def newExpr = + def newExpr(ctorResultType: Type) = val qual = qualifier(tree) val tpt = qual match case Ident(name) => @@ -4249,7 +4249,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cpy.Ident(qual)(qual.symbol.name.sourceModuleName.toTypeName) case _ => errorTree(tree, em"cannot convert from $tree to an instance creation expression") - val tycon = tree.tpe.widen.finalResultType.underlyingClassRef(refinementOK = false) + val tycon = ctorResultType.underlyingClassRef(refinementOK = false) typed( untpd.Select( untpd.New(untpd.TypedSplice(tpt.withType(tycon))), @@ -4257,9 +4257,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer pt) .showing(i"convert creator $tree -> $result", typr) - def isApplyProxy(tree: Tree) = tree match - case Select(_, nme.apply) => tree.symbol.isAllOf(ApplyProxyFlags) - case _ => false + def applyProxy(tree: Tree) = tree match + case Select(_, nme.apply) => + tree.denot.altsWith(_.isAllOf(ApplyProxyFlags)) match + case denot :: _ => + // any of the constructors will do, in order to get the result type, so using the first one + denot.info.widen.finalResultType + case _ => NoType + case _ => NoType tree match { case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[?] | _: Closure => tree @@ -4273,7 +4278,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if needsTupledDual(ref, pt) && Feature.autoTuplingEnabled => adapt(tree, pt.tupledDual, locked) case _ => - adaptOverloaded(ref) + val ctorResultType = applyProxy(tree) + if ctorResultType.exists then newExpr(ctorResultType) + else adaptOverloaded(ref) } case poly: PolyType if !(ctx.mode is Mode.Type) && dummyTreeOfType.unapply(tree).isEmpty => @@ -4282,7 +4289,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Test case was but i18695.scala, but it got fixed by a different tweak in #18719. // We leave test for this condition in as a defensive measure in case // it arises somewhere else. - if isApplyProxy(tree) then newExpr + val ctorResultType = applyProxy(tree) + if ctorResultType.exists then newExpr(ctorResultType) else if pt.isInstanceOf[PolyProto] then tree else var typeArgs = tree match @@ -4296,7 +4304,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer readaptSimplified(handleStructural(tree)) else pt match { case pt: FunProto => - if isApplyProxy(tree) then newExpr + val ctorResultType = applyProxy(tree) + if ctorResultType.exists then newExpr(ctorResultType) else adaptToArgs(wtp, pt) case pt: PolyProto if !wtp.isImplicitMethod => tryInsertApplyOrImplicit(tree, pt, locked)(tree) // error will be reported in typedTypeApply diff --git a/tests/pos/i19201.scala b/tests/pos/i19201.scala new file mode 100644 index 000000000000..4d9fa7bf3df1 --- /dev/null +++ b/tests/pos/i19201.scala @@ -0,0 +1,12 @@ +class Person( + val firstName: String, + val lastName: String, + val birthYear: Int = -1, + val address: String = "" +): + // Works if remove this constructor + def this() = this("John", "Doe") + +class Test: + def p1 = Person("First", "Last") // was: Type Error: none of the overloads.. match arguments + def p2 = Person("Josh", "Joe", 1912, "Main Street") From 5af5c711795d01feaaba18f3393ee7acd877101d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 22 Jan 2024 10:26:01 +0000 Subject: [PATCH 2/2] Use EmptyTree and Tree#orElse [Cherry-picked 0b233c3f1d976950f8e8b6e1a2ca515348f4de2e] --- .../src/dotty/tools/dotc/typer/Typer.scala | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9bfac06f15af..e3f1f99e1bb5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4237,8 +4237,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => } - /** Convert constructor proxy reference to a new expression */ - def newExpr(ctorResultType: Type) = + /** If `tree` is a constructor proxy reference, convert it to a `new` expression, + * otherwise return EmptyTree. + */ + def newExpr(tree: Tree): Tree = + val ctorResultType = applyProxyResultType(tree) + if !ctorResultType.exists then return EmptyTree val qual = qualifier(tree) val tpt = qual match case Ident(name) => @@ -4257,8 +4261,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer pt) .showing(i"convert creator $tree -> $result", typr) - def applyProxy(tree: Tree) = tree match + /** If `tree` is a constructor proxy reference, return the type it constructs, + * otherwise return NoType. + */ + def applyProxyResultType(tree: Tree): Type = tree match case Select(_, nme.apply) => + // can't use tree.symbol and tree.tpe.widen.finalResultType, because when overloaded + // tree.symbol is NoSymbol (via MultiDenotation.symbol) and tree.tpe won't widen. tree.denot.altsWith(_.isAllOf(ApplyProxyFlags)) match case denot :: _ => // any of the constructors will do, in order to get the result type, so using the first one @@ -4278,9 +4287,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if needsTupledDual(ref, pt) && Feature.autoTuplingEnabled => adapt(tree, pt.tupledDual, locked) case _ => - val ctorResultType = applyProxy(tree) - if ctorResultType.exists then newExpr(ctorResultType) - else adaptOverloaded(ref) + newExpr(tree).orElse(adaptOverloaded(ref)) } case poly: PolyType if !(ctx.mode is Mode.Type) && dummyTreeOfType.unapply(tree).isEmpty => @@ -4289,24 +4296,21 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Test case was but i18695.scala, but it got fixed by a different tweak in #18719. // We leave test for this condition in as a defensive measure in case // it arises somewhere else. - val ctorResultType = applyProxy(tree) - if ctorResultType.exists then newExpr(ctorResultType) - else if pt.isInstanceOf[PolyProto] then tree - else - var typeArgs = tree match - case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree(_)) - case _ => Nil - if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2.map(_.wrapInTypeTree(tree)) - convertNewGenericArray(readapt(tree.appliedToTypeTrees(typeArgs))) + newExpr(tree).orElse: + if pt.isInstanceOf[PolyProto] then tree + else + var typeArgs = tree match + case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree(_)) + case _ => Nil + if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2.map(_.wrapInTypeTree(tree)) + convertNewGenericArray(readapt(tree.appliedToTypeTrees(typeArgs))) case wtp => val isStructuralCall = wtp.isValueType && isStructuralTermSelectOrApply(tree) if (isStructuralCall) readaptSimplified(handleStructural(tree)) else pt match { case pt: FunProto => - val ctorResultType = applyProxy(tree) - if ctorResultType.exists then newExpr(ctorResultType) - else adaptToArgs(wtp, pt) + newExpr(tree).orElse(adaptToArgs(wtp, pt)) case pt: PolyProto if !wtp.isImplicitMethod => tryInsertApplyOrImplicit(tree, pt, locked)(tree) // error will be reported in typedTypeApply case _ =>