diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index d22d70b41bfc..5a224005a7d1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -562,7 +562,6 @@ trait ImplicitRunInfo: object collectParts extends TypeTraverser: - private var provisional: Boolean = _ private var parts: mutable.LinkedHashSet[Type] = _ private val partSeen = util.HashSet[Type]() @@ -587,19 +586,18 @@ trait ImplicitRunInfo: case t: ConstantType => traverse(t.underlying) case t: TypeParamRef => + assert(!ctx.typerState.constraint.contains(t), i"`wildApprox` failed to remove uninstantiated $t") traverse(t.underlying) - if ctx.typerState.constraint.contains(t) then provisional = true case t: TermParamRef => traverse(t.underlying) case t => traverseChildren(t) - def apply(tp: Type): (collection.Set[Type], Boolean) = - provisional = false + def apply(tp: Type): collection.Set[Type] = parts = mutable.LinkedHashSet() partSeen.clear() traverse(tp) - (parts, provisional) + parts end collectParts val seen = util.HashSet[Type]() @@ -674,12 +672,11 @@ trait ImplicitRunInfo: end collectCompanions def recur(tp: Type): OfTypeImplicits = - val (parts, provisional) = collectParts(tp) + val parts = collectParts(tp) val companions = collectCompanions(tp, parts) val result = OfTypeImplicits(tp, companions)(runContext) if Config.cacheImplicitScopes && tp.hash != NotCached - && !provisional && (tp eq rootTp) // first type traversed is always cached || !incomplete.contains(tp) // other types are cached if they are not incomplete then implicitScopeCache(tp) = result diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index fb701c1e2e94..167128be08e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -394,7 +394,10 @@ object ProtoTypes { // `protoTyperState` committable we must ensure that it does not // contain any type variable which don't already exist in the passed // TyperState. This is achieved by instantiating any such type - // variable. + // variable. NOTE: this does not suffice to discard type variables + // in ancestors of `protoTyperState`, if this situation ever + // comes up, an assertion in TyperState will trigger and this code + // will need to be generalized. if protoTyperState.isCommittable then val passedConstraint = passedTyperState.constraint val newLambdas = newConstraint.domainLambdas.filter(tl => @@ -822,6 +825,17 @@ object ProtoTypes { tp.derivedViewProto( wildApprox(tp.argType, theMap, seen, internal), wildApprox(tp.resultType, theMap, seen, internal)) + case tp: FunProto => + val args = tp.args.mapconserve(arg => + val argTp = tp.typeOfArg(arg) match + case NoType => WildcardType + case tp => wildApprox(tp, theMap, seen, internal) + arg.withType(argTp)) + val resTp = wildApprox(tp.resultType, theMap, seen, internal) + if (args eq tp.args) && (resTp eq tp.resultType) then + tp + else + FunProtoTyped(args, resTp)(ctx.typer, tp.applyKind) case tp: IgnoredProto => WildcardType case _: ThisType | _: BoundType => // default case, inlined for speed diff --git a/tests/neg/i13340.scala b/tests/neg/i13340.scala new file mode 100644 index 000000000000..1db3f32af52d --- /dev/null +++ b/tests/neg/i13340.scala @@ -0,0 +1,3 @@ +case class Field(name: String, subQuery: Option[Query] = None) +case class Query(fields: Seq[Field]) +val x = Query(Seq(Field("a", subQuery=Some(Query(Seq(Field("b")), Nil)))), Nil) // error