Skip to content

Commit

Permalink
Merge pull request #12979 from dotty-staging/fix-12949
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand authored Aug 27, 2021
2 parents 094a3c5 + 733200e commit bf7009e
Show file tree
Hide file tree
Showing 23 changed files with 119 additions and 38 deletions.
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ object desugar {
DefDef(
className.toTermName, joinParams(constrTparams, defParamss),
classTypeRef, creatorExpr)
.withMods(companionMods | mods.flags.toTermFlags & GivenOrImplicit | Synthetic | Final)
.withMods(companionMods | mods.flags.toTermFlags & GivenOrImplicit | Final)
.withSpan(cdef.span) :: Nil
}

Expand All @@ -809,7 +809,7 @@ object desugar {
Nil
}
}
val classMods = if mods.is(Given) then mods &~ Given | Synthetic else mods
val classMods = if mods.is(Given) then mods | Synthetic else mods
cpy.TypeDef(cdef: TypeDef)(
name = className,
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class TreeMapWithImplicits extends tpd.TreeMap {
private def nestedScopeCtx(defs: List[Tree])(using Context): Context = {
val nestedCtx = ctx.fresh.setNewScope
defs foreach {
case d: DefTree if d.symbol.isOneOf(GivenOrImplicit) => nestedCtx.enter(d.symbol)
case d: DefTree if d.symbol.isOneOf(GivenOrImplicitVal) => nestedCtx.enter(d.symbol)
case _ =>
}
nestedCtx
Expand All @@ -74,7 +74,7 @@ class TreeMapWithImplicits extends tpd.TreeMap {
new TreeTraverser {
def traverse(tree: Tree)(using Context): Unit = {
tree match {
case d: DefTree if d.symbol.isOneOf(GivenOrImplicit) =>
case d: DefTree if d.symbol.isOneOf(GivenOrImplicitVal) =>
nestedCtx.enter(d.symbol)
case _ =>
}
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import Scopes._
import Uniques._
import ast.Trees._
import ast.untpd
import Flags.GivenOrImplicit
import util.{NoSource, SimpleIdentityMap, SourceFile, HashSet, ReusableInstance}
import typer.{Implicits, ImportInfo, Inliner, SearchHistory, SearchRoot, TypeAssigner, Typer, Nullables}
import Nullables._
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ object Flags {
val (Param @ _, TermParam @ _, TypeParam @ _) = newFlags(8, "<param>")

/** Labeled with `implicit` modifier (implicit value) */
val (Implicit @ _, ImplicitTerm @ _, _) = newFlags(9, "implicit")
val (Implicit @ _, ImplicitVal @ _, _) = newFlags(9, "implicit")

/** Labeled with `lazy` (a lazy val) / a trait */
val (LazyOrTrait @ _, Lazy @ _, Trait @ _) = newFlags(10, "lazy", "<trait>")
Expand Down Expand Up @@ -321,7 +321,7 @@ object Flags {
val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "<extension>")

/** An inferable (`given`) parameter */
val (Given @ _, _, _) = newFlags(29, "given")
val (Given @ _, GivenVal @ _, _) = newFlags(29, "given")

/** Symbol is defined by a Java class */
val (JavaDefined @ _, JavaDefinedVal @ _, _) = newFlags(30, "<java>")
Expand Down Expand Up @@ -568,6 +568,7 @@ object Flags {
val FinalOrSealed: FlagSet = Final | Sealed
val GivenOrImplicit: FlagSet = Given | Implicit
val GivenOrImplicitVal: FlagSet = GivenOrImplicit.toTermFlags
val GivenMethod: FlagSet = Given | Method
val InlineOrProxy: FlagSet = Inline | InlineProxy // An inline method or inline argument proxy */
val InlineMethod: FlagSet = Inline | Method
val InlineParam: FlagSet = Inline | Param
Expand Down Expand Up @@ -600,7 +601,6 @@ object Flags {
val Scala2Trait: FlagSet = Scala2x | Trait
val SyntheticArtifact: FlagSet = Synthetic | Artifact
val SyntheticCase: FlagSet = Synthetic | Case
val SyntheticGivenMethod: FlagSet = Synthetic | Given | Method
val SyntheticModule: FlagSet = Synthetic | Module
val SyntheticOpaque: FlagSet = Synthetic | Opaque
val SyntheticParam: FlagSet = Synthetic | Param
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Scopes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ object Scopes {
var irefs = new mutable.ListBuffer[TermRef]
var e = lastEntry
while (e ne null) {
if (e.sym.isOneOf(GivenOrImplicit)) {
if (e.sym.isOneOf(GivenOrImplicitVal)) {
val d = e.sym.denot
irefs += TermRef(NoPrefix, d.symbol.asTerm).withDenot(d)
}
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,19 @@ 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 =
is(Method) && isOneOf(GivenOrImplicit)
&& ( is(Synthetic) // previous scheme used in 3.0
|| cls.isOneOf(GivenOrImplicit) // new scheme from 3.1
)
&& 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 Expand Up @@ -2231,7 +2244,7 @@ object SymDenotations {
if (keepOnly eq implicitFilter)
if (this.is(Package)) Iterator.empty
// implicits in package objects are added by the overriding `memberNames` in `PackageClassDenotation`
else info.decls.iterator.filter(_.isOneOf(GivenOrImplicit))
else info.decls.iterator.filter(_.isOneOf(GivenOrImplicitVal))
else info.decls.iterator
for (sym <- ownSyms) maybeAdd(sym.name)
names
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class CyclicReference private (val denot: SymDenotation) extends TypeError {
}

// Give up and give generic errors.
else if (cycleSym.isOneOf(GivenOrImplicit, butNot = Method) && cycleSym.owner.isTerm)
else if (cycleSym.isOneOf(GivenOrImplicitVal, butNot = Method) && cycleSym.owner.isTerm)
CyclicReferenceInvolvingImplicit(cycleSym)
else
CyclicReferenceInvolving(denot)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -722,9 +722,9 @@ class TreePickler(pickler: TastyPickler) {
if flags.is(Invisible) then writeModTag(INVISIBLE)
if (flags.is(Erased)) writeModTag(ERASED)
if (flags.is(Exported)) writeModTag(EXPORTED)
if (flags.is(Given)) writeModTag(GIVEN)
if (flags.is(Implicit)) writeModTag(IMPLICIT)
if (isTerm) {
if (flags.is(Implicit)) writeModTag(IMPLICIT)
if (flags.is(Given)) writeModTag(GIVEN)
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
if (flags.is(Mutable)) writeModTag(MUTABLE)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/interactive/Completion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ object Completion {
val extMethodsFromImplicitScope = extractMemberExtensionMethods(implicitScopeCompanions)

// 4. The reference is of the form r.m and the extension method is defined in some given instance in the implicit scope of the type of r.
val givensInImplicitScope = implicitScopeCompanions.flatMap(_.membersBasedOnFlags(required = Given, excluded = EmptyFlags)).map(_.info)
val givensInImplicitScope = implicitScopeCompanions.flatMap(_.membersBasedOnFlags(required = GivenVal, excluded = EmptyFlags)).map(_.info)
val extMethodsFromGivensInImplicitScope = extractMemberExtensionMethods(givensInImplicitScope)

val availableExtMethods = extMethodsFromGivensInImplicitScope ++ extMethodsFromImplicitScope ++ extMethodsFromGivensInScope ++ extMethodsInScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class ExtractSemanticDB extends Phase:
|| sym.isLocalDummy
|| sym.is(Synthetic)
|| sym.isSetter
|| sym.isOldStyleImplicitConversion(forImplicitClassOnly = true)
|| sym.owner.isGivenInstanceSummoner
|| excludeDefOrUse(sym)

private def excludeDefOrUse(sym: Symbol)(using Context): Boolean =
Expand All @@ -102,6 +104,7 @@ class ExtractSemanticDB extends Phase:
private def excludeChildren(sym: Symbol)(using Context): Boolean =
!sym.exists
|| sym.is(Param) && sym.info.bounds.hi.isInstanceOf[Types.HKTypeLambda]
|| sym.isOldStyleImplicitConversion(forImplicitClassOnly = true)

/** Uses of this symbol where the reference has given span should be excluded from semanticdb */
private def excludeUse(qualifier: Option[Symbol], sym: Symbol)(using Context): Boolean =
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/semanticdb/Tools.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ object Tools:
document.copy(text = text)
end loadTextDocument

def loadTextDocumentUnsafe(scalaAbsolutePath: Path, semanticdbAbsolutePath: Path): TextDocument =
val docs = parseTextDocuments(semanticdbAbsolutePath).documents
assert(docs.length == 1)
docs.head.copy(text = new String(Files.readAllBytes(scalaAbsolutePath), StandardCharsets.UTF_8))

/** Parses SemanticDB text documents from an absolute path to a `*.semanticdb` file. */
private def parseTextDocuments(path: Path): TextDocuments =
val bytes = Files.readAllBytes(path) // NOTE: a semanticdb file is a TextDocuments message, not TextDocument
Expand Down
25 changes: 25 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ object SymUtils:

def isGenericProduct(using Context): Boolean = whyNotGenericProduct.isEmpty

/** Is this an old style implicit conversion?
* @param directOnly only consider explicitly written methods
* @param forImplicitClassOnly only consider methods generated from implicit classes
*/
def isOldStyleImplicitConversion(directOnly: Boolean = false, forImplicitClassOnly: Boolean = false)(using Context): Boolean =
self.is(Implicit) && self.info.stripPoly.match
case mt @ MethodType(_ :: Nil) if !mt.isImplicitMethod =>
if self.isCoDefinedGiven(mt.finalResultType.typeSymbol)
then !directOnly
else !forImplicitClassOnly
case _ =>
false

/** Is this the method that summons a structural given instance? */
def isGivenInstanceSummoner(using Context): Boolean =
def isCodefined(info: Type): Boolean = info.stripPoly match
case mt: MethodType =>
// given summoner can only have contextual params
mt.isImplicitMethod && isCodefined(mt.resultType)
case mt: ExprType =>
isCodefined(mt.resultType)
case res =>
self.isCoDefinedGiven(res.typeSymbol)
self.isAllOf(Given | Method) && isCodefined(self.info)

def useCompanionAsMirror(using Context): Boolean = self.linkedClass.exists && !self.is(Scala2x)

/** Is this a sealed class or trait for which a sum mirror is generated?
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1631,7 +1631,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 @@ -1641,7 +1641,7 @@ trait Applications extends Compatibility {
* To compare specificity we should compare with `T`, not with its implementation `I[X]`.
* No such widening is performed for given aliases, which are not synthetic. E.g.
*
* given J[X] as T = rhs
* given J[X]: T = rhs
*
* already has the right result type `T`. Neither is widening performed for given
* objects, since these are anyway taken to be more specific than methods
Expand All @@ -1652,8 +1652,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
14 changes: 3 additions & 11 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ object Checking {
if (sym.is(Implicit)) {
if (sym.owner.is(Package))
fail(TopLevelCantBeImplicit(sym))
if (sym.isType)
if sym.isType && (!sym.isClass || sym.is(Trait)) then
fail(TypesAndTraitsCantBeImplicit())
}
if sym.is(Transparent) then
Expand Down Expand Up @@ -861,22 +861,14 @@ trait Checking {
/** If `sym` is an old-style implicit conversion, check that implicit conversions are enabled.
* @pre sym.is(GivenOrImplicit)
*/
def checkImplicitConversionDefOK(sym: Symbol)(using Context): Unit = {
def check(): Unit =
def checkImplicitConversionDefOK(sym: Symbol)(using Context): Unit =
if sym.isOldStyleImplicitConversion(directOnly = true) then
checkFeature(
nme.implicitConversions,
i"Definition of implicit conversion $sym",
ctx.owner.topLevelClass,
sym.srcPos)

sym.info.stripPoly match {
case mt @ MethodType(_ :: Nil)
if !mt.isImplicitMethod && !sym.is(Synthetic) => // it's an old-styleconversion
check()
case _ =>
}
}

/** If `tree` is an application of a new-style implicit conversion (using the apply
* method of a `scala.Conversion` instance), check that implicit conversions are
* enabled.
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class ImportInfo(symf: Context ?=> Symbol,
else
for
renamed <- reverseMapping.keys
denot <- pre.member(reverseMapping(renamed)).altsWith(_.isOneOf(GivenOrImplicit))
denot <- pre.member(reverseMapping(renamed)).altsWith(_.isOneOf(GivenOrImplicitVal))
yield
val original = reverseMapping(renamed)
val ref = TermRef(pre, original, denot)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class Namer { typer: Typer =>

tree match {
case tree: TypeDef if tree.isClassDef =>
val flags = checkFlags(tree.mods.flags &~ Implicit)
val flags = checkFlags(tree.mods.flags)
val name = checkNoConflict(tree.name, flags.is(Private), tree.span).asTypeName
val cls =
createOrRefine[ClassSymbol](tree, name, flags, ctx.owner,
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ trait QuotesAndSplices {
tdef.symbol.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
val bindingType = getBinding(tdef.symbol).symbol.typeRef
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
val sym = newPatternBoundSymbol(nameOfSyntheticGiven, bindingTypeTpe, tdef.span, flags = ImplicitTerm)(using ctx0)
val sym = newPatternBoundSymbol(nameOfSyntheticGiven, bindingTypeTpe, tdef.span, flags = ImplicitVal)(using ctx0)
buff += Bind(sym, untpd.Ident(nme.WILDCARD).withType(bindingTypeTpe)).withSpan(tdef.span)
super.transform(tdef)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/test-resources/repl/i1374
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
scala> implicit class Padder(val sb: StringBuilder) extends AnyVal { def pad2(width: Int) = { 1 to width - sb.length foreach { sb append '*' }; sb } }
// defined class Padder
def Padder(sb: StringBuilder): Padder
scala> val greeting = new StringBuilder("Hello, kitteh!")
val greeting: StringBuilder = Hello, kitteh!
scala> val a = greeting pad2 20
Expand Down
24 changes: 24 additions & 0 deletions compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ import dotty.tools.dotc.util.SourceFile
@main def updateExpect =
SemanticdbTests().runExpectTest(updateExpectFiles = true)

/** Useful for printing semanticdb metac output for one file
*
* @param root the output directory containing semanticdb output,
* only 1 semanticdb file should be present
* @param source the single source file producing the semanticdb
*/
@main def metac(root: String, source: String) =
val rootSrc = Paths.get(root)
val sourceSrc = Paths.get(source)
val semanticFile = FileSystems.getDefault.getPathMatcher("glob:**.semanticdb")
def inputFile(): Path =
val ls = Files.walk(rootSrc.resolve("META-INF").resolve("semanticdb"))
val files =
try ls.filter(p => semanticFile.matches(p)).collect(Collectors.toList).asScala
finally ls.close()
require(files.sizeCompare(1) == 0, s"No semanticdb files! $rootSrc")
files.head
val metacSb: StringBuilder = StringBuilder(5000)
val semanticdbPath = inputFile()
val doc = Tools.loadTextDocumentUnsafe(sourceSrc.toAbsolutePath, semanticdbPath)
Tools.metac(doc, Paths.get(doc.uri))(using metacSb)
Files.write(rootSrc.resolve("metac.expect"), metacSb.toString.getBytes(StandardCharsets.UTF_8))


@Category(Array(classOf[BootstrappedOnlyTests]))
class SemanticdbTests:
val javaFile = FileSystems.getDefault.getPathMatcher("glob:**.java")
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
2 changes: 1 addition & 1 deletion tests/semanticdb/expect/InventedNames.expect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ val f/*<-givens::InventedNames$package.f.*/ = given_Float/*->givens::InventedNam
val g/*<-givens::InventedNames$package.g.*/ = `* *`/*->givens::InventedNames$package.`* *`.*/
val x/*<-givens::InventedNames$package.x.*/ = given_X/*->givens::InventedNames$package.given_X.*/
val y/*<-givens::InventedNames$package.y.*/ = given_Y/*->givens::InventedNames$package.given_Y().*/
val z/*<-givens::InventedNames$package.z.*/ = given_Z_T/*->givens::InventedNames$package.given_Z_T().*/[String/*->scala::Predef.String#*/]
val z/*<-givens::InventedNames$package.z.*/ = given_Z_T/*->givens::InventedNames$package.given_Z_T().*/[String/*->scala::Predef.String#*/]
2 changes: 1 addition & 1 deletion tests/semanticdb/expect/InventedNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ val f = given_Float
val g = `* *`
val x = given_X
val y = given_Y
val z = given_Z_T[String]
val z = given_Z_T[String]
Loading

0 comments on commit bf7009e

Please sign in to comment.