From 03fdd37428e4292d4f1196a1cc40915edbaa2846 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 11 Jul 2025 13:45:26 +0200 Subject: [PATCH 1/4] Guard against invalid prefixes in argForParam Fixes #23504 --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 +++++-- tests/neg/i23504.scala | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/neg/i23504.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2e0b227bba7c..c6ddb2a781ca 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2682,8 +2682,11 @@ object Types extends TypeUtils { while (tparams.nonEmpty && args.nonEmpty) { if (tparams.head.eq(tparam)) return args.head match { - case _: TypeBounds if !widenAbstract => TypeRef(pre, tparam) - case arg => arg.boxedUnlessFun(tycon) + case _: TypeBounds if !widenAbstract => + if !NamedType.validPrefix(pre) then + throw TypeError(em"invalid prefix $pre cannot replace parameter $tparam in result of selection") + TypeRef(pre, tparam) + case arg => arg } tparams = tparams.tail args = args.tail diff --git a/tests/neg/i23504.scala b/tests/neg/i23504.scala new file mode 100644 index 000000000000..9d79b074a4da --- /dev/null +++ b/tests/neg/i23504.scala @@ -0,0 +1,2 @@ +def test = + Seq.empty[[T] =>> () => ?].head() // error \ No newline at end of file From 2a2bbe8c0e9dc2be51ce5fe236f2c099c9866ae6 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Wed, 23 Jul 2025 15:08:45 +0200 Subject: [PATCH 2/4] Guard against invalid prefixes in argForParam Fixes #23504 [Cherry-picked 982f21a0cec8bce40ed4443ee4c8a98c6d258bec][modified] From 4564fbe252c65ad8ec904b8b86d3e53f802ed95b Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Wed, 23 Jul 2025 15:09:05 +0200 Subject: [PATCH 3/4] Add failing test to exclude list We should come back to this and allow this test by catching the TypeError and reporting it. [Cherry-picked 40e076d228fb37c8d7fea23a7fb832e7586affc7][modified] From a57a95969d6a3566c1eacf843b6727a88f9e9f5f Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 12 Jul 2025 13:05:54 +0200 Subject: [PATCH 4/4] More systematic handling of invalid prefixes It looks like there are many situations where an illegal higher-kinded type in an argument for a value type parameter causes illegal TermRefs and TypeRefs to be constructed, leading to an assertion error. We now turn the assertion error into a specialized exception which eventually leads to a TypeError being thrown. The problem is we cannot detect illegal kinds in arguments early enough to prevent these situations. We do detect them later, but the damage can already be done before that. [Cherry-picked 4a959b1181f4a17d9722fbe0fda010da435e9cf4] --- compiler/src/dotty/tools/dotc/core/Types.scala | 9 ++++----- compiler/src/dotty/tools/dotc/core/Uniques.scala | 15 +++++++++++++-- tests/neg/i23504.scala | 3 ++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c6ddb2a781ca..c4efb291da00 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2261,7 +2261,7 @@ object Types extends TypeUtils { def _1: Type def _2: Designator - assert(NamedType.validPrefix(prefix), s"invalid prefix $prefix") + if !NamedType.validPrefix(prefix) then throw InvalidPrefix() private var myName: Name | Null = null private var lastDenotation: Denotation | Null = null @@ -2682,10 +2682,7 @@ object Types extends TypeUtils { while (tparams.nonEmpty && args.nonEmpty) { if (tparams.head.eq(tparam)) return args.head match { - case _: TypeBounds if !widenAbstract => - if !NamedType.validPrefix(pre) then - throw TypeError(em"invalid prefix $pre cannot replace parameter $tparam in result of selection") - TypeRef(pre, tparam) + case _: TypeBounds if !widenAbstract => TypeRef(pre, tparam) case arg => arg } tparams = tparams.tail @@ -3041,6 +3038,8 @@ object Types extends TypeUtils { apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot) } + class InvalidPrefix extends Exception + // --- Other SingletonTypes: ThisType/SuperType/ConstantType --------------------------- /** The type cls.this diff --git a/compiler/src/dotty/tools/dotc/core/Uniques.scala b/compiler/src/dotty/tools/dotc/core/Uniques.scala index da6b0aba88bd..b50905c22c98 100644 --- a/compiler/src/dotty/tools/dotc/core/Uniques.scala +++ b/compiler/src/dotty/tools/dotc/core/Uniques.scala @@ -3,6 +3,7 @@ package core import Types.*, Contexts.*, util.Stats.*, Hashable.*, Names.* import config.Config +import Symbols.Symbol import Decorators.* import util.{WeakHashSet, Stats} import WeakHashSet.Entry @@ -41,8 +42,10 @@ object Uniques: val h = doHash(null, designator, prefix) if monitored then recordCaching(h, classOf[NamedType]) def newType = - if (isTerm) new CachedTermRef(prefix, designator, h) - else new CachedTypeRef(prefix, designator, h) + try + if isTerm then new CachedTermRef(prefix, designator, h) + else new CachedTypeRef(prefix, designator, h) + catch case ex: InvalidPrefix => badPrefix(prefix, designator) if h == NotCached then newType else // Inlined from WeakHashSet#put @@ -61,6 +64,14 @@ object Uniques: linkedListLoop(oldHead) end if + end enterIfNew + + private def badPrefix(prefix: Type, desig: Designator)(using Context): Nothing = + def name = desig match + case desig: Name => desig + case desig: Symbol => desig.name + throw TypeError(em"invalid prefix $prefix when trying to form $prefix . $name") + end NamedTypeUniques final class AppliedUniques extends WeakHashSet[AppliedType](Config.initialUniquesCapacity * 2) with Hashable: diff --git a/tests/neg/i23504.scala b/tests/neg/i23504.scala index 9d79b074a4da..e53337eaa105 100644 --- a/tests/neg/i23504.scala +++ b/tests/neg/i23504.scala @@ -1,2 +1,3 @@ def test = - Seq.empty[[T] =>> () => ?].head() // error \ No newline at end of file + Seq.empty[[T] =>> () => ?].head() // error + Seq.empty[[T] =>> Int => Int].head(1) // error \ No newline at end of file