GrammaticalFramework / gf-core

Grammatical Framework core: compiler, shell & runtimes
https://www.grammaticalframework.org
Other
129 stars 35 forks source link

Compilation hangs with `param` variants, succeeds with `record` variants #132

Open inariksit opened 3 years ago

inariksit commented 3 years ago

TL;DR

Now follows the long explanation.

Grammars

Abstract: TestVariants.gf

abstract TestVariants = {
  cat
    S ; Subj ; Pred ;
  fun
    PredVP : Subj -> Pred -> S ;
    cat_Subj : Subj ;
    doesnt_sit_Pred : Pred ;
}

Concrete with bug: TestVariantsBugEng.gf

The category Pred is has lincat ExtendEng.VPS, like this.

lincat
  Pred = ExtendEng.VPS ;

In addition, we have an oper that allows both contracted and uncontracted negation.

oper
  PNegInlineVariants : Pol = lin Pol {s = [] ; p = ResEng.CNeg (True|False)} ;

The issue comes in the linearisation of doesnt_sit_Pred, which is defined as follows.

lin
  doesnt_sit_Pred =
    let temp : Temp = mkTemp pastTense simultaneousAnt ;
        vp   : VP   = mkVP sit_V ;
     in ExtendEng.MkVPS temp PNegInlineVariants vp ;

However, this version of the same grammar compiles just fine. It's still using variants, but now they are not on the level of params, but rather the whole record.

oper
  PNegTopVariants  : Pol = lin Pol ( {s = [] ; p = R.CNeg True }
                                   | {s = [] ; p = R.CNeg False}
                                   ) ;
lin
  doesnt_sit_Pred =
    let temp : Temp = mkTemp pastTense simultaneousAnt ;
        vp   : VP   = mkVP sit_V ;
     in ExtendEng.MkVPS temp PNegTopVariants vp ;

The bug

When I try to compile the code that uses PNegInlineVariants, it gets stuck at doesnt_sit_Pred.

$ gf -v TestVariantsBugEng.gf
…
- parsing TestVariantsBugEng.gf
  renaming
  type checking
  optimizing  PNegInlineVariants
 doesnt_sit_Pred
 cat_Subj
 Subj
 S
 PredVP
 Pred
 PNegTopVariants

  generating PMCFG
+ Pred 1
+ PredVP 10 (10,10)
+ S 1
+ Subj 10
+ cat_Subj 1 (1,1)
+ doesnt_sit_Pred 1

It is stuck there in what seems to be an infinite loop. I added debug output in getFIds, and this is what it spits out:

+ doesnt_sit_Pred 1
-------
getFIds
Pred
1 Pred
CRec [LIdent (Id {rawId2utf8 = "s"}),LIdent (Id {rawId2utf8 = "lock_VPS"})]
-------
getFIds.variants: found a record
getFIds.variants: found a table Order
getFIds.variants: found a table Agr
getFIds.variants: found a record
getFIds.variants: recursion ended on string 0
getFIds.variants: recursion ended on string 1
getFIds.variants: found a record
…
getFIds.variants: recursion ended on string 58
getFIds.variants: recursion ended on string 59
getFIds.variants: found a record

-------
getFIds
Pred
1 Pred
CRec [LIdent (Id {rawId2utf8 = "s"}),LIdent (Id {rawId2utf8 = "lock_VPS"})]
-------
getFIds.variants: found a record
getFIds.variants: found a table Order
getFIds.variants: found a table Agr
getFIds.variants: found a record
getFIds.variants: recursion ended on string 0
getFIds.variants: recursion ended on string 1

… more of the same until I press Ctrl+C

So there's something that calls getFIds over and over again, the subfunction getFIds.variants ends every time. This is the stack trace from when I pressed Ctrl+C: getFIds is called most immediately from GeneratePMCFG.addPMCFG.addRule, but that doesn't mean the looping happens there–there are many other functions where I didn't put debug output.

*** Exception (reporting due to +RTS -xc): (base:GHC.Exception.Type.SomeException), stack trace:
  GF.Compile.GeneratePMCFG.getFIds.variants,
  called from GF.Compile.GeneratePMCFG.getFIds,
  called from GF.Compile.GeneratePMCFG.addPMCFG.addRule.(...),
  called from GF.Compile.GeneratePMCFG.addPMCFG.addRule,
  called from GF.Data.BacktrackM.return.\,
  called from GF.Data.BacktrackM.return,
  called from GF.Data.BacktrackM.>>=.\.\,
  called from GF.Data.BacktrackM.member.\.\,
  called from GF.Data.BacktrackM.member.\,
  called from GF.Data.BacktrackM.member,
  called from GF.Data.BacktrackM.>>=.\,
  called from GF.Data.BacktrackM.>>=,
  called from GF.Compile.GeneratePMCFG.goV,
  called from GF.Compile.GeneratePMCFG.goB,
  called from GF.Data.BacktrackM.foldBM,
  called from GF.Compile.GeneratePMCFG.addPMCFG.pmcfgEnv1,
  called from GF.Compile.GeneratePMCFG.addPMCFG,
  called from GF.Compile.GeneratePMCFG.mapAccumWithKeyM.mapAccumM,
  called from GF.Compile.GeneratePMCFG.mapAccumWithKeyM,
  called from GF.Compile.GeneratePMCFG.generatePMCFG,

Other observations

I tried to make a more minimal version of the grammar, that doesn't use the RGL, but has the same inline variants.

The full grammar is at TestVariantsNoRGLNoBug.gf. I defined the parameters as follows:

  param
    Bool = True | False ;
    CPolarity = CPos | CNeg Bool ;

  oper
    Pol : Type = {s : Str ; p : CPolarity} ;
    PNegInlineVariants : Pol = {s = [] ; p = CNeg (True|False)} ;
    PNegTopVariants    : Pol = {s = [] ; p = CNeg True } |
                               {s = [] ; p = CNeg False} ;

But when I use PNegInlineVariants in the linearisation of doesnt_sit_Pred, it compiles happily.