diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 112bd182b1ad..9e8b925c6eb9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -711,6 +711,17 @@ object SymDenotations { } ) + /** Do this symbol and `cls` represent a pair of a given or implicit method and + * its associated class that were defined by a single definition? + * This can mean one of two things: + * - the method and class are defined in a structural given instance, or + * - the class is an implicit class and the method is its implicit conversion. + */ + final def isCoDefinedGiven(cls: Symbol)(using Context): Boolean = + isAllOf(Method | Synthetic) + && isOneOf(GivenOrImplicit) + && name == cls.name.toTermName && owner == cls.owner + /** Is this a denotation of a stable term (or an arbitrary type)? * Terms are stable if they are idempotent (as in TreeInfo.Idempotent): that is, they always return the same value, * if any. diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 7ada64b2c50b..e8d41adec9d2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1627,7 +1627,7 @@ trait Applications extends Compatibility { /** Widen the result type of synthetic given methods from the implementation class to the * type that's implemented. Example * - * given I[X] as T { ... } + * given I[X]: T with { ... } * * This desugars to * @@ -1648,8 +1648,8 @@ trait Applications extends Compatibility { mt.derivedLambdaType(mt.paramNames, mt.paramInfos, widenGiven(mt.resultType, alt)) case pt: PolyType => pt.derivedLambdaType(pt.paramNames, pt.paramInfos, widenGiven(pt.resultType, alt)) - case _ => - if (alt.symbol.isAllOf(SyntheticGivenMethod)) tp.widenToParents + case rt => + if alt.symbol.isCoDefinedGiven(rt.typeSymbol) then tp.widenToParents else tp } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e1415f908cb8..07d63e32c921 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -847,7 +847,8 @@ trait Checking { sym.info.stripPoly match { case mt @ MethodType(_ :: Nil) - if !mt.isImplicitMethod && !sym.is(Synthetic) => // it's an old-styleconversion + if !mt.isImplicitMethod && !sym.isCoDefinedGiven(mt.finalResultType.typeSymbol) => + // it's an old-style conversion check() case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9b286163c468..89f4c38e898d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1101,17 +1101,25 @@ class Namer { typer: Typer => def addWildcardForwarders(seen: List[TermName], span: Span): Unit = val nonContextual = mutable.HashSet(seen: _*) - for mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do - if !mbr.symbol.isSuperAccessor then - // Scala 2 superaccessors have neither Synthetic nor Artfact set, so we - // need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts) - val alias = mbr.name.toTermName - if mbr.symbol.is(Given) then - if !seen.contains(alias) && mbr.matchesImportBound(givenBound) then - addForwarder(alias, mbr, span) - else if !nonContextual.contains(alias) && mbr.matchesImportBound(wildcardBound) then - nonContextual += alias - addWildcardForwardersNamed(alias, span) + for + mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = Artifact|Private) + if !mbr.symbol.isSuperAccessor + do + val alias = mbr.name.toTermName + val isSynthetic = mbr.filterWithFlags(required = Synthetic, excluded = EmptyFlags).exists + // Scala 2 superaccessors have neither Synthetic nor Artfact set, so we + // need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts) + if mbr.symbol.is(Given) then + val eligable = !seen.contains(alias) && mbr.matchesImportBound(givenBound) + if + eligable && (!isSynthetic || mbr.symbol.exists + && mbr.symbol.isCoDefinedGiven(mbr.info.finalResultType.typeSymbol) + ) + then + addForwarder(alias, mbr, span) + else if !isSynthetic && !nonContextual.contains(alias) && mbr.matchesImportBound(wildcardBound) then + nonContextual += alias + addWildcardForwardersNamed(alias, span) def addForwarders(sels: List[untpd.ImportSelector], seen: List[TermName]): Unit = sels match case sel :: sels1 => diff --git a/tests/pos/i12949.scala b/tests/pos/i12949.scala new file mode 100644 index 000000000000..5a886aa894b3 --- /dev/null +++ b/tests/pos/i12949.scala @@ -0,0 +1,19 @@ +object Catch22: + trait TC[V] + object TC: + export Hodor.TC.given + +object Hodor: + object TC: + import Catch22.TC + given fromString[V <: String]: TC[V] = ??? + transparent inline given fromDouble[V <: Double]: TC[V] = + new TC[V]: + type Out = Double + given fromInt[V <: Int]: TC[V] with + type Out = Int + +object Test: + summon[Catch22.TC["hi"]] //works + summon[Catch22.TC[7.7]] //works + summon[Catch22.TC[1]] //error