diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 6757e3170f91..f2e84a9e7e43 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -671,7 +671,8 @@ object Types { val rinfo = tp.refinedInfo if (name.isTypeName && !pinfo.isInstanceOf[ClassInfo]) { // simplified case that runs more efficiently val jointInfo = - if (ctx.base.pendingMemberSearches.contains(name)) pinfo safe_& rinfo + if rinfo.isInstanceOf[TypeAlias] then rinfo + else if (ctx.base.pendingMemberSearches.contains(name)) pinfo safe_& rinfo else pinfo recoverable_& rinfo pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } @@ -1029,12 +1030,10 @@ object Types { */ def recoverable_&(that: Type)(using Context): Type = try this & that - catch { - case ex: CyclicReference => this safe_& that - // A test case where this happens is tests/pos/i536.scala. - // The & causes a subtype check which calls baseTypeRef again with the same - // superclass. - } + catch case ex: CyclicReference => this safe_& that + // A test case where this happens is tests/pos/i536.scala. + // The & causes a subtype check which calls baseTypeRef again with the same + // superclass. def | (that: Type)(using Context): Type = { record("|") @@ -2641,10 +2640,9 @@ object Types { def ref(using Context): Type = if computed then if myRef == null then - // if errors were reported previously handle this by throwing a CyclicReference - // instead of crashing immediately. A test case is neg/i6057.scala. - assert(reportCycles || ctx.reporter.errorsReported) throw CyclicReference(NoDenotation) + // i9346.scala shows that such cycles can arise in normal code + // when trying to compute asSeenFrom on refined types. else computed = true val result = refFn diff --git a/compiler/src/dotty/tools/dotc/core/Variances.scala b/compiler/src/dotty/tools/dotc/core/Variances.scala index 122c7a10e4b7..d994a584db0f 100644 --- a/compiler/src/dotty/tools/dotc/core/Variances.scala +++ b/compiler/src/dotty/tools/dotc/core/Variances.scala @@ -116,6 +116,10 @@ object Variances { def traverse(t: Type): Unit = t match case t: TypeParamRef if t.binder eq lam => lam.typeParams(t.paramNum).storedVariance &= varianceFromInt(variance) + case t: LazyRef => + try traverse(t.ref) + catch case _: CyclicReference => + () // can happen with deeply entangled F-bounds, such as in i9364.scala under -Ytest-pickler case _ => traverseChildren(t) } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 92ae55663077..bf7f3b387497 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -340,6 +340,34 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase else Checking.checkAppliedType(tree) super.transform(tree) + case tree: RefinedTypeTree => + //println(i"checking $tree: ${tree.tpe}") + def checkRefinements(tp: Type): Unit = tp match + case RefinedType(parent, name, info) => + info match + case info: TypeAlias => + val mbr = parent.member(name) + mbr.info match + case bounds: TypeBounds if !bounds.contains(info) => + //println(i"falling back to checking F-bounded ${tree.tpe}") + val site = tree.tpe.narrow + val narrowMbr = mbr.asSeenFrom(site) + val owner = mbr.symbol.maybeOwner + val narrowInfo = info.asSeenFrom(site, owner) + val ok = narrowMbr.info match + case bounds: TypeBounds => bounds.contains(narrowInfo) + case _ => false + //println(i"fall back ${narrowMbr.info} $narrowInfo") + if !ok then + report.error( + em"""type alias type $name$info + |out of bounds: $bounds / ${bounds.toString}""", tree.sourcePos) + case _ => + case _ => + checkRefinements(parent) + case _ => + checkRefinements(tree.tpe) + super.transform(tree) case SingletonTypeTree(ref) => Checking.checkRealizable(ref.tpe, ref.posd) super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 1e87a08b0fe0..bd8a649a19d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -123,6 +123,18 @@ object Checking { withMode(Mode.AllowLambdaWildcardApply)(checkValidIfApply) } + /** Check refined type for well-formedness. This means + * - all arguments are within their corresponding bounds + * - if type is a higher-kinded application with wildcard arguments, + * check that it or one of its supertypes can be reduced to a normal application. + * Unreducible applications correspond to general existentials, and we + * cannot handle those. + * @param tree The applied type tree to check + * @param tpt If `tree` is synthesized from a type in a TypeTree, + * the original TypeTree, or EmptyTree otherwise. + def checkRefinedType(tp: RefinedType) + */ + /** Check all applied type trees in inferred type `tpt` for well-formedness */ def checkAppliedTypesIn(tpt: TypeTree)(using Context): Unit = val checker = new TypeTraverser: diff --git a/tests/neg/i6225.scala b/tests/neg/i6225.scala index 6d2f7943a394..148a484fd0f1 100644 --- a/tests/neg/i6225.scala +++ b/tests/neg/i6225.scala @@ -1,11 +1,11 @@ -object O1 { // error: cannot be instantiated +object O1 { type A[X] = X opaque type T = A // error: opaque type alias must be fully applied } object O2 { opaque type A[X] = X - object A { // error: cannot be instantiated + object A { opaque type T = A // error: opaque type alias must be fully applied } } diff --git a/tests/pending/pos/i9346.scala b/tests/pending/pos/i9346.scala deleted file mode 100644 index 51e747fd976e..000000000000 --- a/tests/pending/pos/i9346.scala +++ /dev/null @@ -1,9 +0,0 @@ -trait Foo[+A] { - type Repr[+O] <: Foo[O] { - type Repr[+OO] = Foo.this.Repr[OO] - } - - def foo: Repr[A] - - def bar: Repr[A] = this.foo.foo -} diff --git a/tests/pos-deep-subtype/i9346.scala b/tests/pos-deep-subtype/i9346.scala new file mode 100644 index 000000000000..83ce56d91225 --- /dev/null +++ b/tests/pos-deep-subtype/i9346.scala @@ -0,0 +1,7 @@ +trait Foo { + type Repr[+O] <: Foo { + type Repr[+OO] = Foo.this.Repr[OO] + } + + def foo[T](f: Repr[T]): f.Repr[T] = ??? +} \ No newline at end of file