Skip to content

Fix #7810: Survive wildcards when summoning a ValueOf #7838

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

Merged
merged 14 commits into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -723,10 +723,7 @@ object desugar {
else if (companionMembers.nonEmpty || companionDerived.nonEmpty || isEnum)
companionDefs(anyRef, companionMembers)
else if (isValueClass)
impl.constr.vparamss match {
case (_ :: Nil) :: _ => companionDefs(anyRef, Nil)
case _ => Nil // error will be emitted in typer
}
companionDefs(anyRef, Nil)
else Nil

enumCompanionRef match {
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
case tp: SingletonType if tp.isStable => tp
case tp: ValueType => SkolemType(tp)
case tp: TypeProxy => ensureStableSingleton(tp.underlying)
case tp => assert(ctx.reporter.errorsReported); SkolemType(tp)
}

/** Skip refinements in `tp2` which match corresponding refinements in `tp1`.
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4599,7 +4599,7 @@ object Types {

case tp: AppliedType =>
def mapArgs(args: List[Type], tparams: List[ParamInfo]): List[Type] = args match {
case arg :: otherArgs =>
case arg :: otherArgs if tparams.nonEmpty =>
val arg1 = arg match {
case arg: TypeBounds => this(arg)
case arg => atVariance(variance * tparams.head.paramVariance)(this(arg))
Expand Down
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ object Erasure {
tree.symbol.getAnnotation(defn.CompileTimeOnlyAnnot) match {
case Some(annot) =>
def defaultMsg =
s"""Reference to ${tree.symbol.showLocated} should not have survived,
i"""Reference to ${tree.symbol.showLocated} should not have survived,
|it should have been processed and eliminated during expansion of an enclosing macro or term erasure."""
val message = annot.argumentConstant(0).fold(defaultMsg)(_.stringValue)
ctx.error(message, tree.sourcePos)
Expand Down Expand Up @@ -750,8 +750,11 @@ object Erasure {

override def adapt(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree =
trace(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) {
assert(ctx.phase == ctx.erasurePhase || ctx.phase == ctx.erasurePhase.next, ctx.phase)
if (tree.isEmpty) tree
if ctx.phase != ctx.erasurePhase && ctx.phase != ctx.erasurePhase.next then
// this can happen when reading annotations loaded during erasure,
// since these are loaded at phase typer.
adapt(tree, pt, locked)(given ctx.withPhase(ctx.erasurePhase.next))
else if (tree.isEmpty) tree
else if (ctx.mode is Mode.Pattern) tree // TODO: replace with assertion once pattern matcher is active
else adaptToType(tree, pt)
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,10 @@ trait Applications extends Compatibility {

/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
throw new Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
if ctx.reporter.errorsReported then
throw TypeError(i"unexpected function type: ${methPart(fun).tpe}")
else
throw Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")

def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context): List[NamedArg] =
for (arg @ NamedArg(id, argtpt) <- args) yield {
Expand Down
25 changes: 12 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ trait Implicits { self: Typer =>
(formal, span) => implicit ctx => {
def success(t: Tree) = New(defn.ValueOfClass.typeRef.appliedTo(t.tpe), t :: Nil).withSpan(span)

formal.argTypes match {
formal.argInfos match {
case arg :: Nil =>
fullyDefinedType(arg.dealias, "ValueOf argument", span) match {
case ConstantType(c: Constant) =>
Expand Down Expand Up @@ -1401,18 +1401,17 @@ trait Implicits { self: Typer =>
untpd.Apply(untpdConv, untpd.TypedSplice(argument) :: Nil),
pt, locked)
}
if (cand.isExtension) {
val SelectionProto(name: TermName, mbrType, _, _) = pt
val result = extMethodApply(untpd.Select(untpdGenerated, name), argument, mbrType)
if (!ctx.reporter.hasErrors && cand.isConversion) {
val testCtx = ctx.fresh.setExploreTyperState()
tryConversion(testCtx)
if (testCtx.reporter.hasErrors)
ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container")
}
result
}
else tryConversion
pt match
case SelectionProto(name: TermName, mbrType, _, _) if cand.isExtension =>
val result = extMethodApply(untpd.Select(untpdGenerated, name), argument, mbrType)
if !ctx.reporter.hasErrors && cand.isConversion then
val testCtx = ctx.fresh.setExploreTyperState()
tryConversion(testCtx)
if testCtx.reporter.hasErrors then
ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container")
result
case _ =>
tryConversion
}
if (ctx.reporter.hasErrors) {
ctx.reporter.removeBufferedMessages
Expand Down
29 changes: 21 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,10 @@ class Typer extends Namer
def caseRest(implicit ctx: Context) = {
val pat1 = checkSimpleKinded(typedType(cdef.pat)(ctx.addMode(Mode.Pattern)))
val pat2 = indexPattern(cdef).transform(pat1)
val body1 = typedType(cdef.body, pt)
var body1 = typedType(cdef.body, pt)
if !body1.isType then
assert(ctx.reporter.errorsReported)
body1 = TypeTree(errorType("<error: not a type>", cdef.sourcePos))
assignType(cpy.CaseDef(cdef)(pat2, EmptyTree, body1), pat2, body1)
}
caseRest(ctx.fresh.setFreshGADTBounds.setNewScope)
Expand Down Expand Up @@ -1546,7 +1549,13 @@ class Typer extends Namer

def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = {
val nestedCtx = ctx.addMode(Mode.InPatternAlternative)
def ensureValueTypeOrWildcard(tree: Tree) =
if tree.tpe.isValueTypeOrWildcard then tree
else
assert(ctx.reporter.errorsReported)
tree.withType(defn.AnyType)
val trees1 = tree.trees.mapconserve(typed(_, pt)(nestedCtx))
.mapconserve(ensureValueTypeOrWildcard)
assignType(cpy.Alternative(tree)(trees1), trees1)
}

Expand Down Expand Up @@ -1922,13 +1931,17 @@ class Typer extends Namer
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef)
val arg1 = typed(tree.arg, pt)
if (ctx.mode is Mode.Type) {
val result = assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1)
result.tpe match {
case AnnotatedType(rhs, Annotation.WithBounds(bounds)) =>
if (!bounds.contains(rhs)) ctx.error(em"type $rhs outside bounds $bounds", tree.sourcePos)
case _ =>
}
result
if arg1.isType then
val result = assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1)
result.tpe match {
case AnnotatedType(rhs, Annotation.WithBounds(bounds)) =>
if (!bounds.contains(rhs)) ctx.error(em"type $rhs outside bounds $bounds", tree.sourcePos)
case _ =>
}
result
else
assert(ctx.reporter.errorsReported)
TypeTree(UnspecifiedErrorType)
}
else {
val arg2 = arg1 match {
Expand Down
155 changes: 0 additions & 155 deletions docs/docs/reference/contextual/extension-methods-new.md

This file was deleted.

1 change: 1 addition & 0 deletions tests/neg/i7810.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val a = implicitly[ValueOf[_]] // error
8 changes: 8 additions & 0 deletions tests/neg/i7811.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait A {
type Type[X,] // error
def a[X]: Type[X,] // error
}
class B extends A {
type Type[X]
var a = 1
}
3 changes: 3 additions & 0 deletions tests/neg/i7812.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def f(): Any = ???
var f: (UndefinedA & UndefinedB) { val x: Int } = ??? // error // error
val a = f // error
3 changes: 3 additions & 0 deletions tests/neg/i7813.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
implicit def f[X <: Undefined](implicit a: Int): X = ??? // error
def g(arg: h.NonExistent): Int = ???
val h: Int = ???
1 change: 1 addition & 0 deletions tests/neg/i7814.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
def i0 = Unit // error
3 changes: 3 additions & 0 deletions tests/neg/i7815.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trait A {
val a: Int match { case Int => this } // error
}
4 changes: 4 additions & 0 deletions tests/neg/i7816.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
object A {
def f()(>) = ??? // error
import f.NonExistent // error
}
8 changes: 8 additions & 0 deletions tests/neg/i7817.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait Foo[A]
class A {
def F[x : Foo]() = ???

Int match {
case Int | F => () // error
}
}
1 change: 1 addition & 0 deletions tests/neg/i7818.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
def foo = (x: @) => () // error
1 change: 1 addition & 0 deletions tests/pending/neg/i7820.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
object A { type F[X >: F, Y] }
3 changes: 3 additions & 0 deletions tests/pending/pos/i7745.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trait F[x]
implicit def foo[f[_], y, x <: f[y]](implicit ev: F[y]): F[x] = ???
val test = implicitly
5 changes: 5 additions & 0 deletions tests/pending/pos/i7778.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Example extends App {

final case class Foo[A](run: (given A) => Int)

}
Loading