diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 5e6dd471e304..60ec6ce48787 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1085,10 +1085,10 @@ object SymDenotations { */ def matchNullaryLoosely(using Context): Boolean = { def test(sym: Symbol) = - sym.is(JavaDefined) || - sym.owner == defn.AnyClass || - sym == defn.Object_clone || - sym.owner.is(Scala2x) + sym.is(JavaDefined) + || sym.owner == defn.AnyClass + || sym == defn.Object_clone + || sym.owner.is(Scala2x) && sym.name != nme.apply this.exists && (test(symbol) || allOverriddenSymbols.exists(test)) } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index fc0cfb436088..bbf8c919cc68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -331,7 +331,7 @@ object Applications { * parameter list, or EmptyTree if none was found. * @param fn the tree referring to the function part of this call * @param n the index of the parameter in the parameter list of the call - * @param testOnly true iff we just to find out whether a getter exists + * @param testOnly true iff we just want to find out whether a getter exists */ def findDefaultGetter(fn: Tree, n: Int, testOnly: Boolean)(using Context): Tree = def reifyPrefix(pre: Type): Tree = pre match @@ -412,20 +412,21 @@ object Applications { /** Splice new method reference `meth` into existing application `app` */ private def spliceMeth(meth: Tree, app: Tree)(using Context): Tree = app match { case Apply(fn, args) => - // Constructors always have one leading non-implicit parameter list. - // Empty list is inserted for constructors where the first parameter list is implicit. - // - // Therefore, we need to ignore the first empty argument list. - // This is needed for the test tests/neg/i12344.scala - // - // see NamerOps.normalizeIfConstructor + // Constructors written with a leading implicit parameter list are normalized + // to have one leading non-implicit parameter list. See NamerOps.normalizeIfConstructor. + // However, a default getter for the implicit parameter will not reflect the augmented signature. + // If leading empty args is detected for this case, but the default arg getter isNullaryMethod, + // then the empty args are supplied as usual: $lessinit$greater$default$1() // if args == Nil && !fn.isInstanceOf[Apply] && app.tpe.isImplicitMethod && fn.symbol.isConstructor - then meth - else spliceMeth(meth, fn).appliedToArgs(args) + && !meth.tpe.widen.isNullaryMethod + then + meth + else + spliceMeth(meth, fn).appliedToArgs(args) case TypeApply(fn, targs) => // Note: It is important that the type arguments `targs` are passed in new trees // instead of being spliced in literally. Otherwise, a type argument to a default diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2cdb1b75bfaa..e036bcf35f13 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4640,30 +4640,30 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } /** A synthetic apply should be eta-expanded if it is the apply of an implicit function - * class, and the expected type is a function type. This rule is needed so we can pass - * an implicit function to a regular function type. So the following is OK - * - * val f: implicit A => B = ??? - * val g: A => B = f - * - * and the last line expands to - * - * val g: A => B = (x$0: A) => f.apply(x$0) - * - * One could be tempted not to eta expand the rhs, but that would violate the invariant - * that expressions of implicit function types are always implicit closures, which is - * exploited by ShortcutImplicits. - * - * On the other hand, the following would give an error if there is no implicit - * instance of A available. - * - * val x: AnyRef = f - * - * That's intentional, we want to fail here, otherwise some unsuccessful implicit searches - * would go undetected. - * - * Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala. - */ + * class, and the expected type is a function type. This rule is needed so we can pass + * an implicit function to a regular function type. So the following is OK + * + * val f: implicit A => B = ??? + * val g: A => B = f + * + * and the last line expands to + * + * val g: A => B = (x$0: A) => f.apply(x$0) + * + * One could be tempted not to eta expand the rhs, but that would violate the invariant + * that expressions of implicit function types are always implicit closures, which is + * exploited by ShortcutImplicits. + * + * On the other hand, the following would give an error if there is no implicit + * instance of A available. + * + * val x: AnyRef = f + * + * That's intentional, we want to fail here, otherwise some unsuccessful implicit searches + * would go undetected. + * + * Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala. + */ def adaptNoArgsUnappliedMethod(wtp: MethodType, functionExpected: Boolean, arity: Int): Tree = { /** Is reference to this symbol `f` automatically expanded to `f()`? */ def isAutoApplied(sym: Symbol): Boolean = diff --git a/tests/neg/i21351.scala b/tests/neg/i21351.scala new file mode 100644 index 000000000000..c2286ad6b8aa --- /dev/null +++ b/tests/neg/i21351.scala @@ -0,0 +1,8 @@ +def f = () => 42 + +object X: + def apply() = 42 + +@main def Test = + f.apply // error + X.apply // error diff --git a/tests/pos/i22061.scala b/tests/pos/i22061.scala new file mode 100644 index 000000000000..c42475ea5ea5 --- /dev/null +++ b/tests/pos/i22061.scala @@ -0,0 +1,55 @@ +object TraitExtendingClass { + + /*abstract*/ + class A()( + implicit + val x: Int = 3 + ) + + trait B extends A + + class C(implicit c: Int = 42) + + @main def main(): Unit = { + + val a = new A() + val b = new B {} // error happens here + val c = new C() + + println(b.x) + } +} +def f(i: Int)(j: Int)(implicit x: Int = 42) = x*(i+j) +def g = f(1)(1) + +abstract class A()(implicit val x: Int = 3) +class A1 extends A +class A2 extends A() + +abstract class B()(using y: Int = 9) +class B1 extends B +class B2 extends B() + +abstract class C(implicit val x: Int = 3) +class C1 extends C +class C2 extends C() + +abstract class D(using y: Int = 9) +class D1 extends D +class D2 extends D() + +abstract class E()(implicit val x: Int = 3, val y: Int = 4) +class E1 extends E +class E2 extends E() + +abstract class F()(using y: Int = 9, x: Int = 8) +class F1 extends F +class F2 extends F() + +abstract class G(implicit val x: Int = 3, val y: Int = 4) +class G1 extends G +class G2 extends G() + +abstract class H(using y: Int = 9, x: Int = 8) +class H1 extends H +class H2 extends H()