Skip to content

Commit

Permalink
fix scala#12949: export synthetic given co-defined with class
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Odersky <odersky@gmail.com>
Co-authored-by: Jamie Thompson <bishbashboshjt@gmail.com>
  • Loading branch information
bishabosha and odersky committed Jul 2, 2021
1 parent 8a6703f commit 657ab61
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 15 deletions.
11 changes: 11 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _ =>
}
Expand Down
30 changes: 19 additions & 11 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
19 changes: 19 additions & 0 deletions tests/pos/i12949.scala
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 657ab61

Please sign in to comment.