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

Unexpected termination failure with %tcinline and assert_total #3459

Open
gallais opened this issue Jan 3, 2025 · 1 comment
Open

Unexpected termination failure with %tcinline and assert_total #3459

gallais opened this issue Jan 3, 2025 · 1 comment

Comments

@gallais
Copy link
Member

gallais commented Jan 3, 2025

The idea here is to have a way to express domain-specific knowledge by declaring that
some primitives are considered guarding.

I figured I could introduce a newtype wrapper that users cannot build themselves (unexported
constructor) and give some primitives a blessing by making them return a guarded result.
This guarded result will morally only arrive after something productive has happened and can
therefore be used to guard a co-recursive call.

By marking callGuarded with %tcinline, and making its body be a big assert_total I was
hoping it'd have the right behaviour however the main I include is currently rejected by the
compiler with the error

Guarded.idr line 24 col 0:
     main is not total, possibly not terminating due to recursive path Main.main

while the (commented-out) alternative definition is accepted just fine.

%default total

namespace Guard

  -- Do NOT export the constructor
  export
  data Guarded : Type -> Type where
    MkGuarded : (1 _ : a) -> Guarded a

  runGuarded : (1 _ : Guarded a) -> a
  runGuarded (MkGuarded v) = v

  export %tcinline
  callGuarded : Guarded a -> (a -> b) -> b
  callGuarded x f = assert_total (f (runGuarded x))

  export
  -- getLine is declared guarding
  getLine : IO (Guarded String)
  getLine = do
    str <- Prelude.getLine
    pure (MkGuarded str)

main : IO ()
main = do
  putStrLn "What is your name?"
  gstr <- getLine
  callGuarded gstr $ \ name => do
    putStrLn "Hello \{name}"
    main

{-
  name <- Prelude.getLine
  assert_total $ do
    putStrLn "Hello \{name}"
    main
-}
gallais added a commit to gallais/potpourri that referenced this issue Jan 3, 2025
Unfortunately my attempted trick using Guarded, assert_total &
tc_inline does not work, cf.

idris-lang/Idris2#3459
@gallais
Copy link
Member Author

gallais commented Jan 6, 2025

It does work if I make callGuarded public export.

That may very well be the intended behaviour although I would expect to be able to distinguish
exporting the definition to allow the machine to inline it during termination checking vs. users
being able to look at the internals themselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant