koka-lang / koka

Koka language compiler and interpreter
http://koka-lang.org
Other
3.26k stars 161 forks source link

Phantom compile errors in interactive compiler #399

Closed chtenb closed 1 week ago

chtenb commented 9 months ago

For some programs, loading the source code in the koka repl gives a strange error. Reloading it through :r does not give the compile error. Compiling as an executable also does not give the error. Happens in dev and latest release. Tested on windows 11.

Reproduction

Save the following code as repro.kk:

import std/num/float64

fun min-by(l : list<a>, ordering : a -> float64, default : a) : a
  min-by-rec(l, ordering, default, flt-min, default)

fun min-by-rec(l : list<a>, ordering : a -> float64, default : a, smallest : float64, result : a) : a
  match l
    Nil -> result
    Cons(x, tail) -> 
      if ordering(x) < smallest then 
        min-by-rec(tail, ordering, default, ordering(x), x)
      else
        min-by-rec(tail, ordering, default, smallest, result)

pub fun main()
  min-by([1.0, 2.0], id, 0.0)

Now run the following commands

  1. koka repro (no error)
  2. koka
  3. :l repro (error)
  4. :l repro (error)
  5. :r (no error)

See my output at the bottom for reference.

Note the error message:

.koka/v2.4.2/clang-cl-debug/repro.kki(52,41): error: invalid syntax
 unexpected ","
 expecting "fip", "fbip" or "fun"

What strikes me as odd is that the line number does not exist in the original source, but seems to refer to a kki file. I've added the kki file at the bottom of the post.

Reproduction console output

~\prj\kklib> koka repro                                                                          12/21/2023 11:53:34 AM
compile: repro.kk
loading: std/core
loading: std/core/types
loading: std/core/hnd
loading: std/num/float64
loading: std/text/parse
loading: std/num/int64
check  : repro
linking: repro
created: .koka\v2.4.2\clang-cl-debug\repro.exe
~\prj\kklib> koka                                                                                12/21/2023 11:53:43 AM
 _         _
| |       | |
| | _ ___ | | _ __ _
| |/ / _ \| |/ / _' |  welcome to the koka interactive compiler
|   ( (_) |   ( (_| |  version 2.4.2, Jul  3 2023, libc x64 (clang-cl)
|_|\_\___/|_|\_\__,_|  type :? for help, and :q to quit

loading: std/core
loading: std/core/types
loading: std/core/hnd

> :l repro
loading: repro
loading: std/core/types
loading: std/core
loading: std/core/hnd
.koka/v2.4.2/clang-cl-debug/repro.kki(52,41): error: invalid syntax
 unexpected ","
 expecting "fip", "fbip" or "fun"

> :l repro
loading: repro
loading: std/core/types
loading: std/core
loading: std/core/hnd
.koka/v2.4.2/clang-cl-debug/repro.kki(52,41): error: invalid syntax
 unexpected ","
 expecting "fip", "fbip" or "fun"

> :r
compile: repro.kk
loading: std/core
loading: std/core/types
loading: std/core/hnd
loading: std/num/float64
loading: std/text/parse
loading: std/num/int64
check  : repro
modules:
  repro

>

repro.kki

module interface repro

//------------------------------
//#kki: import declarations

import std/core/types = std/core/types = "";
import std/core = std/core = "";

//------------------------------
//#kki: external imports

//------------------------------
//#kki: fixity declarations

//------------------------------
//#kki: local imported aliases

//------------------------------
//#kki: type declarations

//------------------------------
//#kki: declarations

pub  fun min-by-rec : forall<a> (l : (std/core/list :: V -> V)<a>, ordering : (a) -> std/core/types/float64, default : a, smallest : std/core/types/float64, result : a) -> a;
pub  fun min-by : forall<a> (l : (std/core/list :: V -> V)<a>, ordering : (a) -> std/core/types/float64, default : a) -> a;
pub  fun main : () -> std/core/types/float64;

//------------------------------
//#kki: external declarations

//------------------------------
//#kki: inline definitions

//.inline-section
specialize "_*_"  fun min-by // inline size: 1
  = forall<a> fn(l: (std/core/list :: V -> V)<a>, ordering: (a) -> std/core/types/float64, default: a){
    repro/min-by-rec<a>(l, ordering, default, 2.2250738585072014e-308, default);
  };
recursive specialize "_*___"  fun min-by-rec // inline size: 7
  = forall<a> fn(l: (std/core/list :: V -> V)<a>, ordering: (a) -> std/core/types/float64, default: a, smallest: std/core/types/float64, result: a){
    (match (l) {
      ((std/core/Nil() : (std/core/list :: V -> V)<a> ) as .pat: ((std/core/list :: V -> V)<a>))
         -> result;
      ((.skip std/core/Cons((x: a) : a, (tail: (std/core/list :: V -> V)<a>) : (std/core/list :: V -> V)<a>) : (std/core/list :: V -> V)<a> ) as .pat0: ((std/core/list :: V -> V)<a>))
         -> (match ((std/core/(<.4)((ordering(x)), smallest))) {
          ((std/core/types/True() : std/core/types/bool ) as .pat1: std/core/types/bool)
             -> repro/min-by-rec<a>(tail, ordering, default, (ordering(x)), x);
          ((.skip std/core/types/False() : std/core/types/bool ) as .pat2: std/core/types/bool)
             -> repro/min-by-rec<a>(tail, ordering, default, smallest, result);
        });
    });
  };
 fun min-by // inline size: 1
  = forall<a> fn(l: (std/core/list :: V -> V)<a>, ordering: (a) -> std/core/types/float64, default: a){
    repro/min-by-rec<a>(l, ordering, default, 2.2250738585072014e-308, default);
  };
 fun main // inline size: 3
  = fn(){
    repro/min-by-rec<std/core/types/float64>((std/core/Cons<std/core/types/float64>(1.0, (std/core/Cons<std/core/types/float64>(2.0, (std/core/Nil<std/core/types/float64>))))), (std/core/types/id<std/core/types/float64>), 0.0, 2.2250738585072014e-308, 0.0);
  };
anfelor commented 9 months ago

@daanx This is due to the identifier tail in -> repro/min-by-rec<a>(tail, ordering, default, (ordering(x)), x); being parsed as the tail fip keyword. In this line the try parseFip parser succeeds while consuming input (due to the catch-all case of a tail but non-fip function) Then, the next line fails but the try keyword does not span that line. I would suggest moving the try keyword to span both the parseFip and the dockeyword "fun" in the parser.

TimWhiting commented 9 months ago

Actually this is now fixed on dev.

anfelor commented 9 months ago

Great, I can confirm that this is fixed! There is another occurrence of try parseFip in inlineDefSort -- while there is currently no danger of this going wrong, I would recommend fixing that as well since confusingly try parseFip == parseFip. Should I push a fix?

TimWhiting commented 9 months ago

Let's avoid that change for now. Daan has changed some parsing on the branch dev-overload, and we should wait for that to be merged.

daanx commented 1 week ago

Really fixed now -- famous last words :-)