Skip to content
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

fix #2053 by using Var.rawName to produce text for synthetic vars #2138

Merged
merged 1 commit into from
Jun 29, 2021

Conversation

aryairani
Copy link
Contributor

@aryairani aryairani commented Jun 19, 2021

display <foo> stores results in the watch expression cache, which doesn't currently model the synthetic Vars that can come out of the runtime. This PR just flattens them down to Text.

@aryairani aryairani requested review from dolio and runarorama June 23, 2021 16:43
@dolio
Copy link
Contributor

dolio commented Jun 23, 2021

So, the map example isn't actually correct, right?

If I recall, there's a problem where the numbers for these variables aren't actually displayed when used as terms for reasons I don't understand. Thus all pattern variables show up with rawName _pattern rather than _patternN. So, patterns will bind the correct variable, but then the occurrence in a term will reference the wrong variable.

I'm unclear on what the right fix is, though. On separate occasions, I've been told that this shouldn't be fixed, at least for some variables (like user variables), and should, probably for variables like these. I don't know exactly what is safe to change, and what expected consequences make it unsafe. But something needs to be done to prevent caching incorrect results.

@aryairani
Copy link
Contributor Author

@dolio Bleh, thanks for catching that. I think it's a term-printer issue in this case? An inconsistency between how the numbers are rendered in pattern vars vs other vars, although I'm not certain because I didn't track down the spot (/cc @atacratic could you confirm or deny).

Let's sync up with @pchiusano next week and try to hash out what we'd need to change in order to begin to know exactly what's safe to change, and to know why whatever isn't wouldn't be, etc.

@dolio
Copy link
Contributor

dolio commented Jun 24, 2021

Okay, sounds good. I think there's some situation where it's discarding all the new numberings for fresh variables made up from user variables, because they "don't matter" or something. But I didn't understand why that was even safe at the time.

@@ -242,8 +242,7 @@ symbol2to1 :: V2.Symbol -> V1.Symbol
symbol2to1 (V2.Symbol i t) = V1.Symbol i (Var.User t)

symbol1to2 :: V1.Symbol -> V2.Symbol
symbol1to2 (V1.Symbol i (Var.User t)) = V2.Symbol i t
symbol1to2 x = error $ "unimplemented: symbol1to2 " ++ show x
symbol1to2 (V1.Symbol i varType) = V2.Symbol i (Var.rawName varType)
Copy link
Member

Choose a reason for hiding this comment

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

@aryairani @dolio okay yeah, this isn't quite the right fix, because the raw variable name may shadow or collide with another user-chosen name that's in the parent expression. In general, it's not safe to just rewrite a single variable using only local info like that, because of the possibility of collisions. You need to pass in something that is a closed term / type (no free vars) so the renaming of variables can be done in a way that avoids accidental capture.

If it's just a type signature you're tweaking, I think the correct fix will involve calling this function, passing it the full type:

-- The typechecker generates synthetic type variables as part of type inference.
-- This function converts these synthetic type variables to regular named type
-- variables guaranteed to not collide with any other type variables.
--
-- It also attempts to pick "nice" type variable names, based on what sort of
-- synthetic type variable it is and what type variable names are not already
-- being used.
removeSyntheticTypeVars :: Var v => Type.Type v loc -> Type.Type v loc

It will pick nice type variable names and avoid variable capture.

If you have a term with embedded annotations, then you might have to do something fancier to avoid capturing type variables from an outer scope. Hmm, thinking through this, I'm not sure it's possible for a type signature to refer to synthetic type variables in an outer scope. By design, there's no syntax for synthetic type variables, so the user can't write them in an explicit type signature. And the only way to capture a type variable from an outer scope is with an explicit type signature. So it seems that just recursively calling removeSyntheticTypeVars on any embedded type annotations in a term might be fine.

If it were necessary to do something fancier to deal with scoped type variable capture inside of terms, these functions do that properly and you could probably take inspiration from them and I'd be happy to pair.

freeTypeVars :: Ord vt => Term' vt v a -> Set vt

substTypeVars :: (Ord v, Var vt)
=> [(vt, Type vt b)]
-> Term' vt v a
-> Term' vt v a

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 function is just about converting a single symbol from one format to another, and this PR is just about fixing a crash in it.

What you're saying sounds to me like there's a bigger problem that's unrelated to this function or this PR. We get the same bad pretty-term even with --old-codebase, which doesn't call this code.

So I think we should merge this and open a ticket for the thing I don't yet understand, and maybe @dolio could take point on? 🙏

WDYT?

Copy link
Contributor Author

@aryairani aryairani Jun 28, 2021

Choose a reason for hiding this comment

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

I could back out or move the transcript, given that the output is wrong, but it's not related to this PR; the point was that it produces output at all now instead of crashing, and there could also be existing transcripts that are wrong for the same reason.

Copy link
Member

Choose a reason for hiding this comment

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

@aryairani yeah, I'm okay with merging as is, just to avoid the crash, even though it's not correct, as long you create a separate ticket to track the proper fix.

It will only come up if someone picks one of those weird blackboard symbols for a type signature, which I think should be pretty rare.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@pchiusano Our example case wasn't due to blackboard symbols, but due to pretty-printing a value that is a function that does pattern matching:

.> view List.map

  base.List.map : (a ->{𝕖} b) -> [a] ->{𝕖} [b]
  base.List.map f a =
    go i as acc =
      match List.at i as with
        None   -> acc
        Some a ->
          use Nat +
          go (i + 1) as (acc :+ f a)
    go 0 a []

.> display List.map

  go f i as acc =
    _pattern = List.at i as
    match _pattern with
      None           -> acc
      Some _pattern1 ->
        use Nat +
        go f (i + 1) as (acc :+ f _pattern)
  f a -> go f 0 a []

@pchiusano pchiusano merged commit 05eef2e into trunk Jun 29, 2021
@pchiusano pchiusano deleted the topic/render-synthetic-vars branch June 29, 2021 12:27
@hojberg
Copy link
Member

hojberg commented Jun 29, 2021

@aryairani @dolio @pchiusano did this also fix #2144 ? (seems similar to #2053)

@aryairani
Copy link
Contributor Author

@aryairani @dolio @pchiusano did this also fix #2144 ? (seems similar to #2053)

@hojberg #2144 reports two bugs, only the second of which was fixed by this.

@pchiusano pchiusano mentioned this pull request Aug 20, 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.

Ucm exits with unimplemented: symbol1to2 Pattern when using display command
4 participants