agda / agda

Agda is a dependently typed programming language / interactive theorem prover.

Home Page:https://wiki.portal.chalmers.se/agda/pmwiki.php

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Agda >=2.6.3 hangs on conflicting record directives

andreasabel opened this issue · comments

Since 2.6.3, Agda hangs on:

record Foo : Set where
  inductive; coinductive

Agda-2.6.2 still says:

agda-2.6.2 ConflictingRecordDirectives.agda
/Users/abel/play/agda/bugs/ConflictingRecordDirectives.agda:2,3-3
/Users/abel/play/agda/bugs/ConflictingRecordDirectives.agda:2,3: Repeated record directives at:
/Users/abel/play/agda/bugs/ConflictingRecordDirectives.agda:2,3-12
/Users/abel/play/agda/bugs/ConflictingRecordDirectives.agda:2,14-25

<EOF><ERROR>
...

This was the behavior since 2.5.1.1.

NB. Since 2.5.1.1 we also accept the stray semicolon in:

record Foo : Set₁ where
  ; field f : Set

I guess this is not intentional but owed to our grammar:

RecordDeclarations :: { (RecordDirectives, [Declaration]) }
RecordDeclarations
: vopen RecordDirectives close {% verifyRecordDirectives $2 <&> (,[]) }
| vopen RecordDirectives semi Declarations1 close {% verifyRecordDirectives $2 <&> (, List1.toList $4) }
| vopen Declarations1 close { (emptyRecordDirectives, List1.toList $2) }
RecordDirectives :: { [RecordDirective] }
RecordDirectives
: {- empty -} { [] }
| RecordDirectives semi RecordDirective { $3 : $1 }
| RecordDirective { [$1] }

We should rather parser non-empty lists of RecordDirectives.

I have not understood yet why the parser loops, but I suppose I broke the parser when adding the pattern directive to record declarations in https://github.com/agda/agda/pull/4611/files#diff-0c56f1735859726e838b86513623fc00d52a99963ba2094bb062d2e2af82ad2b

It could have to do something with pattern starting both a directive and a declaration in records. E.g. we parse this, but reject it in the scope checker:

record Foo : Set1 where
  constructor c
  pattern p x = x
  field f : Set
This declaration is illegal in a record before the last field

However, we accept this:

record Foo : Set1 where
  constructor c
  pattern p x = x

So in the unit record, you can declare a pattern synonym in a way it is potentially ambiguous with the pattern record directive. I'd say we do not lose anything if we forbid this pattern synonym. (Note that the constructor c isn't even in scope here, pattern p = c is rejected.)

While debugging this, I found that printing anything containing ranges in the parser loops.
Actually now I believe that this issue is caused by @nad's f4e7639, see comment f4e7639#r142630350 .
This commit uses a mdo to lazily fill in the SrcFile component in ranges:

parseSource :: SourceFile -> TCM Source
parseSource sourceFile@(SourceFile f) = Bench.billTo [Bench.Parsing] $ do
(source, fileType, parsedMod, attrs, parsedModName) <- mdo
-- This piece of code uses mdo because the top-level module name
-- (parsedModName) is obtained from the parser's result, but it is
-- also used by the parser.
let rf = mkRangeFile f (Just parsedModName)
source <- runPM $ readFilePM rf
((parsedMod, attrs), fileType) <- runPM $
parseFile moduleParser rf $
TL.unpack source
parsedModName <- moduleName f parsedMod
return (source, fileType, parsedMod, attrs, parsedModName)
libs <- getAgdaLibFiles f parsedModName

Looks like someone shot himself in the foot when playing with a powerful weapon.
Basically any Range is a now a loaded mine during parsing which detonates if you touch it.
I don't think we should turn the code base into such a minefield. I lost 3 hours hunting for this bug, wondering why I could not even get debug printing.

In general, making code very sophisticated isn't helping long-term maintenance of a project.
I think we should outright ban mdo. Atm, the present bug is the only active use of mdo, we have another one in some dead code (old record pattern translation by @nad).

I am fixing this issue by moving the verifyRecordDirectives procedure into the scope checker.