From b6b12f3eb0686acaaa8b851c1742d83a4ba8e98b Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Fri, 8 Nov 2024 17:39:01 +0100 Subject: [PATCH 01/16] Add support for applied constructor sugar --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 79aaf367851a..5a86db354628 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2557,17 +2557,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(using Context): Tree = { + val tpt1 = withoutMode(Mode.Pattern): + typed(tree.tpt, AnyTypeConstructorProto) + tree.args match case arg :: _ if arg.isTerm => if Feature.dependentEnabled then - return errorTree(tree, em"Not yet implemented: T(...)") + tpt1.tpe.typeSymbol.primaryConstructor.typeRef.underlying match + case mt: MethodType => + return TypeTree(mt.instantiate(tree.args.map((typedExpr(_).tpe)))) else return errorTree(tree, dependentMsg) case _ => - - val tpt1 = withoutMode(Mode.Pattern) { - typed(tree.tpt, AnyTypeConstructorProto) - } + val tparams = tpt1.tpe.typeParams if tpt1.tpe.isError then val args1 = tree.args.mapconserve(typedType(_)) From 67191b3e274f84e044bd2d6728e546c73751da8f Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 11 Nov 2024 15:59:33 +0100 Subject: [PATCH 02/16] Move applied constructor types under modularity --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 14 +++++++++----- .../dotty/tools/dotc/typer/ErrorReporting.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- tests/neg/deptypes.scala | 2 +- tests/pos/applied_constructors.scala | 9 +++++++++ 5 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 tests/pos/applied_constructors.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3a43f53f3ca4..4daa8018fed4 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1334,12 +1334,16 @@ object Parsers { /** Singleton ::= SimpleRef * | SimpleLiteral * | Singleton ‘.’ id - * -- not yet | Singleton ‘(’ Singletons ‘)’ + * | Singleton ‘(’ Singletons ‘)’ * -- not yet | Singleton ‘[’ Types ‘]’ */ def singleton(): Tree = - if isSimpleLiteral then simpleLiteral() - else dotSelectors(simpleRef()) + val res = + if isSimpleLiteral then simpleLiteral() + else dotSelectors(simpleRef()) + if in.token == LPAREN then + AppliedTypeTree(res, inParens(commaSeparated(() => singleton()))) + else res /** SimpleLiteral ::= [‘-’] integerLiteral * | [‘-’] floatingPointLiteral @@ -2051,7 +2055,7 @@ object Parsers { /** SimpleType ::= SimpleLiteral * | ‘?’ TypeBounds * | SimpleType1 - * | SimpleType ‘(’ Singletons ‘)’ -- under language.experimental.dependent, checked in Typer + * | SimpleType ‘(’ Singletons ‘)’ -- under language.experimental.modularity, checked in Typer * Singletons ::= Singleton {‘,’ Singleton} */ def simpleType(): Tree = @@ -2084,7 +2088,7 @@ object Parsers { typeBounds().withSpan(Span(start, in.lastOffset, start)) else def singletonArgs(t: Tree): Tree = - if in.token == LPAREN && in.featureEnabled(Feature.dependent) + if in.token == LPAREN && in.featureEnabled(Feature.modularity) then singletonArgs(AppliedTypeTree(t, inParensWithCommas(commaSeparated(singleton)))) else t singletonArgs(simpleType1()) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 58119981dfc4..a6254c0d5c00 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -294,7 +294,7 @@ object ErrorReporting { def dependentMsg = """Term-dependent types are experimental, - |they must be enabled with a `experimental.dependent` language import or setting""".stripMargin.toMessage + |they must be enabled with a `experimental.modularity` language import or setting""".stripMargin.toMessage def err(using Context): Errors = new Errors } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5a86db354628..cb266d1fe8a4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2562,14 +2562,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tree.args match case arg :: _ if arg.isTerm => - if Feature.dependentEnabled then + if Feature.enabled(Feature.modularity) then tpt1.tpe.typeSymbol.primaryConstructor.typeRef.underlying match case mt: MethodType => return TypeTree(mt.instantiate(tree.args.map((typedExpr(_).tpe)))) else return errorTree(tree, dependentMsg) case _ => - + val tparams = tpt1.tpe.typeParams if tpt1.tpe.isError then val args1 = tree.args.mapconserve(typedType(_)) @@ -2693,7 +2693,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typeIndexedLambdaTypeTree(tree, tparams, body) def typedTermLambdaTypeTree(tree: untpd.TermLambdaTypeTree)(using Context): Tree = - if Feature.dependentEnabled then + if Feature.enabled(Feature.modularity) then errorTree(tree, em"Not yet implemented: (...) =>> ...") else errorTree(tree, dependentMsg) diff --git a/tests/neg/deptypes.scala b/tests/neg/deptypes.scala index 39b1e42ccbca..8325f1a1c032 100644 --- a/tests/neg/deptypes.scala +++ b/tests/neg/deptypes.scala @@ -8,4 +8,4 @@ type Tensor2[T](m: Int)(n: Int) = Matrix[T](m, n) // error: not yet implemente val x: Vec[Int](10) = ??? // error: not yet implemented val n = 10 -type T = Vec[String](n) // error: not yet implemented \ No newline at end of file +type T = Vec[String](n) // error: not yet implemented diff --git a/tests/pos/applied_constructors.scala b/tests/pos/applied_constructors.scala new file mode 100644 index 000000000000..131d7d0b3e58 --- /dev/null +++ b/tests/pos/applied_constructors.scala @@ -0,0 +1,9 @@ +import scala.language.experimental.modularity + +class C(tracked val x: Int) +class D(tracked val c: C) + +object Test extends App { + val c: C(42) = C(42) + // val d: D(C(42)) = D(C(42)) +} From 896c844a1a61876915ac3a63af306bd507fea5b9 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 11 Nov 2024 16:43:45 +0100 Subject: [PATCH 03/16] Support nested constructor applications as types --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 17 +++++++++-------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 9 +++++++-- tests/pos/applied_constructors.scala | 7 ++++++- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4daa8018fed4..c5b15f125b68 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1335,15 +1335,20 @@ object Parsers { * | SimpleLiteral * | Singleton ‘.’ id * | Singleton ‘(’ Singletons ‘)’ - * -- not yet | Singleton ‘[’ Types ‘]’ + * | Singleton ‘[’ Types ‘]’ */ def singleton(): Tree = val res = if isSimpleLiteral then simpleLiteral() else dotSelectors(simpleRef()) - if in.token == LPAREN then - AppliedTypeTree(res, inParens(commaSeparated(() => singleton()))) - else res + singletonArgs(res) + + def singletonArgs(t: Tree): Tree = + if in.token == LPAREN && in.featureEnabled(Feature.modularity) then + singletonArgs(AppliedTypeTree(t, inParensWithCommas(commaSeparated(singleton)))) + else if in.token == LBRACKET && in.featureEnabled(Feature.modularity) then + singletonArgs(AppliedTypeTree(t, inBrackets(commaSeparated(() => typ())))) + else t /** SimpleLiteral ::= [‘-’] integerLiteral * | [‘-’] floatingPointLiteral @@ -2087,10 +2092,6 @@ object Parsers { val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) else - def singletonArgs(t: Tree): Tree = - if in.token == LPAREN && in.featureEnabled(Feature.modularity) - then singletonArgs(AppliedTypeTree(t, inParensWithCommas(commaSeparated(singleton)))) - else t singletonArgs(simpleType1()) /** SimpleType1 ::= id diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb266d1fe8a4..efc03a4b949b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2563,8 +2563,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tree.args match case arg :: _ if arg.isTerm => if Feature.enabled(Feature.modularity) then - tpt1.tpe.typeSymbol.primaryConstructor.typeRef.underlying match - case mt: MethodType => + val constr = + if tpt1.tpe.typeSymbol.primaryConstructor.exists then + tpt1.tpe.typeSymbol.primaryConstructor + else + tpt1.tpe.typeSymbol.companionClass.primaryConstructor + constr.typeRef.underlying match + case mt: MethodOrPoly => return TypeTree(mt.instantiate(tree.args.map((typedExpr(_).tpe)))) else return errorTree(tree, dependentMsg) diff --git a/tests/pos/applied_constructors.scala b/tests/pos/applied_constructors.scala index 131d7d0b3e58..10309fe70154 100644 --- a/tests/pos/applied_constructors.scala +++ b/tests/pos/applied_constructors.scala @@ -1,9 +1,14 @@ import scala.language.experimental.modularity +import scala.language.future class C(tracked val x: Int) class D(tracked val c: C) +class E(tracked val c: D) +class F[A](tracked val a: A) object Test extends App { val c: C(42) = C(42) - // val d: D(C(42)) = D(C(42)) + val d: D(C(42)) = D(C(42)) + val e: E(D(C(42))) = E(D(C(42))) + // val f: F[Int](42) = F(42) } From a0833b3a23a1066f95e5c7b485e955a174cd143f Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 14 Nov 2024 14:55:37 +0100 Subject: [PATCH 04/16] Extract applied constructors logic to a separate function --- .../dotty/tools/dotc/parsing/Parsers.scala | 6 ++-- .../src/dotty/tools/dotc/typer/Typer.scala | 36 +++++++++++++------ tests/pos/applied_constructors.scala | 6 ++-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c5b15f125b68..df29ba8e51fc 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1335,7 +1335,7 @@ object Parsers { * | SimpleLiteral * | Singleton ‘.’ id * | Singleton ‘(’ Singletons ‘)’ - * | Singleton ‘[’ Types ‘]’ + * -- not yet | Singleton ‘[’ Types ‘]’ */ def singleton(): Tree = val res = @@ -1346,8 +1346,8 @@ object Parsers { def singletonArgs(t: Tree): Tree = if in.token == LPAREN && in.featureEnabled(Feature.modularity) then singletonArgs(AppliedTypeTree(t, inParensWithCommas(commaSeparated(singleton)))) - else if in.token == LBRACKET && in.featureEnabled(Feature.modularity) then - singletonArgs(AppliedTypeTree(t, inBrackets(commaSeparated(() => typ())))) + // else if in.token == LBRACKET && in.featureEnabled(Feature.modularity) then + // singletonArgs(AppliedTypeTree(t, inBrackets(commaSeparated(() => typ())))) // should this be marked in a different way, so that we know that it is a type application, and not a term application? else t /** SimpleLiteral ::= [‘-’] integerLiteral diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index efc03a4b949b..fa664e217b22 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2556,23 +2556,37 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls) } + /** Type applied dependent class constructors in type positions */ + private def typedTermAppliedTypeTree(tree: untpd.AppliedTypeTree, tpt1: Tree)(using Context): Tree = { + if Feature.enabled(Feature.modularity) then + val constr = + if tpt1.tpe.typeSymbol.primaryConstructor.exists then + tpt1.tpe.typeSymbol.primaryConstructor + else + tpt1.tpe.typeSymbol.companionClass.primaryConstructor + // TODO(kπ) vvvvvvv Might want to take the first term list? or all term lists? depends on the rest of the logic. + constr.paramSymss.flatten.foreach { p => + if p.isTerm && !p.flags.is(Tracked) then + report.error( + em"""The constructor parameter `${p.name}` of `${tpt1.tpe}` is not tracked. + |Only tracked parameters are allowed in dependent constructor applications.""", + tree.srcPos + ) + } + constr.typeRef.underlying match + case mt: MethodOrPoly => + TypeTree(mt.instantiate(tree.args.map((typedExpr(_).tpe)))) + else + errorTree(tree, dependentMsg) + } + def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(using Context): Tree = { val tpt1 = withoutMode(Mode.Pattern): typed(tree.tpt, AnyTypeConstructorProto) tree.args match case arg :: _ if arg.isTerm => - if Feature.enabled(Feature.modularity) then - val constr = - if tpt1.tpe.typeSymbol.primaryConstructor.exists then - tpt1.tpe.typeSymbol.primaryConstructor - else - tpt1.tpe.typeSymbol.companionClass.primaryConstructor - constr.typeRef.underlying match - case mt: MethodOrPoly => - return TypeTree(mt.instantiate(tree.args.map((typedExpr(_).tpe)))) - else - return errorTree(tree, dependentMsg) + return typedTermAppliedTypeTree(tree, tpt1) case _ => val tparams = tpt1.tpe.typeParams diff --git a/tests/pos/applied_constructors.scala b/tests/pos/applied_constructors.scala index 10309fe70154..0ca11d399c39 100644 --- a/tests/pos/applied_constructors.scala +++ b/tests/pos/applied_constructors.scala @@ -4,11 +4,13 @@ import scala.language.future class C(tracked val x: Int) class D(tracked val c: C) class E(tracked val c: D) -class F[A](tracked val a: A) +class F[A](tracked val a: Int) +class G[A](tracked val a: A) object Test extends App { val c: C(42) = C(42) val d: D(C(42)) = D(C(42)) val e: E(D(C(42))) = E(D(C(42))) - // val f: F[Int](42) = F(42) + // val f: F[Int](42) = F[Int](42) + // val g: G(42) = G(42) } From 0616734a66eb6372e20a8f14255dc57e1187be36 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 15 Nov 2024 12:28:49 +0100 Subject: [PATCH 05/16] Fix desugaring explicitly applied generics --- .../src/dotty/tools/dotc/typer/Typer.scala | 34 ++++++++++++++----- tests/pos/applied_constructors.scala | 2 +- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fa664e217b22..53c214ca26c7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2558,6 +2558,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Type applied dependent class constructors in type positions */ private def typedTermAppliedTypeTree(tree: untpd.AppliedTypeTree, tpt1: Tree)(using Context): Tree = { + val AppliedTypeTree(originalTpt, args) = tree if Feature.enabled(Feature.modularity) then val constr = if tpt1.tpe.typeSymbol.primaryConstructor.exists then @@ -2565,17 +2566,32 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else tpt1.tpe.typeSymbol.companionClass.primaryConstructor // TODO(kπ) vvvvvvv Might want to take the first term list? or all term lists? depends on the rest of the logic. - constr.paramSymss.flatten.foreach { p => - if p.isTerm && !p.flags.is(Tracked) then - report.error( - em"""The constructor parameter `${p.name}` of `${tpt1.tpe}` is not tracked. - |Only tracked parameters are allowed in dependent constructor applications.""", - tree.srcPos - ) - } + // constr.paramSymss.flatten.foreach { p => + // if p.isTerm && !p.flags.is(Tracked) then + // report.error( + // em"""The constructor parameter `${p.name}` of `${tpt1.tpe}` is not tracked. + // |Only tracked parameters are allowed in dependent constructor applications.""", + // tree.srcPos + // ) + // } + def getArgs(t: Tree): List[List[Tree]] = t match + case AppliedTypeTree(base, args) => getArgs(base) :+ args + case _ => Nil + + def instAll(t: Type, args: List[List[Tree]]): Type = (t.widenDealias, args) match + case (_, Nil) => t + case (t: MethodType, args :: rest) => + val t1 = t.instantiate(args.map(_.tpe)) + instAll(t1, rest) + case (_, args :: rest) => + val t1 = t.appliedTo(args.map(_.tpe)) + instAll(t1, rest) + constr.typeRef.underlying match case mt: MethodOrPoly => - TypeTree(mt.instantiate(tree.args.map((typedExpr(_).tpe)))) + val typedArgs = tree.args.map(a => (TypeTree(typedExpr(a).tpe))) + val preArgs = getArgs(tpt1) + TypeTree(instAll(mt, preArgs :+ typedArgs)) else errorTree(tree, dependentMsg) } diff --git a/tests/pos/applied_constructors.scala b/tests/pos/applied_constructors.scala index 0ca11d399c39..ad3126b6f441 100644 --- a/tests/pos/applied_constructors.scala +++ b/tests/pos/applied_constructors.scala @@ -11,6 +11,6 @@ object Test extends App { val c: C(42) = C(42) val d: D(C(42)) = D(C(42)) val e: E(D(C(42))) = E(D(C(42))) - // val f: F[Int](42) = F[Int](42) + val f: F[Int](42) = F[Int](42) // val g: G(42) = G(42) } From ca070a0aa8c469320260f680999ea95d6d23fbe4 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 21 Nov 2024 13:34:44 +0100 Subject: [PATCH 06/16] Some more applied constructor tests --- tests/pos/applied_constructors.scala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/pos/applied_constructors.scala b/tests/pos/applied_constructors.scala index ad3126b6f441..b109862eb1ec 100644 --- a/tests/pos/applied_constructors.scala +++ b/tests/pos/applied_constructors.scala @@ -2,15 +2,18 @@ import scala.language.experimental.modularity import scala.language.future class C(tracked val x: Int) -class D(tracked val c: C) -class E(tracked val c: D) +class NC(tracked val c: C) +class NNC(tracked val c: D) class F[A](tracked val a: Int) class G[A](tracked val a: A) +class NF[A](tracked val f: F[A]) +class NG[A](tracked val ) object Test extends App { val c: C(42) = C(42) - val d: D(C(42)) = D(C(42)) - val e: E(D(C(42))) = E(D(C(42))) + val nc: NC(C(42)) = NC(C(42)) + val nc1: NC(c) = NC(c) + val nnc: NNC(NC(C(42))) = NNC(NC(C(42))) val f: F[Int](42) = F[Int](42) // val g: G(42) = G(42) } From 2b71bfde52c58b16e34c754c6996acc4160c17ef Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 5 Feb 2025 10:57:24 +0000 Subject: [PATCH 07/16] Parse applied constructor type arguments as trees Co-authored-by: Kacper Korban --- .../dotty/tools/dotc/parsing/Parsers.scala | 25 ++------- .../dotty/tools/dotc/typer/Applications.scala | 11 ++++ .../src/dotty/tools/dotc/typer/Typer.scala | 51 ++----------------- tests/neg/applied_constructor_types.check | 21 ++++++++ tests/neg/applied_constructor_types.scala | 10 ++++ tests/pos/applied_constructor_types.scala | 35 +++++++++++++ tests/pos/applied_constructors.scala | 19 ------- tests/warn/applied_constructor_types.check | 1 + tests/warn/applied_constructor_types.scala | 6 +++ 9 files changed, 93 insertions(+), 86 deletions(-) create mode 100644 tests/neg/applied_constructor_types.check create mode 100644 tests/neg/applied_constructor_types.scala create mode 100644 tests/pos/applied_constructor_types.scala delete mode 100644 tests/pos/applied_constructors.scala create mode 100644 tests/warn/applied_constructor_types.check create mode 100644 tests/warn/applied_constructor_types.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index df29ba8e51fc..24c38df08b83 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1331,25 +1331,6 @@ object Parsers { */ def qualId(): Tree = dotSelectors(termIdent()) - /** Singleton ::= SimpleRef - * | SimpleLiteral - * | Singleton ‘.’ id - * | Singleton ‘(’ Singletons ‘)’ - * -- not yet | Singleton ‘[’ Types ‘]’ - */ - def singleton(): Tree = - val res = - if isSimpleLiteral then simpleLiteral() - else dotSelectors(simpleRef()) - singletonArgs(res) - - def singletonArgs(t: Tree): Tree = - if in.token == LPAREN && in.featureEnabled(Feature.modularity) then - singletonArgs(AppliedTypeTree(t, inParensWithCommas(commaSeparated(singleton)))) - // else if in.token == LBRACKET && in.featureEnabled(Feature.modularity) then - // singletonArgs(AppliedTypeTree(t, inBrackets(commaSeparated(() => typ())))) // should this be marked in a different way, so that we know that it is a type application, and not a term application? - else t - /** SimpleLiteral ::= [‘-’] integerLiteral * | [‘-’] floatingPointLiteral * | booleanLiteral @@ -2092,7 +2073,11 @@ object Parsers { val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) else - singletonArgs(simpleType1()) + val tpt = simpleType1() + if in.featureEnabled(Feature.modularity) && in.token == LPAREN then + parArgumentExprss(wrapNew(tpt)) + else + tpt /** SimpleType1 ::= id * | Singleton `.' id diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 54f033fe6fd8..dd6530ad7de9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1715,6 +1715,17 @@ trait Applications extends Compatibility { def typedUnApply(tree: untpd.UnApply, selType: Type)(using Context): UnApply = throw new UnsupportedOperationException("cannot type check an UnApply node") + def typedAppliedConstructorType(tree: untpd.Apply)(using Context) = + val Select(New(tpt), _) = tree.fun: @unchecked // Always wrapped in `New`, see `simpleType` in `Parsers` + val tree1 = typedExpr(tree) + val widenSkolemsMap = new TypeMap: + def apply(tp: Type) = mapOver(tp.widenSkolem) + val preciseTp = widenSkolemsMap(tree1.tpe) + val classTp = typedType(tpt).tpe + if preciseTp frozen_=:= classTp then + report.warning(em"Blop blop") + TypeTree(preciseTp) + /** Is given method reference applicable to argument trees `args`? * @param resultType The expected result type of the application */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 53c214ca26c7..825bd9583624 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2556,55 +2556,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls) } - /** Type applied dependent class constructors in type positions */ - private def typedTermAppliedTypeTree(tree: untpd.AppliedTypeTree, tpt1: Tree)(using Context): Tree = { - val AppliedTypeTree(originalTpt, args) = tree - if Feature.enabled(Feature.modularity) then - val constr = - if tpt1.tpe.typeSymbol.primaryConstructor.exists then - tpt1.tpe.typeSymbol.primaryConstructor - else - tpt1.tpe.typeSymbol.companionClass.primaryConstructor - // TODO(kπ) vvvvvvv Might want to take the first term list? or all term lists? depends on the rest of the logic. - // constr.paramSymss.flatten.foreach { p => - // if p.isTerm && !p.flags.is(Tracked) then - // report.error( - // em"""The constructor parameter `${p.name}` of `${tpt1.tpe}` is not tracked. - // |Only tracked parameters are allowed in dependent constructor applications.""", - // tree.srcPos - // ) - // } - def getArgs(t: Tree): List[List[Tree]] = t match - case AppliedTypeTree(base, args) => getArgs(base) :+ args - case _ => Nil - - def instAll(t: Type, args: List[List[Tree]]): Type = (t.widenDealias, args) match - case (_, Nil) => t - case (t: MethodType, args :: rest) => - val t1 = t.instantiate(args.map(_.tpe)) - instAll(t1, rest) - case (_, args :: rest) => - val t1 = t.appliedTo(args.map(_.tpe)) - instAll(t1, rest) - - constr.typeRef.underlying match - case mt: MethodOrPoly => - val typedArgs = tree.args.map(a => (TypeTree(typedExpr(a).tpe))) - val preArgs = getArgs(tpt1) - TypeTree(instAll(mt, preArgs :+ typedArgs)) - else - errorTree(tree, dependentMsg) - } - def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(using Context): Tree = { val tpt1 = withoutMode(Mode.Pattern): typed(tree.tpt, AnyTypeConstructorProto) - tree.args match - case arg :: _ if arg.isTerm => - return typedTermAppliedTypeTree(tree, tpt1) - case _ => - val tparams = tpt1.tpe.typeParams if tpt1.tpe.isError then val args1 = tree.args.mapconserve(typedType(_)) @@ -3556,7 +3511,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Typecheck tree without adapting it, returning a typed tree. * @param initTree the untyped tree - * @param pt the expected result type + * @param pt the expected result typ * @param locked the set of type variables of the current typer state that cannot be interpolated * at the present time */ @@ -3596,7 +3551,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedUnnamed(tree: untpd.Tree): Tree = tree match { case tree: untpd.Apply => - if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) else typedApply(tree, pt) + if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) + else if (ctx.mode is Mode.Type) typedAppliedConstructorType(tree) + else typedApply(tree, pt) case tree: untpd.This => typedThis(tree) case tree: untpd.Number => typedNumber(tree, pt) case tree: untpd.Literal => typedLiteral(tree) diff --git a/tests/neg/applied_constructor_types.check b/tests/neg/applied_constructor_types.check new file mode 100644 index 000000000000..23e596c710e7 --- /dev/null +++ b/tests/neg/applied_constructor_types.check @@ -0,0 +1,21 @@ +-- [E006] Not Found Error: tests/neg/applied_constructors.scala:8:10 --------------------------------------------------- +8 | val v1: f(1) = f(1) // error + | ^ + | Not found: type f + | + | longer explanation available when compiling with `-explain` +Blop blop +-- [E006] Not Found Error: tests/neg/applied_constructors.scala:9:10 --------------------------------------------------- +9 | val v2: id(1) = f(1) // error + | ^^ + | Not found: type id - did you mean is? + | + | longer explanation available when compiling with `-explain` +Blop blop +-- [E006] Not Found Error: tests/neg/applied_constructors.scala:10:10 -------------------------------------------------- +10 | val v3: idDependent(1) = f(1) // error + | ^^^^^^^^^^^ + | Not found: type idDependent + | + | longer explanation available when compiling with `-explain` +Blop blop diff --git a/tests/neg/applied_constructor_types.scala b/tests/neg/applied_constructor_types.scala new file mode 100644 index 000000000000..8207b1213851 --- /dev/null +++ b/tests/neg/applied_constructor_types.scala @@ -0,0 +1,10 @@ +import scala.language.experimental.modularity + +def f(x: Int): Int = x +def id[T](x: T): T = x +def idDependent(x: Any): x.type = x + +def test = + val v1: f(1) = f(1) // error + val v2: id(1) = f(1) // error + val v3: idDependent(1) = f(1) // error diff --git a/tests/pos/applied_constructor_types.scala b/tests/pos/applied_constructor_types.scala new file mode 100644 index 000000000000..2a2e6be0e1b5 --- /dev/null +++ b/tests/pos/applied_constructor_types.scala @@ -0,0 +1,35 @@ +import scala.language.experimental.modularity + +class Box(tracked val v: Any) +class C(tracked val x: Int) +class NC(tracked val c: C) +class NNC(tracked val c: NC) +class F[A](tracked val a: Int) +class G[A](tracked val a: A) +class NF[A](tracked val f: F[A]) + +object O: + val m: Int = 42 + + class InnerClass(tracked val x: Int) + +object Test extends App { + val c: C(42) = C(42) + val nc: NC(C(42)) = NC(C(42)) + val nc1: NC(c) = NC(c) + val nnc: NNC(NC(C(42))) = NNC(NC(C(42))) + val f: F[Int](42) = F[Int](42) + val f2: F[Int](42) = F(42) + val f3: F(42) = F(42) + val g: G(42) = G(42) + + val n: Int = 42 + val c2: C(n) = C(n) + val c3: C(O.m) = C(O.m) + + val box: Box(O.InnerClass(42)) = Box(O.InnerClass(42)) + val box2: Box(O.InnerClass(n)) = Box(O.InnerClass(n)) + val box3: Box(O.InnerClass(O.m)) = Box(O.InnerClass(O.m)) + val box4: Box(n) = Box(O.InnerClass(n).x) + val box5: Box(O.m) = Box(O.InnerClass(O.m).x) +} diff --git a/tests/pos/applied_constructors.scala b/tests/pos/applied_constructors.scala deleted file mode 100644 index b109862eb1ec..000000000000 --- a/tests/pos/applied_constructors.scala +++ /dev/null @@ -1,19 +0,0 @@ -import scala.language.experimental.modularity -import scala.language.future - -class C(tracked val x: Int) -class NC(tracked val c: C) -class NNC(tracked val c: D) -class F[A](tracked val a: Int) -class G[A](tracked val a: A) -class NF[A](tracked val f: F[A]) -class NG[A](tracked val ) - -object Test extends App { - val c: C(42) = C(42) - val nc: NC(C(42)) = NC(C(42)) - val nc1: NC(c) = NC(c) - val nnc: NNC(NC(C(42))) = NNC(NC(C(42))) - val f: F[Int](42) = F[Int](42) - // val g: G(42) = G(42) -} diff --git a/tests/warn/applied_constructor_types.check b/tests/warn/applied_constructor_types.check new file mode 100644 index 000000000000..1333ed77b7e1 --- /dev/null +++ b/tests/warn/applied_constructor_types.check @@ -0,0 +1 @@ +TODO diff --git a/tests/warn/applied_constructor_types.scala b/tests/warn/applied_constructor_types.scala new file mode 100644 index 000000000000..d575dabe80f0 --- /dev/null +++ b/tests/warn/applied_constructor_types.scala @@ -0,0 +1,6 @@ +import scala.language.experimental.modularity + +class UnspecificBox(val v: Any) + +def test = + val v1: UnspecificBox(4) = UnspecificBox(4) // warn From 1f91fe631c9fcfbdc2f70063c55f8e9f7f04d005 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 6 Feb 2025 10:30:35 +0100 Subject: [PATCH 08/16] Update check file for applied constructor types neg test --- tests/neg/applied_constructor_types.check | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neg/applied_constructor_types.check b/tests/neg/applied_constructor_types.check index 23e596c710e7..535b4a33778c 100644 --- a/tests/neg/applied_constructor_types.check +++ b/tests/neg/applied_constructor_types.check @@ -1,18 +1,18 @@ --- [E006] Not Found Error: tests/neg/applied_constructors.scala:8:10 --------------------------------------------------- +-- [E006] Not Found Error: tests/neg/applied_constructor_types.scala:8:10 ---------------------------------------------- 8 | val v1: f(1) = f(1) // error | ^ | Not found: type f | | longer explanation available when compiling with `-explain` Blop blop --- [E006] Not Found Error: tests/neg/applied_constructors.scala:9:10 --------------------------------------------------- +-- [E006] Not Found Error: tests/neg/applied_constructor_types.scala:9:10 ---------------------------------------------- 9 | val v2: id(1) = f(1) // error | ^^ | Not found: type id - did you mean is? | | longer explanation available when compiling with `-explain` Blop blop --- [E006] Not Found Error: tests/neg/applied_constructors.scala:10:10 -------------------------------------------------- +-- [E006] Not Found Error: tests/neg/applied_constructor_types.scala:10:10 --------------------------------------------- 10 | val v3: idDependent(1) = f(1) // error | ^^^^^^^^^^^ | Not found: type idDependent From f7f49f99ed0c73c814688faf43e4ba0b3f7a7637 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 6 Feb 2025 12:13:52 +0100 Subject: [PATCH 09/16] Extract a tracked related bug to a separate issue: #22540 --- tests/pos/applied_constructor_types.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/pos/applied_constructor_types.scala b/tests/pos/applied_constructor_types.scala index 2a2e6be0e1b5..d977d1336958 100644 --- a/tests/pos/applied_constructor_types.scala +++ b/tests/pos/applied_constructor_types.scala @@ -30,6 +30,4 @@ object Test extends App { val box: Box(O.InnerClass(42)) = Box(O.InnerClass(42)) val box2: Box(O.InnerClass(n)) = Box(O.InnerClass(n)) val box3: Box(O.InnerClass(O.m)) = Box(O.InnerClass(O.m)) - val box4: Box(n) = Box(O.InnerClass(n).x) - val box5: Box(O.m) = Box(O.InnerClass(O.m).x) } From d9fe50389d55d461242b6c93d8223178dc4e1fe9 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 1 May 2025 10:46:52 +0200 Subject: [PATCH 10/16] Add a warning message for a pointless applied constructor type use --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 16 ++++++++++++++++ .../dotty/tools/dotc/typer/Applications.scala | 4 ++-- tests/neg/applied_constructor_types.check | 3 --- tests/warn/applied_constructor_types.check | 8 +++++++- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index cafe99973701..e1d050d954b5 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -225,6 +225,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case FormatInterpolationErrorID // errorNumber: 209 case ValueClassCannotExtendAliasOfAnyValID // errorNumber: 210 case MatchIsNotPartialFunctionID // errorNumber: 211 + case PointlessAppliedConstructorTypeID // errorNumber: 212 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index ab9fc507b052..bdce9f53e00b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3487,3 +3487,19 @@ class MatchIsNotPartialFunction(using Context) extends SyntaxMsg(MatchIsNotParti | |Efficient operations will use `applyOrElse` to avoid computing the match twice, |but the `apply` body would be executed "per element" in the example.""" + def msg(using Context) = errorText + def explain(using Context) = "" + +final class PointlessAppliedConstructorType(tpt: untpd.Tree, args: List[untpd.Tree], tpe: Type)(using Context) extends TypeMsg(PointlessAppliedConstructorTypeID): + override protected def msg(using Context): String = + val act = i"$tpt(${args.map(_.show).mkString(", ")})" + i"""|Applied constructor type $act has no effect. + |The resulting type of $act is the same as its base type, namely: $tpe""".stripMargin + + override protected def explain(using Context): String = + i"""|Applied constructor types are used to ascribe specialized types of constructor applications. + |To benefit from this feature, the constructor in question has to have a more specific type than the class itself. + | + |If you want to track a precise type of any of the class parameters, make sure to mark the parameter as `tracked`. + |Otherwise, you can safely remove the argument list from the type. + |""" diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index dd6530ad7de9..f0407b626245 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1722,8 +1722,8 @@ trait Applications extends Compatibility { def apply(tp: Type) = mapOver(tp.widenSkolem) val preciseTp = widenSkolemsMap(tree1.tpe) val classTp = typedType(tpt).tpe - if preciseTp frozen_=:= classTp then - report.warning(em"Blop blop") + if !preciseTp.isError && (preciseTp frozen_=:= classTp) then + report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) TypeTree(preciseTp) /** Is given method reference applicable to argument trees `args`? diff --git a/tests/neg/applied_constructor_types.check b/tests/neg/applied_constructor_types.check index 535b4a33778c..e966c5d15f27 100644 --- a/tests/neg/applied_constructor_types.check +++ b/tests/neg/applied_constructor_types.check @@ -4,18 +4,15 @@ | Not found: type f | | longer explanation available when compiling with `-explain` -Blop blop -- [E006] Not Found Error: tests/neg/applied_constructor_types.scala:9:10 ---------------------------------------------- 9 | val v2: id(1) = f(1) // error | ^^ | Not found: type id - did you mean is? | | longer explanation available when compiling with `-explain` -Blop blop -- [E006] Not Found Error: tests/neg/applied_constructor_types.scala:10:10 --------------------------------------------- 10 | val v3: idDependent(1) = f(1) // error | ^^^^^^^^^^^ | Not found: type idDependent | | longer explanation available when compiling with `-explain` -Blop blop diff --git a/tests/warn/applied_constructor_types.check b/tests/warn/applied_constructor_types.check index 1333ed77b7e1..31bde31dbaec 100644 --- a/tests/warn/applied_constructor_types.check +++ b/tests/warn/applied_constructor_types.check @@ -1 +1,7 @@ -TODO +-- [E208] Type Warning: tests/warn/applied_constructor_types.scala:6:10 ------------------------------------------------ +6 | val v1: UnspecificBox(4) = UnspecificBox(4) // warn + | ^^^^^^^^^^^^^^^^ + | Applied constructor type UnspecificBox(4) has no effect. + | The resulting type of UnspecificBox(4) is the same as its base type, namely: UnspecificBox + | + | longer explanation available when compiling with `-explain` From 77b0eef32cbca060547ab01312ba86e7cd012aef Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 6 Feb 2025 13:49:30 +0100 Subject: [PATCH 11/16] Add docs and syntax change for applied constructor types --- docs/_docs/internals/syntax.md | 2 +- .../reference/experimental/modularity.md | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 6c144f436690..5a0e543a6d42 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -196,7 +196,7 @@ AnnotType1 ::= SimpleType1 {Annotation} SimpleType ::= SimpleLiteral SingletonTypeTree(l) | ‘?’ TypeBounds - | SimpleType1 + | SimpleType1 {ParArgumentExprs} SimpleType1 ::= id Ident(name) | Singleton ‘.’ id Select(t, name) | Singleton ‘.’ ‘type’ SingletonTypeTree(p) diff --git a/docs/_docs/reference/experimental/modularity.md b/docs/_docs/reference/experimental/modularity.md index 1a3d47695861..0cbc10b91b01 100644 --- a/docs/_docs/reference/experimental/modularity.md +++ b/docs/_docs/reference/experimental/modularity.md @@ -196,6 +196,33 @@ LocalModifier ::= ‘tracked’ The (soft) `tracked` modifier is allowed as a local modifier. +## Applied constructor types + +A new syntax is also introduced, to make classes with `tracked` parameters +easier to use. The new syntax is essentially the ability to use an application +of a class constructor as a type, we call such types applied constructor types. + +With this new feature the following example compiles correctly and the type in +the comment is the resulting type of the applied constructor types. + +```scala +import scala.language.experimental.modularity + +class Box(tracked val v: Any) + +val c: C(42) /* C { val v: 42 } */ = C(42) +``` + +### Syntax change + +``` +SimpleType ::= SimpleLiteral + | ‘?’ TypeBounds +--- | SimpleType1 ++++ | SimpleType1 {ParArgumentExprs} +``` + +A `SimpleType` can now optionally be followed by `ParArgumentExprs`. ## Allow Class Parents to be Refined Types From 32906fcbbfafbeaeea30090265051285e068cbf3 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 6 Feb 2025 16:35:31 +0100 Subject: [PATCH 12/16] Prevent crashes in erroneous cases --- .../dotty/tools/dotc/typer/Applications.scala | 22 ++++++++++--------- tests/neg/context-function-syntax.scala | 2 +- tests/neg/deptypes.scala | 8 +++---- tests/neg/i7751.scala | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index f0407b626245..9414e2861501 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1715,16 +1715,18 @@ trait Applications extends Compatibility { def typedUnApply(tree: untpd.UnApply, selType: Type)(using Context): UnApply = throw new UnsupportedOperationException("cannot type check an UnApply node") - def typedAppliedConstructorType(tree: untpd.Apply)(using Context) = - val Select(New(tpt), _) = tree.fun: @unchecked // Always wrapped in `New`, see `simpleType` in `Parsers` - val tree1 = typedExpr(tree) - val widenSkolemsMap = new TypeMap: - def apply(tp: Type) = mapOver(tp.widenSkolem) - val preciseTp = widenSkolemsMap(tree1.tpe) - val classTp = typedType(tpt).tpe - if !preciseTp.isError && (preciseTp frozen_=:= classTp) then - report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) - TypeTree(preciseTp) + def typedAppliedConstructorType(tree: untpd.Apply)(using Context) = tree.fun match + case Select(New(tpt), _) => + val tree1 = typedExpr(tree) + val widenSkolemsMap = new TypeMap: + def apply(tp: Type) = mapOver(tp.widenSkolem) + val preciseTp = widenSkolemsMap(tree1.tpe) + val classTp = typedType(tpt).tpe + if !preciseTp.isError && (preciseTp frozen_=:= classTp) then + report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) + TypeTree(preciseTp) + case _ => + throw TypeError(em"Unexpected applied constructor type: $tree") /** Is given method reference applicable to argument trees `args`? * @param resultType The expected result type of the application diff --git a/tests/neg/context-function-syntax.scala b/tests/neg/context-function-syntax.scala index 1f9b74cc69a6..e411e840d8b5 100644 --- a/tests/neg/context-function-syntax.scala +++ b/tests/neg/context-function-syntax.scala @@ -1,5 +1,5 @@ val test = - (using x: Int) => x // error // error + (using x: Int) => x // error // error // error val f = () ?=> 23 // error val g: ContextFunction0[Int] = ??? // ok diff --git a/tests/neg/deptypes.scala b/tests/neg/deptypes.scala index 8325f1a1c032..a64143a05185 100644 --- a/tests/neg/deptypes.scala +++ b/tests/neg/deptypes.scala @@ -2,10 +2,10 @@ type Vec[T] = (n: Int) =>> Array[T] // error: not yet implemented -type Matrix[T](m: Int, n: Int) = Vec[Vec[T](n)](m) // error: not yet implemented +type Matrix[T](m: Int, n: Int) = Vec[Vec[T](n)](m) // error // error: not yet implemented -type Tensor2[T](m: Int)(n: Int) = Matrix[T](m, n) // error: not yet implemented +type Tensor2[T](m: Int)(n: Int) = Matrix[T](m, n) -val x: Vec[Int](10) = ??? // error: not yet implemented +val x: Vec[Int](10) = ??? val n = 10 -type T = Vec[String](n) // error: not yet implemented +type T = Vec[String](n) diff --git a/tests/neg/i7751.scala b/tests/neg/i7751.scala index fd66e7d451be..18070cfd0551 100644 --- a/tests/neg/i7751.scala +++ b/tests/neg/i7751.scala @@ -1,3 +1,3 @@ import language.`3.3` -val a = Some(a=a,)=> // error // error // error // error +val a = Some(a=a,)=> // error // error // error val a = Some(x=y,)=> From 05882dbfbfd92e51e128257fe065763e64d4f453 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 7 Feb 2025 18:13:01 +0100 Subject: [PATCH 13/16] More applied constructor types fixes - only type applied constructor types in Typer - handle multiple parameter lists - add tests for mixed tracked and not tracked parameters --- .../dotty/tools/dotc/typer/Applications.scala | 24 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../reference/experimental/modularity.md | 2 +- tests/pos/applied_constructor_types.scala | 20 ++++++++++++++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 9414e2861501..6f9522230a46 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1715,18 +1715,18 @@ trait Applications extends Compatibility { def typedUnApply(tree: untpd.UnApply, selType: Type)(using Context): UnApply = throw new UnsupportedOperationException("cannot type check an UnApply node") - def typedAppliedConstructorType(tree: untpd.Apply)(using Context) = tree.fun match - case Select(New(tpt), _) => - val tree1 = typedExpr(tree) - val widenSkolemsMap = new TypeMap: - def apply(tp: Type) = mapOver(tp.widenSkolem) - val preciseTp = widenSkolemsMap(tree1.tpe) - val classTp = typedType(tpt).tpe - if !preciseTp.isError && (preciseTp frozen_=:= classTp) then - report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) - TypeTree(preciseTp) - case _ => - throw TypeError(em"Unexpected applied constructor type: $tree") + def typedAppliedConstructorType(tree: untpd.Apply)(using Context) = untpd.methPart(tree) match + case Select(New(tpt), _) => + val tree1 = typedExpr(tree) + val widenSkolemsMap = new TypeMap: + def apply(tp: Type) = mapOver(tp.widenSkolem) + val preciseTp = widenSkolemsMap(tree1.tpe) + val classTp = typedType(tpt).tpe + if !preciseTp.isError && (preciseTp frozen_=:= classTp) then + report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) + TypeTree(preciseTp) + case _ => + throw TypeError(em"Unexpected applied constructor type: $tree") /** Is given method reference applicable to argument trees `args`? * @param resultType The expected result type of the application diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 825bd9583624..3766b4a2ea4c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3552,7 +3552,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedUnnamed(tree: untpd.Tree): Tree = tree match { case tree: untpd.Apply => if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) - else if (ctx.mode is Mode.Type) typedAppliedConstructorType(tree) + else if (ctx.mode.is(Mode.Type) && !ctx.isAfterTyper) typedAppliedConstructorType(tree) else typedApply(tree, pt) case tree: untpd.This => typedThis(tree) case tree: untpd.Number => typedNumber(tree, pt) diff --git a/docs/_docs/reference/experimental/modularity.md b/docs/_docs/reference/experimental/modularity.md index 0cbc10b91b01..1c8c30bdea31 100644 --- a/docs/_docs/reference/experimental/modularity.md +++ b/docs/_docs/reference/experimental/modularity.md @@ -208,7 +208,7 @@ the comment is the resulting type of the applied constructor types. ```scala import scala.language.experimental.modularity -class Box(tracked val v: Any) +class C(tracked val v: Any) val c: C(42) /* C { val v: 42 } */ = C(42) ``` diff --git a/tests/pos/applied_constructor_types.scala b/tests/pos/applied_constructor_types.scala index d977d1336958..f2e7d162cffe 100644 --- a/tests/pos/applied_constructor_types.scala +++ b/tests/pos/applied_constructor_types.scala @@ -8,8 +8,12 @@ class F[A](tracked val a: Int) class G[A](tracked val a: A) class NF[A](tracked val f: F[A]) +class Person(val name: String, tracked val age: Int) +class PersonPrime(val name: String)(tracked val age: Int) +class PersonBis(tracked val name: String)(val age: Int) + object O: - val m: Int = 42 + val m: Int = 27 class InnerClass(tracked val x: Int) @@ -23,11 +27,23 @@ object Test extends App { val f3: F(42) = F(42) val g: G(42) = G(42) - val n: Int = 42 + val n: Int = 27 val c2: C(n) = C(n) val c3: C(O.m) = C(O.m) val box: Box(O.InnerClass(42)) = Box(O.InnerClass(42)) val box2: Box(O.InnerClass(n)) = Box(O.InnerClass(n)) val box3: Box(O.InnerClass(O.m)) = Box(O.InnerClass(O.m)) + + val person: Person("Kasia", 27) = Person("Kasia", 27) + val person1: Person("Kasia", n) = Person("Kasia", n) + val person2: Person("Kasia", O.m) = Person("Kasia", O.m) + + val personPrime: PersonPrime("Kasia")(27) = PersonPrime("Kasia")(27) + val personPrime1: PersonPrime("Kasia")(n) = PersonPrime("Kasia")(n) + val personPrime2: PersonPrime("Kasia")(O.m) = PersonPrime("Kasia")(O.m) + + val personBis: PersonBis("Kasia")(27) = PersonBis("Kasia")(27) + val personBis1: PersonBis("Kasia")(n) = PersonBis("Kasia")(n) + val personBis2: PersonBis("Kasia")(O.m) = PersonBis("Kasia")(O.m) } From 36648f7a9b5724049fc379ef008e21cfbd130508 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Sun, 9 Feb 2025 17:33:30 +0100 Subject: [PATCH 14/16] Ignore best effort compilation failure; add some more controversial examples; Hide more implementation parts under modularity --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- compiler/test/dotc/neg-best-effort-unpickling.excludelist | 3 +++ tests/pos/applied_constructor_types.scala | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3766b4a2ea4c..75b3c74d5df0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3552,7 +3552,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedUnnamed(tree: untpd.Tree): Tree = tree match { case tree: untpd.Apply => if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) - else if (ctx.mode.is(Mode.Type) && !ctx.isAfterTyper) typedAppliedConstructorType(tree) + else if (Feature.enabled(modularity) && ctx.mode.is(Mode.Type) && !ctx.isAfterTyper) typedAppliedConstructorType(tree) else typedApply(tree, pt) case tree: untpd.This => typedThis(tree) case tree: untpd.Number => typedNumber(tree, pt) diff --git a/compiler/test/dotc/neg-best-effort-unpickling.excludelist b/compiler/test/dotc/neg-best-effort-unpickling.excludelist index d57f7e0176e8..9c20bf3ccc03 100644 --- a/compiler/test/dotc/neg-best-effort-unpickling.excludelist +++ b/compiler/test/dotc/neg-best-effort-unpickling.excludelist @@ -18,3 +18,6 @@ i18750.scala # Crash on invalid prefix ([A] =>> Int) i22357a.scala + +# `110 (of class java.lang.Integer)` +context-function-syntax.scala diff --git a/tests/pos/applied_constructor_types.scala b/tests/pos/applied_constructor_types.scala index f2e7d162cffe..7b5e8d7ed2c7 100644 --- a/tests/pos/applied_constructor_types.scala +++ b/tests/pos/applied_constructor_types.scala @@ -12,6 +12,8 @@ class Person(val name: String, tracked val age: Int) class PersonPrime(val name: String)(tracked val age: Int) class PersonBis(tracked val name: String)(val age: Int) +class Generic[A](val a: A) + object O: val m: Int = 27 @@ -46,4 +48,8 @@ object Test extends App { val personBis: PersonBis("Kasia")(27) = PersonBis("Kasia")(27) val personBis1: PersonBis("Kasia")(n) = PersonBis("Kasia")(n) val personBis2: PersonBis("Kasia")(O.m) = PersonBis("Kasia")(O.m) + + val generic1: Generic(compiletime.erasedValue[Int]) = Generic(42) + val generic2: Generic(??? : Int) = Generic(42) + val generic3: Generic(43) = Generic(42) } From 04642f5c7eeda8f17729882ae7e1ac671909c234 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Tue, 11 Feb 2025 16:31:56 +0100 Subject: [PATCH 15/16] Warn when a non-fully-dependent class is used with applied constructor types --- .../tools/dotc/reporting/ErrorMessageID.scala | 3 ++- .../dotty/tools/dotc/reporting/messages.scala | 7 ++++++ .../dotty/tools/dotc/typer/Applications.scala | 4 ++++ tests/pos/applied_constructor_types.scala | 24 +++++++++---------- tests/warn/applied_constructor_types.check | 7 ++---- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index e1d050d954b5..3f95789789bd 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -225,7 +225,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case FormatInterpolationErrorID // errorNumber: 209 case ValueClassCannotExtendAliasOfAnyValID // errorNumber: 210 case MatchIsNotPartialFunctionID // errorNumber: 211 - case PointlessAppliedConstructorTypeID // errorNumber: 212 + case OnlyFullyDependentAppliedConstructorTypeID // errorNumber: 212 + case PointlessAppliedConstructorTypeID // errorNumber: 213 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index bdce9f53e00b..b973396ee15a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3503,3 +3503,10 @@ final class PointlessAppliedConstructorType(tpt: untpd.Tree, args: List[untpd.Tr |If you want to track a precise type of any of the class parameters, make sure to mark the parameter as `tracked`. |Otherwise, you can safely remove the argument list from the type. |""" + +final class OnlyFullyDependentAppliedConstructorType()(using Context) + extends TypeMsg(OnlyFullyDependentAppliedConstructorTypeID): + override protected def msg(using Context): String = + i"Applied constructor type can only be used with classes that only have tracked parameters" + + override protected def explain(using Context): String = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6f9522230a46..2d60affedec7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1722,6 +1722,10 @@ trait Applications extends Compatibility { def apply(tp: Type) = mapOver(tp.widenSkolem) val preciseTp = widenSkolemsMap(tree1.tpe) val classTp = typedType(tpt).tpe + def classSymbolHasOnlyTrackedParameters = + classTp.classSymbol.primaryConstructor.paramSymss.flatten.filter(_.isTerm).forall(_.is(Tracked)) + if !preciseTp.isError && !classSymbolHasOnlyTrackedParameters then + report.warning(OnlyFullyDependentAppliedConstructorType(), tree.srcPos) if !preciseTp.isError && (preciseTp frozen_=:= classTp) then report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) TypeTree(preciseTp) diff --git a/tests/pos/applied_constructor_types.scala b/tests/pos/applied_constructor_types.scala index 7b5e8d7ed2c7..8ae7f663dd5e 100644 --- a/tests/pos/applied_constructor_types.scala +++ b/tests/pos/applied_constructor_types.scala @@ -37,19 +37,19 @@ object Test extends App { val box2: Box(O.InnerClass(n)) = Box(O.InnerClass(n)) val box3: Box(O.InnerClass(O.m)) = Box(O.InnerClass(O.m)) - val person: Person("Kasia", 27) = Person("Kasia", 27) - val person1: Person("Kasia", n) = Person("Kasia", n) - val person2: Person("Kasia", O.m) = Person("Kasia", O.m) + val person: Person("Kasia", 27) = Person("Kasia", 27) // warn + val person1: Person("Kasia", n) = Person("Kasia", n) // warn + val person2: Person("Kasia", O.m) = Person("Kasia", O.m) // warn - val personPrime: PersonPrime("Kasia")(27) = PersonPrime("Kasia")(27) - val personPrime1: PersonPrime("Kasia")(n) = PersonPrime("Kasia")(n) - val personPrime2: PersonPrime("Kasia")(O.m) = PersonPrime("Kasia")(O.m) + val personPrime: PersonPrime("Kasia")(27) = PersonPrime("Kasia")(27) // warn + val personPrime1: PersonPrime("Kasia")(n) = PersonPrime("Kasia")(n) // warn + val personPrime2: PersonPrime("Kasia")(O.m) = PersonPrime("Kasia")(O.m) // warn - val personBis: PersonBis("Kasia")(27) = PersonBis("Kasia")(27) - val personBis1: PersonBis("Kasia")(n) = PersonBis("Kasia")(n) - val personBis2: PersonBis("Kasia")(O.m) = PersonBis("Kasia")(O.m) + val personBis: PersonBis("Kasia")(27) = PersonBis("Kasia")(27) // warn + val personBis1: PersonBis("Kasia")(n) = PersonBis("Kasia")(n) // warn + val personBis2: PersonBis("Kasia")(O.m) = PersonBis("Kasia")(O.m) // warn - val generic1: Generic(compiletime.erasedValue[Int]) = Generic(42) - val generic2: Generic(??? : Int) = Generic(42) - val generic3: Generic(43) = Generic(42) + val generic1: Generic(compiletime.erasedValue[Int]) = Generic(42) // warn + val generic2: Generic(??? : Int) = Generic(42) // warn + val generic3: Generic(43) = Generic(42) // warn } diff --git a/tests/warn/applied_constructor_types.check b/tests/warn/applied_constructor_types.check index 31bde31dbaec..e1f3ec434db0 100644 --- a/tests/warn/applied_constructor_types.check +++ b/tests/warn/applied_constructor_types.check @@ -1,7 +1,4 @@ --- [E208] Type Warning: tests/warn/applied_constructor_types.scala:6:10 ------------------------------------------------ +-- [E209] Type Warning: tests/warn/applied_constructor_types.scala:6:10 ------------------------------------------------ 6 | val v1: UnspecificBox(4) = UnspecificBox(4) // warn | ^^^^^^^^^^^^^^^^ - | Applied constructor type UnspecificBox(4) has no effect. - | The resulting type of UnspecificBox(4) is the same as its base type, namely: UnspecificBox - | - | longer explanation available when compiling with `-explain` + | Applied constructor type can only be used with classes that only have tracked parameters From d3fe2d5d7a6cf801984d2d16332a04377929364f Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 23 Apr 2025 14:17:39 +0200 Subject: [PATCH 16/16] Review changes for applied constructor types --- .../src/dotty/tools/dotc/core/NamerOps.scala | 4 +- .../src/dotty/tools/dotc/core/TypeUtils.scala | 7 +++- .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 4 +- .../dotty/tools/dotc/typer/Applications.scala | 41 +++++++++++-------- .../src/dotty/tools/dotc/typer/Typer.scala | 6 +-- .../reference/experimental/modularity.md | 10 +++++ tests/warn/applied_constructor_types.check | 4 +- 8 files changed, 48 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index dbdb46aba334..d6db7348ddc1 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -39,14 +39,12 @@ object NamerOps: */ extension (tp: Type) def separateRefinements(cls: ClassSymbol, refinements: mutable.LinkedHashMap[Name, Type] | Null)(using Context): Type = - val widenSkolemsMap = new TypeMap: - def apply(tp: Type) = mapOver(tp.widenSkolem) tp match case RefinedType(tp1, rname, rinfo) => try tp1.separateRefinements(cls, refinements) finally if refinements != null then - val rinfo1 = widenSkolemsMap(rinfo) + val rinfo1 = rinfo.widenSkolems refinements(rname) = refinements.get(rname) match case Some(tp) => tp & rinfo1 case None => rinfo1 diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index ab035db68d9f..eb526c2b4d85 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -53,6 +53,11 @@ class TypeUtils: case ps => ps.reduceLeft(AndType(_, _)) } + def widenSkolems(using Context): Type = + val widenSkolemsMap = new TypeMap: + def apply(tp: Type) = mapOver(tp.widenSkolem) + widenSkolemsMap(self) + /** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs */ def tupleElementTypes(using Context): Option[List[Type]] = @@ -134,7 +139,7 @@ class TypeUtils: case t => throw TypeError(em"Malformed NamedTuple: names must be string types, but $t was found.") val values = vals.tupleElementTypesUpTo(bound, normalize).getOrElse(Nil) names.zip(values) - + (if normalize then self.normalized else self).dealias match // for desugaring and printer, ignore derived types to avoid infinite recursion in NamedTuple.unapply case defn.NamedTupleDirect(nmes, vals) => extractNamesTypes(nmes, vals) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 24c38df08b83..e396839c972e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2041,7 +2041,7 @@ object Parsers { /** SimpleType ::= SimpleLiteral * | ‘?’ TypeBounds * | SimpleType1 - * | SimpleType ‘(’ Singletons ‘)’ -- under language.experimental.modularity, checked in Typer + * | SimpleType ‘(’ Singletons ‘)’ * Singletons ::= Singleton {‘,’ Singleton} */ def simpleType(): Tree = diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index b973396ee15a..0db766d4e337 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3487,8 +3487,6 @@ class MatchIsNotPartialFunction(using Context) extends SyntaxMsg(MatchIsNotParti | |Efficient operations will use `applyOrElse` to avoid computing the match twice, |but the `apply` body would be executed "per element" in the example.""" - def msg(using Context) = errorText - def explain(using Context) = "" final class PointlessAppliedConstructorType(tpt: untpd.Tree, args: List[untpd.Tree], tpe: Type)(using Context) extends TypeMsg(PointlessAppliedConstructorTypeID): override protected def msg(using Context): String = @@ -3507,6 +3505,6 @@ final class PointlessAppliedConstructorType(tpt: untpd.Tree, args: List[untpd.Tr final class OnlyFullyDependentAppliedConstructorType()(using Context) extends TypeMsg(OnlyFullyDependentAppliedConstructorTypeID): override protected def msg(using Context): String = - i"Applied constructor type can only be used with classes that only have tracked parameters" + i"Applied constructor type can only be used with classes where all parameters in the first parameter list are tracked" override protected def explain(using Context): String = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 2d60affedec7..07ddb37d93b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1282,6 +1282,10 @@ trait Applications extends Compatibility { } else { val app = tree.fun match + case _ if ctx.mode.is(Mode.Type) && Feature.enabled(Feature.modularity) && !ctx.isAfterTyper => + untpd.methPart(tree.fun) match + case Select(nw @ New(_), _) => typedAppliedConstructorType(nw, tree.args, tree) + case _ => realApply case untpd.TypeApply(_: untpd.SplicePattern, _) if Feature.quotedPatternsWithPolymorphicFunctionsEnabled => typedAppliedSpliceWithTypes(tree, pt) case _: untpd.SplicePattern => typedAppliedSplice(tree, pt) @@ -1715,22 +1719,27 @@ trait Applications extends Compatibility { def typedUnApply(tree: untpd.UnApply, selType: Type)(using Context): UnApply = throw new UnsupportedOperationException("cannot type check an UnApply node") - def typedAppliedConstructorType(tree: untpd.Apply)(using Context) = untpd.methPart(tree) match - case Select(New(tpt), _) => - val tree1 = typedExpr(tree) - val widenSkolemsMap = new TypeMap: - def apply(tp: Type) = mapOver(tp.widenSkolem) - val preciseTp = widenSkolemsMap(tree1.tpe) - val classTp = typedType(tpt).tpe - def classSymbolHasOnlyTrackedParameters = - classTp.classSymbol.primaryConstructor.paramSymss.flatten.filter(_.isTerm).forall(_.is(Tracked)) - if !preciseTp.isError && !classSymbolHasOnlyTrackedParameters then - report.warning(OnlyFullyDependentAppliedConstructorType(), tree.srcPos) - if !preciseTp.isError && (preciseTp frozen_=:= classTp) then - report.warning(PointlessAppliedConstructorType(tpt, tree.args, classTp), tree.srcPos) - TypeTree(preciseTp) - case _ => - throw TypeError(em"Unexpected applied constructor type: $tree") + /** Typecheck an applied constructor type – An Apply node in Type mode. + * This expands to the type this term would have if it were typed as an expression. + * + * e.g. + * ```scala + * // class C(tracked val v: Any) + * val c: C(42) = ??? + * ``` + */ + def typedAppliedConstructorType(nw: untpd.New, args: List[untpd.Tree], tree: untpd.Apply)(using Context) = + val tree1 = typedExpr(tree) + val preciseTp = tree1.tpe.widenSkolems + val classTp = typedType(nw.tpt).tpe + def classSymbolHasOnlyTrackedParameters = + !classTp.classSymbol.primaryConstructor.paramSymss.nestedExists: param => + param.isTerm && !param.is(Tracked) + if !preciseTp.isError && !classSymbolHasOnlyTrackedParameters then + report.warning(OnlyFullyDependentAppliedConstructorType(), tree.srcPos) + if !preciseTp.isError && (preciseTp frozen_=:= classTp) then + report.warning(PointlessAppliedConstructorType(nw.tpt, args, classTp), tree.srcPos) + TypeTree(preciseTp) /** Is given method reference applicable to argument trees `args`? * @param resultType The expected result type of the application diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 75b3c74d5df0..428568ecc795 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3511,7 +3511,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Typecheck tree without adapting it, returning a typed tree. * @param initTree the untyped tree - * @param pt the expected result typ + * @param pt the expected result type * @param locked the set of type variables of the current typer state that cannot be interpolated * at the present time */ @@ -3551,9 +3551,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedUnnamed(tree: untpd.Tree): Tree = tree match { case tree: untpd.Apply => - if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) - else if (Feature.enabled(modularity) && ctx.mode.is(Mode.Type) && !ctx.isAfterTyper) typedAppliedConstructorType(tree) - else typedApply(tree, pt) + if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) else typedApply(tree, pt) case tree: untpd.This => typedThis(tree) case tree: untpd.Number => typedNumber(tree, pt) case tree: untpd.Literal => typedLiteral(tree) diff --git a/docs/_docs/reference/experimental/modularity.md b/docs/_docs/reference/experimental/modularity.md index 1c8c30bdea31..580044ce4d66 100644 --- a/docs/_docs/reference/experimental/modularity.md +++ b/docs/_docs/reference/experimental/modularity.md @@ -224,6 +224,16 @@ SimpleType ::= SimpleLiteral A `SimpleType` can now optionally be followed by `ParArgumentExprs`. +The arguments are used to typecheck the whole type, as if it was a normal +constructor application. For classes with `tracked` parameters this will mean +that the resulting type will have a refinement for each `tracked` parameter. + +For example, given the following class definition: +```scala +class Person(tracked val name: String, tracked val age: Int) +``` +**Type** `Person("Kasia", 27)` will be translated to `Person { val name: "Kasia"; val age: 27 }`. + ## Allow Class Parents to be Refined Types Since `tracked` parameters create refinements in constructor types, diff --git a/tests/warn/applied_constructor_types.check b/tests/warn/applied_constructor_types.check index e1f3ec434db0..b2d2b82f4e09 100644 --- a/tests/warn/applied_constructor_types.check +++ b/tests/warn/applied_constructor_types.check @@ -1,4 +1,4 @@ --- [E209] Type Warning: tests/warn/applied_constructor_types.scala:6:10 ------------------------------------------------ +-- [E212] Type Warning: tests/warn/applied_constructor_types.scala:6:10 ------------------------------------------------ 6 | val v1: UnspecificBox(4) = UnspecificBox(4) // warn | ^^^^^^^^^^^^^^^^ - | Applied constructor type can only be used with classes that only have tracked parameters + |Applied constructor type can only be used with classes where all parameters in the first parameter list are tracked