Skip to content

Fix #2412: Local error causes spurious errors to be reported #2922

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 2 commits into from

Conversation

smarter
Copy link
Member

@smarter smarter commented Jul 26, 2017

No description provided.

@smarter smarter requested a review from odersky July 26, 2017 21:28
@smarter smarter force-pushed the fix-spurious-errors branch from 1fef282 to 66801cc Compare July 27, 2017 10:42
@smarter
Copy link
Member Author

smarter commented Aug 16, 2017

Ping for review.

Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

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

It would be good to motivate this in more detail, because I am not convinced yet. Logically, it seems correct to store the context in the prototype and evaluate arguments in this context. You compensate for supercall arguments by passing a special flag. But how do you know architecturally that you do not need other compensations as well?

/** Evalute `op` with the proper context to type the arguments. */
protected def withArgCtx[T](op: Context => T)(implicit ctx: Context) = {
if (isSelfConstrCall && !thisCallArgCtxUsed) {
// Context#thisCallArgContext is not idempotent so we must only use it
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use Mode.InSuperCall as an indicator whether we are already in the right context, instead of defining a special field for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes that should work, I'll give it a try.

@smarter
Copy link
Member Author

smarter commented Aug 16, 2017

It would be good to motivate this in more detail, because I am not convinced yet. Logically, it seems correct to store the context in the prototype and evaluate arguments in this context.

That's fine as long as there is exactly one way to type the arguments and no backtracking takes place, but there's plenty of backtracking in typedApply so using the stored context causes weird issues like reporting errors from branches we backtracked out of.

You compensate for supercall arguments by passing a special flag. But how do you know architecturally that you do not need other compensations as well?

Yes, it's a bit dicey, it relies on the fact that FunProto#typedArgs will always be called in a Context that is an inner Context from the one where we created the FunProto, so the arguments should still be typeable, and they will also have the correct TyperState/Reporter/... needed for backtracking to work correctly. Ideally I think we would have a design where we don't store untyped trees in prototypes to avoid this kind of things.

In several situations, instead of the common pattern in the compiler of
using the enclosing Context available coming an implicit method
parameter, we used some other Context. So far, this wasn't an issue but
starting with the next commit it is important for FunProto#typedArgs to
be called in a Context whose owner chain contains the Context where the
FunProto was created. This motivated the following changes:

- RunInfo does not actually need to keep a reference to the root Context:
  It was previously used by `ImplicitRunInfo#implicitScope`, but can be
  replaced by using the Context from the enclosing scope (which was already
  used in this method with the name `liftingCtx`).
- OfTypeImplicits does not need to keep a reference to the root Context
  either, we can just replace its lazy vals by defs that cache their results.
- ContextualImplicits does need to keep track of the Context from which
  its implicit references come from, but this Context itself does not have
  to be available implicitly in the class, instead the Context from the
  enclosing scope is used.

This also requires replacing OfTypeImplicits#toString and
ContextualImplicits#toString by a toText method in Printer to get access
to a Context.
`FunProto#typedArgs` was using the implicit Context from the `FunProto`
constructor, and thus errors weren't stored in the proper context.
This is fixed by passing an implicit Context argument to
`typedArgs` (this was already done for `typedArg`), this means that we
no longer need to store a Context in the class, on the other hand there
are some new difficulties:
- `FunProto#typedArgs` can only be safely called from a point where the
  implicit Context contains the Context where the `FunProto` instance was
  created in its owner chain. This is usually the case but required some
  changes to Implicits (done in the previous commit)
- The Context used must be created with Context#thisCallArgContext when
  typing the arguments of a this(...) constructor call. This requires
  passing storing this information in FunProto. This wouldn't be necessary
  if we could simply type the whole this(...) Apply node with
  Context#thisCallArgContext, unfortunately this does not work since this
  Context does not have the constructors of the class in scope, and I did
  not figure out a way to construct a Context that would have them in
  scope without having the other methods of the class in scope.
@smarter smarter force-pushed the fix-spurious-errors branch from 66801cc to 04c0f66 Compare October 6, 2017 15:08
smarter added a commit to smarter/dotty that referenced this pull request Dec 19, 2017
In several situations, instead of the common pattern in the compiler of
using the enclosing Context available coming from an implicit method
parameter, we used some other Context. Changing this is necessary for scala#2922
to go through. scala#2922 is currently on hold and may never get in but the
Context handling simplifications seem worth having anyway:

- RunInfo does not actually need to keep a reference to the root Context:
  It was previously used by `ImplicitRunInfo#implicitScope`, but can be
  replaced by using the Context from the enclosing scope (which was already
  used in this method with the name `liftingCtx`).
- OfTypeImplicits does not need to keep a reference to the root Context
  either, we can just replace its lazy vals by defs that cache their results.
- ContextualImplicits does need to keep track of the Context from which
  its implicit references come from, but this Context itself does not have
  to be available implicitly in the class, instead the Context from the
  enclosing scope is used.

This also requires replacing OfTypeImplicits#toString and
ContextualImplicits#toString by a toText method in Printer to get access
to a Context.
@smarter
Copy link
Member Author

smarter commented Aug 24, 2018

Superceded by #4871

@smarter smarter closed this Aug 24, 2018
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