Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make def generated from givens not synthetic #12979

Merged
merged 10 commits into from
Aug 27, 2021
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)
bishabosha marked this conversation as resolved.
Show resolved Hide resolved
.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 @@ -721,9 +721,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 @@ -322,7 +322,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 =
bishabosha marked this conversation as resolved.
Show resolved Hide resolved
bishabosha marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -1628,7 +1628,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 @@ -1638,7 +1638,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 @@ -1649,8 +1649,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