Skip to content

Commit b4f8eef

Browse files
Implement into modifier on parameter types (#14514)
in the thread https://contributors.scala-lang.org/t/proposed-changes-and-restrictions-for-implicit-conversions/4923 we discussed two changes that would make implicit conversions largely redundant. One proposed change was implemented in #14497. The other is implemented here. It's based on #14497. The idea of this PR is to have some way to specify that a method parameter accepts implicit conversions on its arguments. Then the feature warning on implicit conversion use would be suppressed in this case. In the previous thread I proposed `~` to mark convertible arguments but I now feel this is too cryptic. Instead there is a `into` prefix in the parameter type. E.g. ```scala def f(x: into T) = ... ``` For Scala 2, we introduce an annotation on the parameter that expresses the same thing: ```scala // proposed Scala-2 syntax: def f(@allowConversions x: T) = ``` A larger example: ```scala given Conversion[String, Text] = Text(_) @main def Test = def f(x: into Text, y: => into Text, zs: into Text*) = println(s"${x.str} ${y.str} ${zs.map(_.str).mkString(" ")}") f("abc", "def") // ok, no feature warning f("abc", "def", "xyz", "uvw") // also ok f("abc", "def", "xyz", Text("uvw")) // also ok ``` A larger example is a parser combinator library that works without unrestricted implicit conversions: [tests/run/Parser.scala](https://github.com/lampepfl/dotty/blob/0f113da1bacc01603740cde4a5bafbfae39895f0/tests/run/Parser.scala)
2 parents 4af64cd + 015f5cb commit b4f8eef

35 files changed

+390
-59
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import core._
66
import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
77
import Symbols._, StdNames._, Trees._, ContextOps._
88
import Decorators._, transform.SymUtils._
9+
import Annotations.Annotation
910
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName, WildcardParamName}
1011
import typer.{Namer, Checking}
1112
import util.{Property, SourceFile, SourcePosition, Chars}
@@ -165,32 +166,41 @@ object desugar {
165166
*
166167
* Generate setter where needed
167168
*/
168-
def valDef(vdef0: ValDef)(using Context): Tree = {
169+
def valDef(vdef0: ValDef)(using Context): Tree =
169170
val vdef @ ValDef(_, tpt, rhs) = vdef0
170-
val mods = vdef.mods
171-
172171
val valName = normalizeName(vdef, tpt).asTermName
173-
val vdef1 = cpy.ValDef(vdef)(name = valName)
172+
var mods1 = vdef.mods
173+
174+
def dropInto(tpt: Tree): Tree = tpt match
175+
case Into(tpt1) =>
176+
mods1 = vdef.mods.withAddedAnnotation(
177+
TypedSplice(
178+
Annotation(defn.AllowConversionsAnnot).tree.withSpan(tpt.span.startPos)))
179+
tpt1
180+
case ByNameTypeTree(tpt1) =>
181+
cpy.ByNameTypeTree(tpt)(dropInto(tpt1))
182+
case PostfixOp(tpt1, op) if op.name == tpnme.raw.STAR =>
183+
cpy.PostfixOp(tpt)(dropInto(tpt1), op)
184+
case _ =>
185+
tpt
186+
187+
val vdef1 = cpy.ValDef(vdef)(name = valName, tpt = dropInto(tpt))
188+
.withMods(mods1)
174189

175-
if (isSetterNeeded(vdef)) {
176-
// TODO: copy of vdef as getter needed?
177-
// val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos?
178-
// right now vdef maps via expandedTree to a thicket which concerns itself.
179-
// I don't see a problem with that but if there is one we can avoid it by making a copy here.
190+
if isSetterNeeded(vdef) then
180191
val setterParam = makeSyntheticParameter(tpt = SetterParamTree().watching(vdef))
181192
// The rhs gets filled in later, when field is generated and getter has parameters (see Memoize miniphase)
182193
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
183194
val setter = cpy.DefDef(vdef)(
184-
name = valName.setterName,
185-
paramss = (setterParam :: Nil) :: Nil,
186-
tpt = TypeTree(defn.UnitType),
187-
rhs = setterRhs
188-
).withMods((mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
189-
.dropEndMarker() // the end marker should only appear on the getter definition
195+
name = valName.setterName,
196+
paramss = (setterParam :: Nil) :: Nil,
197+
tpt = TypeTree(defn.UnitType),
198+
rhs = setterRhs
199+
).withMods((vdef.mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
200+
.dropEndMarker() // the end marker should only appear on the getter definition
190201
Thicket(vdef1, setter)
191-
}
192202
else vdef1
193-
}
203+
end valDef
194204

195205
def makeImplicitParameters(tpts: List[Tree], implicitFlag: FlagSet, forPrimaryConstructor: Boolean = false)(using Context): List[ValDef] =
196206
for (tpt <- tpts) yield {

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
117117
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
118118
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
119119
case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(implicit @constructorOnly src: SourceFile) extends Tree
120+
case class Into(tpt: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
120121
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
121122

122123
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
@@ -649,6 +650,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
649650
def ExtMethods(tree: Tree)(paramss: List[ParamClause], methods: List[Tree])(using Context): Tree = tree match
650651
case tree: ExtMethods if (paramss eq tree.paramss) && (methods == tree.methods) => tree
651652
case _ => finalize(tree, untpd.ExtMethods(paramss, methods)(tree.source))
653+
def Into(tree: Tree)(tpt: Tree)(using Context): Tree = tree match
654+
case tree: Into if tpt eq tree.tpt => tree
655+
case _ => finalize(tree, untpd.Into(tpt)(tree.source))
652656
def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(using Context): Tree = tree match {
653657
case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree
654658
case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source))
@@ -718,6 +722,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
718722
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
719723
case ExtMethods(paramss, methods) =>
720724
cpy.ExtMethods(tree)(transformParamss(paramss), transformSub(methods))
725+
case Into(tpt) =>
726+
cpy.Into(tree)(transform(tpt))
721727
case ImportSelector(imported, renamed, bound) =>
722728
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
723729
case Number(_, _) | TypedSplice(_) =>
@@ -777,6 +783,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
777783
this(this(this(x, pats), tpt), rhs)
778784
case ExtMethods(paramss, methods) =>
779785
this(paramss.foldLeft(x)(apply), methods)
786+
case Into(tpt) =>
787+
this(x, tpt)
780788
case ImportSelector(imported, renamed, bound) =>
781789
this(this(this(x, imported), renamed), bound)
782790
case Number(_, _) =>

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ object Feature:
3030
val saferExceptions = experimental("saferExceptions")
3131
val pureFunctions = experimental("pureFunctions")
3232
val captureChecking = experimental("captureChecking")
33+
val into = experimental("into")
3334

3435
val globalOnlyImports: Set[TermName] = Set(pureFunctions, captureChecking)
3536

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ object Decorators {
7878
/** Implements filterConserve, zipWithConserve methods
7979
* on lists that avoid duplication of list nodes where feasible.
8080
*/
81-
implicit class ListDecorator[T](val xs: List[T]) extends AnyVal {
81+
extension [T](xs: List[T])
8282

8383
final def mapconserve[U](f: T => U): List[U] = {
8484
@tailrec
@@ -207,11 +207,7 @@ object Decorators {
207207
}
208208

209209
/** Union on lists seen as sets */
210-
def | (ys: List[T]): List[T] = xs ::: (ys filterNot (xs contains _))
211-
212-
/** Intersection on lists seen as sets */
213-
def & (ys: List[T]): List[T] = xs filter (ys contains _)
214-
}
210+
def setUnion (ys: List[T]): List[T] = xs ::: ys.filterNot(xs contains _)
215211

216212
extension [T, U](xss: List[List[T]])
217213
def nestedMap(f: T => U): List[List[U]] = xss match

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ class Definitions {
644644

645645
@tu lazy val RepeatedParamClass: ClassSymbol = enterSpecialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType))
646646

647+
@tu lazy val IntoType: TypeSymbol = enterAliasType(tpnme.INTO, HKTypeLambda(TypeBounds.empty :: Nil)(_.paramRefs(0)))
648+
647649
// fundamental classes
648650
@tu lazy val StringClass: ClassSymbol = requiredClass("java.lang.String")
649651
def StringType: Type = StringClass.typeRef
@@ -973,6 +975,7 @@ class Definitions {
973975
@tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation")
974976

975977
// Annotation classes
978+
@tu lazy val AllowConversionsAnnot: ClassSymbol = requiredClass("scala.annotation.allowConversions")
976979
@tu lazy val AnnotationDefaultAnnot: ClassSymbol = requiredClass("scala.annotation.internal.AnnotationDefault")
977980
@tu lazy val BeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BeanProperty")
978981
@tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty")
@@ -2005,6 +2008,7 @@ class Definitions {
20052008
orType,
20062009
RepeatedParamClass,
20072010
ByNameParamClass2x,
2011+
IntoType,
20082012
AnyValClass,
20092013
NullClass,
20102014
NothingClass,

compiler/src/dotty/tools/dotc/core/Names.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import scala.annotation.internal.sharable
1515
object Names {
1616
import NameKinds._
1717

18-
/** Things that can be turned into names with `totermName` and `toTypeName`
19-
* Decorators defines implements these as extension methods for strings.
18+
/** Things that can be turned into names with `toTermName` and `toTypeName`.
19+
* Decorators implements these as extension methods for strings.
2020
*/
2121
type PreName = Name | String
2222

compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
634634
case param: TypeParamRef if contains(param) =>
635635
param :: (if (isUpper) upper(param) else lower(param))
636636
case tp: AndType if isUpper =>
637-
dependentParams(tp.tp1, isUpper) | (dependentParams(tp.tp2, isUpper))
637+
dependentParams(tp.tp1, isUpper).setUnion(dependentParams(tp.tp2, isUpper))
638638
case tp: OrType if !isUpper =>
639639
dependentParams(tp.tp1, isUpper).intersect(dependentParams(tp.tp2, isUpper))
640640
case EtaExpansion(tycon) =>

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ object StdNames {
128128
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
129129
val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR
130130
val IMPORT: N = "<import>"
131+
val INTO: N = "<into>"
131132
val MODULE_SUFFIX: N = str.MODULE_SUFFIX
132133
val OPS_PACKAGE: N = "<special-ops>"
133134
val OVERLOADED: N = "<overloaded>"
@@ -500,6 +501,7 @@ object StdNames {
500501
val info: N = "info"
501502
val inlinedEquals: N = "inlinedEquals"
502503
val internal: N = "internal"
504+
val into: N = "into"
503505
val isArray: N = "isArray"
504506
val isDefinedAt: N = "isDefinedAt"
505507
val isDefinedAtImpl: N = "$isDefinedAt"

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,10 @@ object Types {
397397
def isRepeatedParam(using Context): Boolean =
398398
typeSymbol eq defn.RepeatedParamClass
399399

400+
/** Is this a parameter type that allows implicit argument converson? */
401+
def isConvertibleParam(using Context): Boolean =
402+
typeSymbol eq defn.IntoType
403+
400404
/** Is this the type of a method that has a repeated parameter type as
401405
* last parameter type?
402406
*/
@@ -536,7 +540,7 @@ object Types {
536540
case tp: ClassInfo =>
537541
tp.cls :: Nil
538542
case AndType(l, r) =>
539-
l.parentSymbols(include) | r.parentSymbols(include)
543+
l.parentSymbols(include).setUnion(r.parentSymbols(include))
540544
case OrType(l, r) =>
541545
l.parentSymbols(include) intersect r.parentSymbols(include) // TODO does not conform to spec
542546
case _ =>
@@ -1864,6 +1868,11 @@ object Types {
18641868

18651869
def dropRepeatedAnnot(using Context): Type = dropAnnot(defn.RepeatedAnnot)
18661870

1871+
/** A translation from types of original parameter ValDefs to the types
1872+
* of parameters in MethodTypes.
1873+
* Translates `Seq[T] @repeated` or `Array[T] @repeated` to `<repeated>[T]`.
1874+
* That way, repeated arguments are made manifest without risk of dropped annotations.
1875+
*/
18671876
def annotatedToRepeated(using Context): Type = this match {
18681877
case tp @ ExprType(tp1) =>
18691878
tp.derivedExprType(tp1.annotatedToRepeated)
@@ -3948,27 +3957,48 @@ object Types {
39483957
* and inline parameters:
39493958
* - replace @repeated annotations on Seq or Array types by <repeated> types
39503959
* - add @inlineParam to inline parameters
3960+
* - add @erasedParam to erased parameters
3961+
* - wrap types of parameters that have an @allowConversions annotation with Into[_]
39513962
*/
3952-
def fromSymbols(params: List[Symbol], resultType: Type)(using Context): MethodType = {
3953-
def translateInline(tp: Type): Type = tp match {
3954-
case ExprType(resType) => ExprType(AnnotatedType(resType, Annotation(defn.InlineParamAnnot)))
3955-
case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot))
3956-
}
3957-
def translateErased(tp: Type): Type = tp match {
3958-
case ExprType(resType) => ExprType(AnnotatedType(resType, Annotation(defn.ErasedParamAnnot)))
3959-
case _ => AnnotatedType(tp, Annotation(defn.ErasedParamAnnot))
3960-
}
3961-
def paramInfo(param: Symbol) = {
3963+
def fromSymbols(params: List[Symbol], resultType: Type)(using Context): MethodType =
3964+
def addAnnotation(tp: Type, cls: ClassSymbol): Type = tp match
3965+
case ExprType(resType) => ExprType(addAnnotation(resType, cls))
3966+
case _ => AnnotatedType(tp, Annotation(cls))
3967+
3968+
def wrapConvertible(tp: Type) =
3969+
AppliedType(defn.IntoType.typeRef, tp :: Nil)
3970+
3971+
/** Add `Into[..] to the type itself and if it is a function type, to all its
3972+
* curried result type(s) as well.
3973+
*/
3974+
def addInto(tp: Type): Type = tp match
3975+
case tp @ AppliedType(tycon, args) if tycon.typeSymbol == defn.RepeatedParamClass =>
3976+
tp.derivedAppliedType(tycon, addInto(args.head) :: Nil)
3977+
case tp @ AppliedType(tycon, args) if defn.isFunctionType(tp) =>
3978+
wrapConvertible(tp.derivedAppliedType(tycon, args.init :+ addInto(args.last)))
3979+
case tp @ RefinedType(parent, rname, rinfo) if defn.isFunctionOrPolyType(tp) =>
3980+
wrapConvertible(tp.derivedRefinedType(parent, rname, addInto(rinfo)))
3981+
case tp: MethodOrPoly =>
3982+
tp.derivedLambdaType(resType = addInto(tp.resType))
3983+
case ExprType(resType) =>
3984+
ExprType(addInto(resType))
3985+
case _ =>
3986+
wrapConvertible(tp)
3987+
3988+
def paramInfo(param: Symbol) =
39623989
var paramType = param.info.annotatedToRepeated
3963-
if (param.is(Inline)) paramType = translateInline(paramType)
3964-
if (param.is(Erased)) paramType = translateErased(paramType)
3990+
if param.is(Inline) then
3991+
paramType = addAnnotation(paramType, defn.InlineParamAnnot)
3992+
if param.is(Erased) then
3993+
paramType = addAnnotation(paramType, defn.ErasedParamAnnot)
3994+
if param.hasAnnotation(defn.AllowConversionsAnnot) then
3995+
paramType = addInto(paramType)
39653996
paramType
3966-
}
39673997

39683998
apply(params.map(_.name.asTermName))(
39693999
tl => params.map(p => tl.integrate(params, paramInfo(p))),
39704000
tl => tl.integrate(params, resultType))
3971-
}
4001+
end fromSymbols
39724002

39734003
final def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(using Context): MethodType =
39744004
checkValid(unique(new CachedMethodType(paramNames)(paramInfosExp, resultTypeExp, self)))

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,13 @@ object Parsers {
19171917
else
19181918
core()
19191919

1920+
private def maybeInto(tp: () => Tree) =
1921+
if in.isIdent(nme.into)
1922+
&& in.featureEnabled(Feature.into)
1923+
&& canStartTypeTokens.contains(in.lookahead.token)
1924+
then atSpan(in.skipToken()) { Into(tp()) }
1925+
else tp()
1926+
19201927
/** FunArgType ::= Type
19211928
* | `=>' Type
19221929
* | [CaptureSet] `->' Type
@@ -1929,10 +1936,10 @@ object Parsers {
19291936
*/
19301937
def paramType(): Tree = paramTypeOf(paramValueType)
19311938

1932-
/** ParamValueType ::= Type [`*']
1939+
/** ParamValueType ::= [`into`] Type [`*']
19331940
*/
19341941
def paramValueType(): Tree = {
1935-
val t = toplevelTyp()
1942+
val t = maybeInto(toplevelTyp)
19361943
if (isIdent(nme.raw.STAR)) {
19371944
in.nextToken()
19381945
atSpan(startOffset(t)) { PostfixOp(t, Ident(tpnme.raw.STAR)) }

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ object Tokens extends TokensCommon {
231231
final val canStartInfixTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet(
232232
THIS, SUPER, USCORE, LPAREN, LBRACE, AT)
233233

234+
final val canStartTypeTokens: TokenSet = canStartInfixTypeTokens | BitSet(LBRACE)
235+
234236
final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)
235237

236238
final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, GIVEN)
@@ -287,7 +289,7 @@ object Tokens extends TokensCommon {
287289

288290
final val closingParens = BitSet(RPAREN, RBRACKET, RBRACE)
289291

290-
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent, nme.infix)
292+
final val softModifierNames = Set(nme.inline, nme.into, nme.opaque, nme.open, nme.transparent, nme.infix)
291293

292294
def showTokenDetailed(token: Int): String = debugString(token)
293295

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
223223
case _ =>
224224
val tsym = tycon.typeSymbol
225225
if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*"
226+
else if tp.isConvertibleParam then "into " ~ toText(args.head)
226227
else if defn.isFunctionSymbol(tsym) then
227228
toTextFunction(args, tsym.name.isContextFunction, tsym.name.isErasedFunction,
228229
isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction)

compiler/src/dotty/tools/dotc/transform/TypeUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ object TypeUtils {
7676
case AndType(tp1, tp2) =>
7777
// We assume that we have the following property:
7878
// (T1, T2, ..., Tn) & (U1, U2, ..., Un) = (T1 & U1, T2 & U2, ..., Tn & Un)
79-
tp1.tupleElementTypes.zip(tp2.tupleElementTypes).map { case (t1, t2) => t1 & t2 }
79+
tp1.tupleElementTypes.zip(tp2.tupleElementTypes).map { case (t1, t2) => t1.intersect(t2) }
8080
case OrType(tp1, tp2) =>
8181
None // We can't combine the type of two tuples
8282
case _ =>

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -979,14 +979,15 @@ trait Checking {
979979
sym.srcPos)
980980

981981
/** If `tree` is an application of a new-style implicit conversion (using the apply
982-
* method of a `scala.Conversion` instance), check that implicit conversions are
983-
* enabled.
982+
* method of a `scala.Conversion` instance), check that the expected type is
983+
* a convertible formal parameter type or that implicit conversions are enabled.
984984
*/
985-
def checkImplicitConversionUseOK(tree: Tree)(using Context): Unit =
985+
def checkImplicitConversionUseOK(tree: Tree, expected: Type)(using Context): Unit =
986986
val sym = tree.symbol
987987
if sym.name == nme.apply
988988
&& sym.owner.derivesFrom(defn.ConversionClass)
989989
&& !sym.info.isErroneous
990+
&& !expected.isConvertibleParam
990991
then
991992
def conv = methPart(tree) match
992993
case Select(qual, _) => qual.symbol.orElse(sym.owner)
@@ -1536,7 +1537,7 @@ trait NoChecking extends ReChecking {
15361537
override def checkStable(tp: Type, pos: SrcPos, kind: String)(using Context): Unit = ()
15371538
override def checkClassType(tp: Type, pos: SrcPos, traitReq: Boolean, stablePrefixReq: Boolean)(using Context): Type = tp
15381539
override def checkImplicitConversionDefOK(sym: Symbol)(using Context): Unit = ()
1539-
override def checkImplicitConversionUseOK(tree: Tree)(using Context): Unit = ()
1540+
override def checkImplicitConversionUseOK(tree: Tree, expected: Type)(using Context): Unit = ()
15401541
override def checkFeasibleParent(tp: Type, pos: SrcPos, where: => String = "")(using Context): Type = tp
15411542
override def checkAnnotArgs(tree: Tree)(using Context): tree.type = tree
15421543
override def checkNoTargetNameConflict(stats: List[Tree])(using Context): Unit = ()

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,12 +1618,14 @@ class Namer { typer: Typer =>
16181618
def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(using Context): tpd.Tree =
16191619
typedAhead(tree, typer.typedExpr(_, pt))
16201620

1621-
def typedAheadAnnotationClass(tree: Tree)(using Context): Symbol = tree match {
1621+
def typedAheadAnnotationClass(tree: Tree)(using Context): Symbol = tree match
16221622
case Apply(fn, _) => typedAheadAnnotationClass(fn)
16231623
case TypeApply(fn, _) => typedAheadAnnotationClass(fn)
16241624
case Select(qual, nme.CONSTRUCTOR) => typedAheadAnnotationClass(qual)
16251625
case New(tpt) => typedAheadType(tpt).tpe.classSymbol
1626-
}
1626+
case TypedSplice(_) =>
1627+
val sym = tree.symbol
1628+
if sym.isConstructor then sym.owner else sym
16271629

16281630
/** Enter and typecheck parameter list */
16291631
def completeParams(params: List[MemberDef])(using Context): Unit = {

0 commit comments

Comments
 (0)