Skip to content

Better resolution of implicits in patterns #13807

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

Closed
wants to merge 1 commit into from

Conversation

adampauls
Copy link
Contributor

@adampauls adampauls commented Oct 24, 2021

Attempts to address lampepfl/dotty-feature-requests#242. I'm still pretty new here, so apologies for any naive errors. At least one of the changes I made is probably not right. I'll leave comments in line. (cc @som-snytt ).

unapplyApp = typedExpr(untpd.TypedSplice(explicitCall))
unapplyApp.tpe
} { (_, _) =>
// Typing explicitCall may fail if there are implicits that can only
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the core of the change.

@@ -1373,6 +1397,7 @@ trait Applications extends Compatibility {
List.fill(argTypes.length - args.length)(WildcardType)
}
val unapplyPatterns = bunchedArgs.lazyZip(argTypes) map (typed(_, _))
if (unapplyApp == null) unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil)))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-infer types after type variables might have been further constrained.

@@ -834,6 +834,9 @@ class Typer extends Namer
val expr1 =
if isWildcard then tree.expr.withType(underlyingTreeTpe.tpe)
else typed(tree.expr, underlyingTreeTpe.tpe.widenSkolem)
if (ctx.mode.is(Mode.Pattern)) {
Copy link
Contributor Author

@adampauls adampauls Oct 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There must be something smarter here. Currently, pt seems to be nearly unused here, which seems wrong. This check has the side effect of e.g. constraining a type variable T in def unapply[T](...): Option[T] to be a subtype of a type ascription (for example, `case SomeUnapply(x: Foo)'). Needed to pass f242-3.scala. This is the thing I would like to do in a better way.

@@ -2019,7 +2022,7 @@ class Typer extends Namer
}

def typedBind(tree: untpd.Bind, pt: Type)(using Context): Tree = {
if !isFullyDefined(pt, ForceDegree.all) then
if !(ctx.mode.is(Mode.Pattern) || isFullyDefined(pt, ForceDegree.all)) then
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to pass f242-2.scala

@@ -2,9 +2,3 @@
5 | case '{ StringContext(${Varargs(parts)}*) } => // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| no implicit argument of type scala.quoted.Quotes was found
-- [E006] Not Found Error: tests/neg-macros/i6436.scala:6:34 -----------------------------------------------------------
6 | val ps: Seq[Expr[String]] = parts // error
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is an improvement?

@adampauls
Copy link
Contributor Author

cc @nicolasstucki since I think you last touched this code in #9003?

// Typing explicitCall may fail if there are implicits that can only
// be resolved with tighter bounds on the output types
// (e.g. def unapply[T](x: String)(using Foo[T]): Option[T])
// If the call above fails to type, we try again here but with wildcards
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having read through https://contributors.scala-lang.org/t/better-implicit-resolution-in-pattern-matches/5308 I sort of understand the usecase, but I'm not sure it's worth it given this kind of weird business with wildcards needed in the implementation. It seems that pattern typing is fundamentally top-down and this code attempts to work around that in some specific cases, but I can't really tell what the implications of that change is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for follow up. I agree that this implementation is not ideal, but I think I would disagree that pattern typing ought to be fundamentally top-down. I think it is right now, but I don't see any good reason for it. I almost filed the feature request as a bug report because it's not really clear to me whether the current behavior is the spec.

If pattern matching were specified as syntactic sugar over a series of called to unapply/flatMap, etc., I think we might find that the behavior specified in the "feature request" would fall out naturally, and this PR would be more of a (possibly hacky) bug fix. I would like to fix the possibly hacky part, but I'll need a little consultation on what would seem less hacky and I suppose agreement on whether the syntactic sugar view of pattern matching is correct. Maybe it's worth starting a separate thread on what that view might be? Or is there already a spec for the bespoke type inference currently implemented beyond the implementation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is there already a spec for the bespoke type inference currently implemented beyond the implementation?

The Scala 2 spec contains this section: https://www.scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#pattern-matching-expressions but I honestly have no idea how closely it matches either the Scala 2 or 3 implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That spec doesn't say anything about how to type a pattern, just how to type a pattern in the context of type variables (IIUC). And if anything, it sounds a lot like the implementation in this PR, except with undefined for type parameters instead of wildcards for implicits?

First, it is attempted to type [pattern] p with T as its expected type. If this fails, p is instead typed with a modified expected type T' which results from T by replacing every occurrence of a type parameter a_i by undefined.

That said, any view of pattern matching as syntactic sugar for a series of calls to flatMap/orElse/isInstanceOf that I could think of would not lead to the behavior that I implement in this PR, so I can't really argue this is a bug. I will close the PR for lack of interest in this new feature. Thanks for your time!

@adampauls adampauls closed this Nov 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants