liquidsoap icon indicating copy to clipboard operation
liquidsoap copied to clipboard

Type infer error with `output.file`

Open vitoyucepi opened this issue 1 year ago • 9 comments

Describe the bug I got an error if I apply a function that modifies source, pass modified source into the output.file and then try to create a list from unmodified source and modified source.

To Reproduce

a = playlist("/a")
b = normalize(a)
output.file(%vorbis, "a.ogg", a, fallible = true)
c = [b, a]

Expected behavior No error as in 2.0

Version details

  • OS: ubuntu:20.04 in docker
  • Version: 2.1.0

Install method Debian package for ubuntu focal in the 2.1.0 release.

Common issues N/A

vitoyucepi avatar Jul 19 '22 00:07 vitoyucepi

Looks like a type inference issue: Screen Shot 2022-07-19 at 12 39 51 AM

toots avatar Jul 19 '22 05:07 toots

Hum, "interesting"... A slightly minified version:

a = sine()
b = a.{xxx = 5}
output.file(%vorbis, "/tmp/a.ogg", a, fallible = true)
c = [b, a]

The strange thing is that if we replace output.file by output.dummy or output.pulseaudio the problem vanishes.

Let's try to understand...

smimram avatar Jul 19 '22 08:07 smimram

Narrowing down: this works with

def f(s)
  ignore((s:source))
end
def g(s)
  ignore((s:source(audio=pcm(stereo))))
end

a = sine()
b = a.{xxx = 5}
f(a)
c = [b, a]

but not when I replace the call to f by a call to g.

smimram avatar Jul 19 '22 08:07 smimram

More narrowing:

a = (sine():source)
b = a.{xxx = 5}
_ = (a : source(audio=pcm(stereo)))
_ = [b, a]

It works if I remove the penultimate line.

smimram avatar Jul 19 '22 11:07 smimram

New finding:

  • it works if I print the type I we cast to in the penultimate line (yes, heisenbug, printing makes the bug disappear)
  • it does not work if I remove the printing of ground types. i.e.
diff --git a/src/lang/repr.ml b/src/lang/repr.ml
index b694c0174..02efffd05 100644
--- a/src/lang/repr.ml
+++ b/src/lang/repr.ml
@@ -182,7 +182,7 @@ let make ?(filter_out = fun _ -> false) ?(generalized = []) t : t =
     if filter_out t then `Ellipsis
     else (
       match t.descr with
-        | Ground g -> `Ground g
+        | Ground g -> (* `Ground g *) `Tuple []
         | Getter t -> `Getter (repr g t)
         | List { t; json_repr } -> `List (repr g t, json_repr)
         | Tuple l -> `Tuple (List.map (repr g) l)
diff --git a/src/lang/typechecking.ml b/src/lang/typechecking.ml
index d9c624da7..b92fa5184 100644
--- a/src/lang/typechecking.ml
+++ b/src/lang/typechecking.ml
@@ -187,6 +187,7 @@ let rec check ?(print_toplevel = false) ~throw ~level ~(env : Typing.env) e =
     | Null -> e.t >: mk (Type.Nullable (Type.var ~level ?pos ()))
     | Cast (a, t) ->
         check ~level ~env a;
+        Printf.printf "t: %s\n%!" (Type.to_string t);
         a.t <: t;
         e.t >: t
     | Meth (l, a, b) ->

Conclusion, this is related to the way ground types are handled... @toots it seems that the ball is in your camp now :)

smimram avatar Jul 19 '22 11:07 smimram

This is actually more related to printing of formats. I get the error if I disable printing of formats:

diff --git a/src/lang/repr.ml b/src/lang/repr.ml
index b45064be1..af715e947 100644
--- a/src/lang/repr.ml
+++ b/src/lang/repr.ml
@@ -256,7 +256,7 @@ let print f t =
         Format.fprintf f "none";
         vars
     | `Constr (_, [(_, `Ground (Ground.Format format))]) ->
-        Format.fprintf f "%s" (Content.string_of_format format);
+        (* Format.fprintf f "%s" (Content.string_of_format format); *)
         vars
     | `Constr (name, params) ->
         Format.open_box (1 + String.length name);

Is there some kind of lazy value or such in there?

My current guess is that we need to properly handle subtyping for formats in <: and the reason why printing them changes anything is that when printing we assign default values to unknown parameters. Am I right?

smimram avatar Jul 19 '22 12:07 smimram

Clearly, there's something non standard with the way formats are passed down as type. I'm working on generalizing and cleaning this up in the main branch and will address it there.

toots avatar Jul 26 '22 17:07 toots

@toots I've revisited this issue and maybe it's broken because of #2524. That issue is not backported to 2.1.x yet.

a = playlist("/a")

a has remaining() of type () -> [string]

b = normalize(a)

b has remaining() of type () -> float

So it's possible that they are actually not type compatible.

The we'll create a list.

c = [a, b]

And check this two loops.

  1. for i = list.iterator(c) do print(i) end;;
    
  2. for i = list.iterator(c) do print(i.remaining()) end;;
    

vitoyucepi avatar Jul 27 '22 04:07 vitoyucepi

I don't think that this is related to #2524: my minimized examples don't use playlist.

smimram avatar Aug 03 '22 11:08 smimram

I can't reproduce this issue in 1a262d49bd2cfb8457439e986c748a1a0317f8d4 and c15ac2ea08f36e49014d8e06e583387d5e11aff5. Perhaps it should be closed.

vitoyucepi avatar Apr 20 '24 01:04 vitoyucepi

Great thanks for checking

toots avatar Apr 20 '24 13:04 toots