From 941301f8048c52739782b96d2633785b0f39f052 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Jun 2021 12:03:07 +0200 Subject: [PATCH 01/10] Make def generated from givens not synthetic Fixes #12949 --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/pos/i12949.scala | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i12949.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 057a93196154..4f3186941513 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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 } 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 From 2fe623c43f302e24a7dc2ae256b994550c816e50 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Jun 2021 17:48:06 +0200 Subject: [PATCH 02/10] Another scheme to mark implicit methods that were synthetic Find other scheme to know whether some method is a structural given instance or an implicit conversion associated with an implicit class. Previously we characterized these methods by their Synthetic flag, but that meant that they were not automatically exported. So we now drop Synthetic and use a flag `GivenClass` on the class half of the pair. The old scheme is still recognized in order to maintain Tasty backwards compatibility. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 5 ++++- compiler/src/dotty/tools/dotc/core/Flags.scala | 6 +++--- .../src/dotty/tools/dotc/core/SymDenotations.scala | 13 +++++++++++++ .../src/dotty/tools/dotc/typer/Applications.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/Checking.scala | 3 ++- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 4f3186941513..d87f56ffd76b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -809,7 +809,10 @@ object desugar { Nil } } - val classMods = if mods.is(Given) then mods &~ Given | Synthetic else mods + val classMods = + if mods.is(Given) then mods &~ Given | Synthetic | GivenClass + else if mods.is(Implicit) then mods &~ Implicit | GivenClass + else mods cpy.TypeDef(cdef: TypeDef)( name = className, rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1, diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 890a0763938e..95bb442ccd95 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -314,8 +314,8 @@ object Flags { /** A Scala 2x super accessor / an unpickled Scala 2.x class */ val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "", "") - /** A parameter with a default value */ - val (_, HasDefault @ _, _) = newFlags(27, "") + /** A parameter with a default value / A structural given class or an implicit class */ + val (_, HasDefault @ _, GivenClass @ _) = newFlags(27, "") /** An extension method, or a collective extension instance */ val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "") @@ -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 @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a7d45abd7a41..ae669239d3de 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -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.is(GivenClass) // 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. diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 008b96a4d174..c15287cae3a1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -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 } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 5534d0c795fc..2cc6fd1ef6e7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -871,7 +871,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 _ => } From 78a010b50de5cc4aa433a082f22e463c8f73d9d1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Jun 2021 19:00:13 +0200 Subject: [PATCH 03/10] Pickle GivenClass flag Pickle GivenClass flag as GIVEN --- .../src/dotty/tools/dotc/core/tasty/TreePickler.scala | 1 + .../src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 78a2c73de3ea..42a55c88093b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -744,6 +744,7 @@ class TreePickler(pickler: TastyPickler) { if (flags.is(Contravariant)) writeModTag(CONTRAVARIANT) if (flags.is(Opaque)) writeModTag(OPAQUE) if (flags.is(Open)) writeModTag(OPEN) + if (flags.is(GivenClass)) writeModTag(GIVEN) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 79088214519b..ce4b043a8538 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -563,7 +563,7 @@ class TreeUnpickler(reader: TastyReader, val rhsStart = currentAddr val rhsIsEmpty = nothingButMods(end) if (!rhsIsEmpty) skipTree() - val (givenFlags, annotFns, privateWithin) = readModifiers(end) + val (givenFlags, annotFns, privateWithin) = readModifiers(end, name.isTypeName) pickling.println(i"creating symbol $name at $start with flags $givenFlags") val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = @@ -623,7 +623,7 @@ class TreeUnpickler(reader: TastyReader, /** Read modifier list into triplet of flags, annotations and a privateWithin * boundary symbol. */ - def readModifiers(end: Addr)(using Context): (FlagSet, List[Symbol => Annotation], Symbol) = { + def readModifiers(end: Addr, isType: Boolean)(using Context): (FlagSet, List[Symbol => Annotation], Symbol) = { var flags: FlagSet = EmptyFlags var annotFns: List[Symbol => Annotation] = Nil var privateWithin: Symbol = NoSymbol @@ -667,7 +667,7 @@ class TreeUnpickler(reader: TastyReader, case HASDEFAULT => addFlag(HasDefault) case STABLE => addFlag(StableRealizable) case EXTENSION => addFlag(Extension) - case GIVEN => addFlag(Given) + case GIVEN => addFlag(if isType then GivenClass else Given) case PARAMsetter => addFlag(ParamAccessor) case PARAMalias => addFlag(SuperParamAlias) case EXPORTED => addFlag(Exported) @@ -1236,7 +1236,7 @@ class TreeUnpickler(reader: TastyReader, readName() readType() val body = readTerm() - val (givenFlags, _, _) = readModifiers(end) + val (givenFlags, _, _) = readModifiers(end, sym.isType) sym.setFlag(givenFlags) Bind(sym, body) case ALTERNATIVE => From 5d7256d10dc23e4f12eb5cddff8797858abdf952 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Jul 2021 19:53:51 +0200 Subject: [PATCH 04/10] Update repl expect file --- compiler/test-resources/repl/i1374 | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/test-resources/repl/i1374 b/compiler/test-resources/repl/i1374 index a56b15df2818..3d117fdb4ff9 100644 --- a/compiler/test-resources/repl/i1374 +++ b/compiler/test-resources/repl/i1374 @@ -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 From 8a334ca42917e7e070421cc2c5bae06426731e7f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Jul 2021 20:12:48 +0200 Subject: [PATCH 05/10] Specify GivenVal where appropriate Be more specific whether we specify a given or implicit val, or whether a class is also OK. This prepares the way for merging GivenClass and Given. --- compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Contexts.scala | 1 - compiler/src/dotty/tools/dotc/core/Flags.scala | 2 +- compiler/src/dotty/tools/dotc/core/Scopes.scala | 2 +- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeErrors.scala | 2 +- compiler/src/dotty/tools/dotc/interactive/Completion.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Applications.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/ImportInfo.scala | 2 +- 9 files changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala b/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala index 9e5b7f036aa0..359f5d5c2580 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala @@ -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 @@ -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 _ => } diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index ced6502833e3..dc1cc4602d6f 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -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._ diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 95bb442ccd95..f024ca0b1329 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -321,7 +321,7 @@ object Flags { val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "") /** An inferable (`given`) parameter */ - val (Given @ _, _, _) = newFlags(29, "given") + val (Given @ _, GivenVal @ _, GivenType @ _) = newFlags(29, "given") /** Symbol is defined by a Java class */ val (JavaDefined @ _, JavaDefinedVal @ _, _) = newFlags(30, "") diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index b22ce84849c1..5dc7ce168c24 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -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) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index ae669239d3de..3c7f850114f9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2244,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 diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 950963497fbc..c9ca98f65f5e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index bb552191fc36..7dfba5b58a82 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index c15287cae3a1..5ba4ae778b60 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -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 * @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index a975dd206d22..e8a923427b38 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -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) From 064bb3033b1fdcead13cc3cdbb155279f6f88d9d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Jul 2021 11:02:48 +0200 Subject: [PATCH 06/10] Merge GivenClass and Given Classes can now be Given (if generated from a given def) or Implicit (if declared as an implicit class) --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 +--- compiler/src/dotty/tools/dotc/core/Flags.scala | 2 +- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 3 +-- compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index d87f56ffd76b..11a406981131 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -810,9 +810,7 @@ object desugar { } } val classMods = - if mods.is(Given) then mods &~ Given | Synthetic | GivenClass - else if mods.is(Implicit) then mods &~ Implicit | GivenClass - else mods + if mods.is(Given) then mods | Synthetic else mods cpy.TypeDef(cdef: TypeDef)( name = className, rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1, diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index f024ca0b1329..93be890c5bbc 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -315,7 +315,7 @@ object Flags { val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "", "") /** A parameter with a default value / A structural given class or an implicit class */ - val (_, HasDefault @ _, GivenClass @ _) = newFlags(27, "") + val (_, HasDefault @ _, _) = newFlags(27, "") /** An extension method, or a collective extension instance */ val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "") diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 3c7f850114f9..fcfbba208eb9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -719,8 +719,8 @@ object SymDenotations { */ final def isCoDefinedGiven(cls: Symbol)(using Context): Boolean = is(Method) && isOneOf(GivenOrImplicit) - && ( is(Synthetic) // previous scheme used in 3.0 - || cls.is(GivenClass) // new scheme from 3.1 + && ( is(Synthetic) // previous scheme used in 3.0 + || cls.isOneOf(GivenOrImplicit) // new scheme from 3.1 ) && name == cls.name.toTermName && owner == cls.owner diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 42a55c88093b..1ace94bf1e9d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -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 (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) @@ -744,7 +744,6 @@ class TreePickler(pickler: TastyPickler) { if (flags.is(Contravariant)) writeModTag(CONTRAVARIANT) if (flags.is(Opaque)) writeModTag(OPAQUE) if (flags.is(Open)) writeModTag(OPEN) - if (flags.is(GivenClass)) writeModTag(GIVEN) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ce4b043a8538..b72cac61ab62 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -667,7 +667,7 @@ class TreeUnpickler(reader: TastyReader, case HASDEFAULT => addFlag(HasDefault) case STABLE => addFlag(StableRealizable) case EXTENSION => addFlag(Extension) - case GIVEN => addFlag(if isType then GivenClass else Given) + case GIVEN => addFlag(Given) case PARAMsetter => addFlag(ParamAccessor) case PARAMalias => addFlag(SuperParamAlias) case EXPORTED => addFlag(Exported) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 2cc6fd1ef6e7..669db53c9f89 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 29a6da138e6c..1eb6c47bce4a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -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, From 0925ac528b7638d9fae4fd4e4aaf1dc1beaf4e24 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Jul 2021 11:11:27 +0200 Subject: [PATCH 07/10] Fix formatting, commments & revert redundant changes --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 3 +-- compiler/src/dotty/tools/dotc/core/Flags.scala | 6 +++--- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- .../src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 11a406981131..8fd06ddc3933 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -809,8 +809,7 @@ object desugar { Nil } } - val classMods = - if mods.is(Given) then mods | 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, diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 93be890c5bbc..cb590e2384a0 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -233,7 +233,7 @@ object Flags { val (Param @ _, TermParam @ _, TypeParam @ _) = newFlags(8, "") /** 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", "") @@ -314,14 +314,14 @@ object Flags { /** A Scala 2x super accessor / an unpickled Scala 2.x class */ val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "", "") - /** A parameter with a default value / A structural given class or an implicit class */ + /** A parameter with a default value */ val (_, HasDefault @ _, _) = newFlags(27, "") /** An extension method, or a collective extension instance */ val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "") /** An inferable (`given`) parameter */ - val (Given @ _, GivenVal @ _, GivenType @ _) = newFlags(29, "given") + val (Given @ _, GivenVal @ _, _) = newFlags(29, "given") /** Symbol is defined by a Java class */ val (JavaDefined @ _, JavaDefinedVal @ _, _) = newFlags(30, "") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 1ace94bf1e9d..a195b157cacd 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -722,8 +722,8 @@ class TreePickler(pickler: TastyPickler) { 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(Lazy, butNot = Module)) writeModTag(LAZY) if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) } if (flags.is(Mutable)) writeModTag(MUTABLE) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index b72cac61ab62..79088214519b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -563,7 +563,7 @@ class TreeUnpickler(reader: TastyReader, val rhsStart = currentAddr val rhsIsEmpty = nothingButMods(end) if (!rhsIsEmpty) skipTree() - val (givenFlags, annotFns, privateWithin) = readModifiers(end, name.isTypeName) + val (givenFlags, annotFns, privateWithin) = readModifiers(end) pickling.println(i"creating symbol $name at $start with flags $givenFlags") val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = @@ -623,7 +623,7 @@ class TreeUnpickler(reader: TastyReader, /** Read modifier list into triplet of flags, annotations and a privateWithin * boundary symbol. */ - def readModifiers(end: Addr, isType: Boolean)(using Context): (FlagSet, List[Symbol => Annotation], Symbol) = { + def readModifiers(end: Addr)(using Context): (FlagSet, List[Symbol => Annotation], Symbol) = { var flags: FlagSet = EmptyFlags var annotFns: List[Symbol => Annotation] = Nil var privateWithin: Symbol = NoSymbol @@ -1236,7 +1236,7 @@ class TreeUnpickler(reader: TastyReader, readName() readType() val body = readTerm() - val (givenFlags, _, _) = readModifiers(end, sym.isType) + val (givenFlags, _, _) = readModifiers(end) sym.setFlag(givenFlags) Bind(sym, body) case ALTERNATIVE => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index fbae1afcbfbd..9c60374d9a9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -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) } From 9b5338d32d74da5351bc5c0c1dc6a0a164c76f7b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 23 Aug 2021 11:55:19 +0200 Subject: [PATCH 08/10] Exclude implicit class conversions in SemanticDB Conversions generated for implicit classes were synthetic before, now they need to be excluded separately. --- .../tools/dotc/semanticdb/ExtractSemanticDB.scala | 2 ++ .../src/dotty/tools/dotc/transform/SymUtils.scala | 13 +++++++++++++ compiler/src/dotty/tools/dotc/typer/Checking.scala | 13 ++----------- tests/semanticdb/expect/InventedNames.expect.scala | 2 +- tests/semanticdb/metac.expect | 11 ++++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index e3d6711af97f..9ac860a33112 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -79,6 +79,7 @@ class ExtractSemanticDB extends Phase: || sym.isLocalDummy || sym.is(Synthetic) || sym.isSetter + || sym.isOldStyleImplicitConversion(forImplicitClassOnly = true) || excludeDefOrUse(sym) private def excludeDefOrUse(sym: Symbol)(using Context): Boolean = @@ -102,6 +103,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 = diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 691425bfb713..7991cd752acf 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -87,6 +87,19 @@ object SymUtils: def isGenericProduct(using Context): Boolean = whyNotGenericProduct.isEmpty + /** Is this the 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 + 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? diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 669db53c9f89..0f838ac20755 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -861,23 +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.isCoDefinedGiven(mt.finalResultType.typeSymbol) => - // it's an old-style conversion - 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. diff --git a/tests/semanticdb/expect/InventedNames.expect.scala b/tests/semanticdb/expect/InventedNames.expect.scala index 131d4304da31..6f0ae238ce7b 100644 --- a/tests/semanticdb/expect/InventedNames.expect.scala +++ b/tests/semanticdb/expect/InventedNames.expect.scala @@ -25,7 +25,7 @@ given X/*->givens::X#*/ with given (using X/*->givens::X#*/): Y/*->givens::Y#*/ with def doY/*<-givens::InventedNames$package.given_Y#doY().*/ = "7" -given [T/*<-givens::InventedNames$package.given_Z_T#[T]*/]: Z/*->givens::Z#*/[T/*->givens::InventedNames$package.given_Z_T#[T]*/] with +given [T/*<-givens::InventedNames$package.given_Z_T#[T]*//*<-givens::InventedNames$package.given_Z_T().[T]*/]: Z/*->givens::Z#*/[T/*->givens::InventedNames$package.given_Z_T#[T]*/] with def doZ/*<-givens::InventedNames$package.given_Z_T#doZ().*/: List/*->scala::package.List#*/[T/*->givens::InventedNames$package.given_Z_T#[T]*/] = Nil/*->scala::package.Nil.*/ diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index c367bf48e8d4..61109db50024 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -484,7 +484,7 @@ classes/C12#foo2Impl().(context) => param context: Context classes/C12#foo2Impl().(x) => param x: context.Expr[Int] classes/C12#foo2Impl().(y) => param y: context.Expr[String] classes/M. => final object M extends Object { self: M.type => +3 decls } -classes/M.C5# => class C5 extends Object { self: C5 => +2 decls } +classes/M.C5# => implicit class C5 extends Object { self: C5 => +2 decls } classes/M.C5#``(). => primary ctor (param x: Int): C5 classes/M.C5#``().(x) => param x: Int classes/M.C5#x. => private[this] val method x Int @@ -1519,7 +1519,7 @@ example/ImplicitConversion#string2Number().(string) => param string: String example/ImplicitConversion#tuple. => val method tuple Tuple2[Int, Int] example/ImplicitConversion#x. => val method x Int example/ImplicitConversion. => final object ImplicitConversion extends Object { self: ImplicitConversion.type => +6 decls } -example/ImplicitConversion.newAny2stringadd# => final class newAny2stringadd [typeparam A ] extends AnyVal { self: newAny2stringadd[A] => +4 decls } +example/ImplicitConversion.newAny2stringadd# => final implicit class newAny2stringadd [typeparam A ] extends AnyVal { self: newAny2stringadd[A] => +4 decls } example/ImplicitConversion.newAny2stringadd#[A] => typeparam A example/ImplicitConversion.newAny2stringadd#`+`(). => method + (param other: String): String example/ImplicitConversion.newAny2stringadd#`+`().(other) => param other: String @@ -1684,7 +1684,7 @@ Uri => InventedNames.scala Text => empty Language => Scala Symbols => 45 entries -Occurrences => 61 entries +Occurrences => 62 entries Symbols: givens/InventedNames$package. => final package object givens extends Object { self: givens.type => +24 decls } @@ -1705,14 +1705,14 @@ givens/InventedNames$package.given_List_T().[T] => typeparam T givens/InventedNames$package.given_String. => final implicit lazy val given method given_String String givens/InventedNames$package.given_X. => final implicit given object given_X extends Object with X { self: given_X.type => +2 decls } givens/InventedNames$package.given_X.doX(). => method doX => Int <: givens/X#doX(). -givens/InventedNames$package.given_Y# => class given_Y extends Object with Y { self: given_Y => +3 decls } +givens/InventedNames$package.given_Y# => implicit given class given_Y extends Object with Y { self: given_Y => +3 decls } givens/InventedNames$package.given_Y#``(). => primary ctor ()(implicit val given param x$1: X): given_Y givens/InventedNames$package.given_Y#``().(x$1) => implicit val given param x$1: X givens/InventedNames$package.given_Y#doY(). => method doY => String <: givens/Y#doY(). givens/InventedNames$package.given_Y#x$1. => protected implicit val given method x$1 X givens/InventedNames$package.given_Y(). => final implicit given method given_Y (implicit given param x$1: X): given_Y givens/InventedNames$package.given_Y().(x$1) => implicit given param x$1: X -givens/InventedNames$package.given_Z_T# => class given_Z_T [typeparam T ] extends Object with Z[T] { self: given_Z_T[T] => +3 decls } +givens/InventedNames$package.given_Z_T# => implicit given class given_Z_T [typeparam T ] extends Object with Z[T] { self: given_Z_T[T] => +3 decls } givens/InventedNames$package.given_Z_T#[T] => typeparam T givens/InventedNames$package.given_Z_T#``(). => primary ctor [typeparam T ](): given_Z_T[T] givens/InventedNames$package.given_Z_T#doZ(). => method doZ => List[T] <: givens/Z#doZ(). @@ -1767,6 +1767,7 @@ Occurrences: [24:17..24:18): Y -> givens/Y# [25:6..25:9): doY <- givens/InventedNames$package.given_Y#doY(). [27:7..27:8): T <- givens/InventedNames$package.given_Z_T#[T] +[27:7..27:8): T <- givens/InventedNames$package.given_Z_T().[T] [27:11..27:12): Z -> givens/Z# [27:13..27:14): T -> givens/InventedNames$package.given_Z_T#[T] [28:6..28:9): doZ <- givens/InventedNames$package.given_Z_T#doZ(). From ec3a21a0065799d4d9d2eb11452330ff5ee556ae Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 23 Aug 2021 13:44:25 +0200 Subject: [PATCH 09/10] semanticdb - adjust occurences don't add definition occurences for given instance parameters --- .../tools/dotc/semanticdb/ExtractSemanticDB.scala | 1 + .../src/dotty/tools/dotc/transform/SymUtils.scala | 11 +++++++++++ tests/semanticdb/expect/InventedNames.expect.scala | 4 ++-- tests/semanticdb/expect/InventedNames.scala | 2 +- tests/semanticdb/metac.expect | 3 +-- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 9ac860a33112..aa1d7b102fb5 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -80,6 +80,7 @@ class ExtractSemanticDB extends Phase: || sym.is(Synthetic) || sym.isSetter || sym.isOldStyleImplicitConversion(forImplicitClassOnly = true) + || sym.owner.isGivenInstanceSummoner || excludeDefOrUse(sym) private def excludeDefOrUse(sym: Symbol)(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 7991cd752acf..30906e9b1356 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -100,6 +100,17 @@ object SymUtils: case _ => false + 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? diff --git a/tests/semanticdb/expect/InventedNames.expect.scala b/tests/semanticdb/expect/InventedNames.expect.scala index 6f0ae238ce7b..7c5b008209c2 100644 --- a/tests/semanticdb/expect/InventedNames.expect.scala +++ b/tests/semanticdb/expect/InventedNames.expect.scala @@ -25,7 +25,7 @@ given X/*->givens::X#*/ with given (using X/*->givens::X#*/): Y/*->givens::Y#*/ with def doY/*<-givens::InventedNames$package.given_Y#doY().*/ = "7" -given [T/*<-givens::InventedNames$package.given_Z_T#[T]*//*<-givens::InventedNames$package.given_Z_T().[T]*/]: Z/*->givens::Z#*/[T/*->givens::InventedNames$package.given_Z_T#[T]*/] with +given [T/*<-givens::InventedNames$package.given_Z_T#[T]*/]: Z/*->givens::Z#*/[T/*->givens::InventedNames$package.given_Z_T#[T]*/] with def doZ/*<-givens::InventedNames$package.given_Z_T#doZ().*/: List/*->scala::package.List#*/[T/*->givens::InventedNames$package.given_Z_T#[T]*/] = Nil/*->scala::package.Nil.*/ @@ -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#*/] \ No newline at end of file +val z/*<-givens::InventedNames$package.z.*/ = given_Z_T/*->givens::InventedNames$package.given_Z_T().*/[String/*->scala::Predef.String#*/] diff --git a/tests/semanticdb/expect/InventedNames.scala b/tests/semanticdb/expect/InventedNames.scala index 3858f22c8331..42c14c90e370 100644 --- a/tests/semanticdb/expect/InventedNames.scala +++ b/tests/semanticdb/expect/InventedNames.scala @@ -39,4 +39,4 @@ val f = given_Float val g = `* *` val x = given_X val y = given_Y -val z = given_Z_T[String] \ No newline at end of file +val z = given_Z_T[String] diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 61109db50024..d66bf1dc17dd 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1684,7 +1684,7 @@ Uri => InventedNames.scala Text => empty Language => Scala Symbols => 45 entries -Occurrences => 62 entries +Occurrences => 61 entries Symbols: givens/InventedNames$package. => final package object givens extends Object { self: givens.type => +24 decls } @@ -1767,7 +1767,6 @@ Occurrences: [24:17..24:18): Y -> givens/Y# [25:6..25:9): doY <- givens/InventedNames$package.given_Y#doY(). [27:7..27:8): T <- givens/InventedNames$package.given_Z_T#[T] -[27:7..27:8): T <- givens/InventedNames$package.given_Z_T().[T] [27:11..27:12): Z -> givens/Z# [27:13..27:14): T -> givens/InventedNames$package.given_Z_T#[T] [28:6..28:9): doZ <- givens/InventedNames$package.given_Z_T#doZ(). From 733200e7c75636da6069f09ae27f76245d0b2d25 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 26 Aug 2021 15:46:17 +0200 Subject: [PATCH 10/10] add more semanticdb testing infrastructure --- .../dotty/tools/dotc/semanticdb/Tools.scala | 5 ++++ .../dotty/tools/dotc/transform/SymUtils.scala | 3 ++- .../dotc/semanticdb/SemanticdbTests.scala | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala b/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala index a7bfdcefc047..ec427523195e 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 30906e9b1356..d4eaed2b837a 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -87,7 +87,7 @@ object SymUtils: def isGenericProduct(using Context): Boolean = whyNotGenericProduct.isEmpty - /** Is this the an old style implicit conversion? + /** Is this an old style implicit conversion? * @param directOnly only consider explicitly written methods * @param forImplicitClassOnly only consider methods generated from implicit classes */ @@ -100,6 +100,7 @@ object SymUtils: 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 => diff --git a/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala b/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala index 5deeaed114a8..d1f58bf52a91 100644 --- a/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala +++ b/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala @@ -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")