From 0906a0a8dd090619127e60a92448e30c986f22d4 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 268526df859901977af7ea25926ded106bf17e0c Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Thu, 24 Jul 2025 14:47:49 +0200 Subject: [PATCH 2/4] Guard against invalid prefixes in argForParam Fixes #23504 [Cherry-picked 982f21a0cec8bce40ed4443ee4c8a98c6d258bec][modified] From 0e57f90d7797fff022be22f1c95debaaa908233a Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Thu, 24 Jul 2025 14:48:02 +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 1a3d5ce5e32206b43fc5304b32b7be5eadee3de3 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