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: EvalFull duplicated IDs and captures variables in BETA reduction #511

Merged
merged 2 commits into from
Jun 20, 2022

Conversation

brprice
Copy link
Contributor

@brprice brprice commented Jun 14, 2022

No description provided.

Comment on lines 367 to 369
-- We would like to say (Λa.t : ∀b.T) S ~> (letType a = S in t) : (letType b = S in T)
-- but we do not have letTypes inside types, so the best we can do is
-- (Λa.t : ∀b.T) S ~> (letType b = S in (letType a = S in t) : T
-- We need to be careful if a /= b: as this can capture a 'b' inside 'S' or 't'.
-- Thus if necessary we do some renaming
-- (Λa.t : ∀b.T) S ~> letType c = b in letType b = c in (Λa.t : ∀b.T) S for b free in t or S, and fresh c
-- We then ensure the delicate property that we reduce the b=c first, then the BETA, then the c=b
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe at some point in the future we can undo this change by adding lets in types. This may be necessary to implement #44

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we expect that adding such lets would be difficult? I suspect the symmetry would make interactive eval a lot easier to follow.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, there's a missing closing parenthesis on line 369.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we expect that adding such lets would be difficult? I suspect the symmetry would make interactive eval a lot easier to follow.

I would expect it to be the same solution as supporting term-level let type in the typechecker #337. The issue is how to display types that may refer to the locally-bound name or its expansion.

Comment on lines +475 to +484
-- Previously EvalFull reducing a BETA expression could result in variable
-- capture. We would reduce (Λa.t : ∀b.T) S to
-- let b = S in (let a = S in t) : T
-- The outer let binding could capture references within S or t.
unit_type_preservation_BETA_regression :: Assertion
unit_type_preservation_BETA_regression =
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The property tests we have "should have caught it eventually", but not in a reasonable timeframe. I would like to improve the generators (or add different tests) to improve the situation, but I don't have any good ideas on how to do this.

One thing I explored is to have a generator of BETA redexes, and test type preservation for that on its own. However, this rather targeted test still only picked up the bug roughly one test in 5000. For posterity, the code is pasted below

-- This test: generate random BETA redex in an environment with a typevar, check for type preservation
--
-- Bug was   (Λa. t : ∀b.T) S  ~~>  letType b=S in (letType a=S in t) : T
-- which can capture a 'b' in 'S' or 't'
hprop_tmp :: Property
hprop_tmp = withTests 1000 $
  withDiscards 2000 $ propertyWT [] $ do
      k <- forAllT genWTKind
      v <- forAllT genTyVarName
      (t, tyBeta) <- local (extendLocalCxtTy (v,k)) genBETA
      annotateShow t
      annotateShow tyBeta
      t' <- generateIDs $ LAM () v t
      let tyOrig = TForall () v k tyBeta
      _ <- checkTest tyOrig t'
      let chk e = do
            annotateShow e
            when (null [() | LetType{} <- universe e]) $ void $ checkTest tyOrig e
      let go i e | i >= 10 = pure ()
                 | otherwise = do
                   annotateShow i
                   e' <- evalFull mempty mempty 1 Chk e
                   case e' of
                     Left (TimedOut e'') -> chk e'' >> go (i +1) e''
                     Right e'' -> chk e''
      go 0 t'
  where
    genBETA = do
      k <- forAllT genWTKind
      a <- forAllT genTyVarName
      ty <- forAllT $ local (extendLocalCxtTy (a,k)) $ genWTType KType
      b <- forAllT $ genTyVarNameAvoiding ty
      ty' <- substTy a (TVar () b) ty
      t <- forAllT $ local (extendLocalCxtTy (b,k)) $ genChk ty'
      ty2 <- forAllT $ genWTType k
      tyRet <- substTy a ty2 ty
      pure (APP () (Ann () (LAM () b t) (TForall () a k ty)) ty2, tyRet)

@dhess
Copy link
Member

dhess commented Jun 14, 2022

Yikes, how did you find this one?

@brprice brprice force-pushed the brprice/evalfull-beta-capture branch from 3ebbff3 to 2f17fbd Compare June 15, 2022 11:14
@brprice brprice requested a review from a team June 15, 2022 11:15
Copy link
Member

@dhess dhess left a comment

Choose a reason for hiding this comment

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

I confess that I don't understand the fix well enough to say anything particularly useful about it. I'm approving this so it can proceed, but also feel free to wait for @georgefst to have a look, if you prefer.

@brprice
Copy link
Contributor Author

brprice commented Jun 15, 2022

Yikes, how did you find this one?

Part of my search for shadowing and capture problems. I noticed when looking at the documented evaluation rule again.

@dhess dhess added the Do not merge Do not merge label Jun 16, 2022
@dhess
Copy link
Member

dhess commented Jun 16, 2022

I labeled this "Do not merge" as a note to myself until @georgefst has a chance to look at it upon his return.

@dhess dhess requested a review from georgefst June 16, 2022 15:00
@dhess dhess removed the Do not merge Do not merge label Jun 16, 2022
Comment on lines 367 to 369
-- We would like to say (Λa.t : ∀b.T) S ~> (letType a = S in t) : (letType b = S in T)
-- but we do not have letTypes inside types, so the best we can do is
-- (Λa.t : ∀b.T) S ~> (letType b = S in (letType a = S in t) : T
-- We need to be careful if a /= b: as this can capture a 'b' inside 'S' or 't'.
-- Thus if necessary we do some renaming
-- (Λa.t : ∀b.T) S ~> letType c = b in letType b = c in (Λa.t : ∀b.T) S for b free in t or S, and fresh c
-- We then ensure the delicate property that we reduce the b=c first, then the BETA, then the c=b
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we expect that adding such lets would be difficult? I suspect the symmetry would make interactive eval a lot easier to follow.

Comment on lines 367 to 369
-- We would like to say (Λa.t : ∀b.T) S ~> (letType a = S in t) : (letType b = S in T)
-- but we do not have letTypes inside types, so the best we can do is
-- (Λa.t : ∀b.T) S ~> (letType b = S in (letType a = S in t) : T
-- We need to be careful if a /= b: as this can capture a 'b' inside 'S' or 't'.
-- Thus if necessary we do some renaming
-- (Λa.t : ∀b.T) S ~> letType c = b in letType b = c in (Λa.t : ∀b.T) S for b free in t or S, and fresh c
-- We then ensure the delicate property that we reduce the b=c first, then the BETA, then the c=b
Copy link
Contributor

Choose a reason for hiding this comment

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

Also, there's a missing closing parenthesis on line 369.

@brprice brprice force-pushed the brprice/evalfull-beta-capture branch from 2f17fbd to 81e929f Compare June 20, 2022 16:07
brprice added 2 commits June 20, 2022 17:09
We previously were testing this in some unit tests but had no property
test. We add a property test to prevent any regression in the future.
We add a unit test to avoid regressions in the future. This test is
rather detailed, testing multiple intermediate steps of the reduction to
illustrate the slightly subtle process.
@brprice brprice force-pushed the brprice/evalfull-beta-capture branch from 81e929f to 716cbb5 Compare June 20, 2022 16:11
@brprice brprice added this pull request to the merge queue Jun 20, 2022
@brprice brprice merged commit eef1832 into main Jun 20, 2022
@brprice brprice deleted the brprice/evalfull-beta-capture branch June 20, 2022 16:52
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.

3 participants