@@ -47,27 +47,40 @@ object ProtoTypes {
4747 necessarySubType(tpn, pt) || tpn.isValueSubType(pt) || viewExists(tpn, pt)
4848
4949 /** Test compatibility after normalization.
50- * Do this in a fresh typerstate unless `keepConstraint` is true .
50+ * If `keepConstraint` is false, the current constraint set will not be modified by this call .
5151 */
52- def normalizedCompatible (tp : Type , pt : Type , keepConstraint : Boolean )(using Context ): Boolean = {
53- def testCompat (using Context ): Boolean = {
52+ def normalizedCompatible (tp : Type , pt : Type , keepConstraint : Boolean )(using Context ): Boolean =
53+
54+ def testCompat (using Context ): Boolean =
5455 val normTp = normalize(tp, pt)
5556 isCompatible(normTp, pt) || pt.isRef(defn.UnitClass ) && normTp.isParameterless
56- }
57- if ( keepConstraint)
58- tp.widenSingleton match {
57+
58+ if keepConstraint then
59+ tp.widenSingleton match
5960 case poly : PolyType =>
60- // We can't keep the constraint in this case, since we have to add type parameters
61- // to it, but there's no place to associate them with type variables.
62- // So we'd get a "inconsistent: no typevars were added to committable constraint"
63- // assertion failure in `constrained`. To do better, we'd have to change the
64- // constraint handling architecture so that some type parameters are committable
65- // and others are not. But that's a whole different ballgame.
66- normalizedCompatible(tp, pt, keepConstraint = false )
61+ val newctx = ctx.fresh.setNewTyperState()
62+ val result = testCompat(using newctx)
63+ typr.println(
64+ i """ normalizedCompatible for $poly, $pt = $result
65+ |constraint was: ${ctx.typerState.constraint}
66+ |constraint now: ${newctx.typerState.constraint}""" )
67+ if result
68+ && (ctx.typerState.constraint ne newctx.typerState.constraint)
69+ && {
70+ val existingVars = ctx.typerState.uninstVars.toSet
71+ newctx.typerState.uninstVars.forall(existingVars.contains)
72+ }
73+ then newctx.typerState.commit()
74+ // If the new constrait contains fresh type variables we cannot keep it,
75+ // since those type variables are not instantiated anywhere in the source.
76+ // See pos/i6682a.scala for a test case. See pos/11243.scala and pos/i5773b.scala
77+ // for tests where it matters that we keep the constraint otherwise.
78+ // TODO: A better solution would clean the new constraint, so that it "avoids"
79+ // the problematic type variables. But we have not implemented such an algorithm yet.
80+ result
6781 case _ => testCompat
68- }
6982 else explore(testCompat)
70- }
83+ end normalizedCompatible
7184
7285 private def disregardProto (pt : Type )(using Context ): Boolean =
7386 pt.dealias.isRef(defn.UnitClass )
@@ -79,10 +92,18 @@ object ProtoTypes {
7992 val savedConstraint = ctx.typerState.constraint
8093 val res = pt.widenExpr match {
8194 case pt : FunProto =>
82- mt match {
83- case mt : MethodType => constrainResult(resultTypeApprox(mt), pt.resultType)
95+ mt match
96+ case mt : MethodType =>
97+ constrainResult(resultTypeApprox(mt), pt.resultType)
98+ && {
99+ if pt.constrainResultDeep
100+ && mt.isImplicitMethod == (pt.applyKind == ApplyKind .Using )
101+ then
102+ pt.args.lazyZip(mt.paramInfos).forall((arg, paramInfo) =>
103+ pt.typedArg(arg, paramInfo).tpe <:< paramInfo)
104+ else true
105+ }
84106 case _ => true
85- }
86107 case _ : ValueTypeOrProto if ! disregardProto(pt) =>
87108 necessarilyCompatible(mt, pt)
88109 case pt : WildcardType if pt.optBounds.exists =>
@@ -123,6 +144,7 @@ object ProtoTypes {
123144 abstract case class IgnoredProto (ignored : Type ) extends CachedGroundType with MatchAlways :
124145 override def revealIgnored = ignored
125146 override def deepenProto (using Context ): Type = ignored
147+ override def deepenProtoTrans (using Context ): Type = ignored.deepenProtoTrans
126148
127149 override def computeHash (bs : Hashable .Binders ): Int = doHash(bs, ignored)
128150
@@ -202,7 +224,12 @@ object ProtoTypes {
202224 def map (tm : TypeMap )(using Context ): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat)
203225 def fold [T ](x : T , ta : TypeAccumulator [T ])(using Context ): T = ta(x, memberProto)
204226
205- override def deepenProto (using Context ): SelectionProto = derivedSelectionProto(name, memberProto.deepenProto, compat)
227+ override def deepenProto (using Context ): SelectionProto =
228+ derivedSelectionProto(name, memberProto.deepenProto, compat)
229+
230+ override def deepenProtoTrans (using Context ): SelectionProto =
231+ derivedSelectionProto(name, memberProto.deepenProtoTrans, compat)
232+
206233 override def computeHash (bs : Hashable .Binders ): Int = {
207234 val delta = (if (compat eq NoViewsAllowed ) 1 else 0 ) | (if (privateOK) 2 else 0 )
208235 addDelta(doHash(bs, name, memberProto), delta)
@@ -276,9 +303,21 @@ object ProtoTypes {
276303 /** A prototype for expressions that appear in function position
277304 *
278305 * [](args): resultType
306+ *
307+ * @param args The untyped arguments to which the function is applied
308+ * @param resType The expeected result type
309+ * @param typer The typer to use for typing the arguments
310+ * @param applyKind The kind of application (regular/using/tupled infix operand)
311+ * @param state The state object to use for tracking the changes to this prototype
312+ * @param constrainResultDeep
313+ * A flag to indicate that constrainResult on this prototype
314+ * should typecheck and compare the arguments.
279315 */
280- case class FunProto (args : List [untpd.Tree ], resType : Type )(typer : Typer ,
281- override val applyKind : ApplyKind , state : FunProtoState = new FunProtoState )(using protoCtx : Context )
316+ case class FunProto (args : List [untpd.Tree ], resType : Type )(
317+ typer : Typer ,
318+ override val applyKind : ApplyKind ,
319+ state : FunProtoState = new FunProtoState ,
320+ val constrainResultDeep : Boolean = false )(using protoCtx : Context )
282321 extends UncachedGroundType with ApplyingProto with FunOrPolyProto {
283322 override def resultType (using Context ): Type = resType
284323
@@ -290,9 +329,17 @@ object ProtoTypes {
290329 typer.isApplicableType(tp, args, resultType, keepConstraint && ! args.exists(isPoly))
291330 }
292331
293- def derivedFunProto (args : List [untpd.Tree ] = this .args, resultType : Type , typer : Typer = this .typer): FunProto =
294- if ((args eq this .args) && (resultType eq this .resultType) && (typer eq this .typer)) this
295- else new FunProto (args, resultType)(typer, applyKind)
332+ def derivedFunProto (
333+ args : List [untpd.Tree ] = this .args,
334+ resultType : Type = this .resultType,
335+ typer : Typer = this .typer,
336+ constrainResultDeep : Boolean = this .constrainResultDeep): FunProto =
337+ if (args eq this .args)
338+ && (resultType eq this .resultType)
339+ && (typer eq this .typer)
340+ && constrainResultDeep == this .constrainResultDeep
341+ then this
342+ else new FunProto (args, resultType)(typer, applyKind, constrainResultDeep = constrainResultDeep)
296343
297344 /** @return True if all arguments have types.
298345 */
@@ -419,7 +466,11 @@ object ProtoTypes {
419466 def fold [T ](x : T , ta : TypeAccumulator [T ])(using Context ): T =
420467 ta(ta.foldOver(x, typedArgs().tpes), resultType)
421468
422- override def deepenProto (using Context ): FunProto = derivedFunProto(args, resultType.deepenProto, typer)
469+ override def deepenProto (using Context ): FunProto =
470+ derivedFunProto(args, resultType.deepenProto)
471+
472+ override def deepenProtoTrans (using Context ): FunProto =
473+ derivedFunProto(args, resultType.deepenProtoTrans, constrainResultDeep = true )
423474
424475 override def withContext (newCtx : Context ): ProtoType =
425476 if newCtx `eq` protoCtx then this
@@ -472,7 +523,11 @@ object ProtoTypes {
472523 def fold [T ](x : T , ta : TypeAccumulator [T ])(using Context ): T =
473524 ta(ta(x, argType), resultType)
474525
475- override def deepenProto (using Context ): ViewProto = derivedViewProto(argType, resultType.deepenProto)
526+ override def deepenProto (using Context ): ViewProto =
527+ derivedViewProto(argType, resultType.deepenProto)
528+
529+ override def deepenProtoTrans (using Context ): ViewProto =
530+ derivedViewProto(argType, resultType.deepenProtoTrans)
476531 }
477532
478533 class CachedViewProto (argType : Type , resultType : Type ) extends ViewProto (argType, resultType) {
@@ -522,7 +577,11 @@ object ProtoTypes {
522577 def fold [T ](x : T , ta : TypeAccumulator [T ])(using Context ): T =
523578 ta(ta.foldOver(x, targs.tpes), resultType)
524579
525- override def deepenProto (using Context ): PolyProto = derivedPolyProto(targs, resultType.deepenProto)
580+ override def deepenProto (using Context ): PolyProto =
581+ derivedPolyProto(targs, resultType.deepenProto)
582+
583+ override def deepenProtoTrans (using Context ): PolyProto =
584+ derivedPolyProto(targs, resultType.deepenProtoTrans)
526585 }
527586
528587 /** A prototype for expressions [] that are known to be functions:
0 commit comments