savonet / liquidsoap

Liquidsoap is a statically typed scripting general-purpose language with dedicated operators and backend for all thing media, streaming, file generation, automation, HTTP backend and more.
http://liquidsoap.info
GNU General Public License v2.0
1.39k stars 126 forks source link

Different http handlers may cause typing errors #3303

Closed vitoyucepi closed 1 month ago

vitoyucepi commented 1 year ago

Describe the bug If I use different handlers with different routes they may trigger typing exception.

Exception ``` At 1.liq, line 9, char 0-0: Error 9: Failure: invalid sup: {a : 'A?} !< {a? : 'A?, b? : 'B?} (Typing error: _.{a : 'A} vs _.{a? : 'A?}) Raised at Stdlib.failwith in file "stdlib.ml", line 29, characters 17-33 Called from Liquidsoap_lang__Typing.(<:) in file "src/lang/typing.ml", line 427, characters 13-164 Called from Liquidsoap_lang__Typing.(<:).(fun) in file "src/lang/typing.ml", line 522, characters 20-27 Called from Stdlib__List.fold_left in file "list.ml", line 121, characters 24-34 Called from Liquidsoap_lang__Typing.(<:) in file "src/lang/typing.ml", line 497, characters 12-1023 Called from Liquidsoap_lang__Typing.unify_meth in file "src/lang/typing.ml", line 360, characters 7-63 Called from Liquidsoap_lang__Typing.(<:).(fun) in file "src/lang/typing.ml", line 522, characters 20-27 Called from Stdlib__List.fold_left in file "list.ml", line 121, characters 24-34 Called from Liquidsoap_lang__Typing.(<:) in file "src/lang/typing.ml", line 497, characters 12-1023 Called from Liquidsoap_lang__Typing.(<:) in file "src/lang/typing.ml", line 639, characters 6-12 Called from Liquidsoap_lang__Typechecking.check.(fun) in file "src/lang/typechecking.ml", line 315, characters 37-45 Called from Stdlib__List.fold_left in file "list.ml", line 121, characters 24-34 Called from Liquidsoap_lang__Typechecking.check.(fun) in file "src/lang/typechecking.ml", line 299, characters 18-990 Called from Liquidsoap_lang__Typechecking.check.(fun).check in file "src/lang/typechecking.ml", line 141, characters 4-39 Called from Liquidsoap_lang__Typechecking.check.(fun) in file "src/lang/typechecking.ml", line 273, characters 10-45 Called from Liquidsoap_lang__Typechecking.check.(fun).check in file "src/lang/typechecking.ml", line 141, characters 4-39 Called from Liquidsoap_lang__Typechecking.check.(fun) in file "src/lang/typechecking.ml", line 389, characters 10-48 Called from Liquidsoap_lang__Typechecking.check.(fun).check in file "src/lang/typechecking.ml", line 141, characters 4-39 Called from Liquidsoap_lang__Typechecking.check.(fun) in file "src/lang/typechecking.ml", line 389, characters 10-48 Called from Liquidsoap_lang__Typechecking.check in file "src/lang/typechecking.ml", line 419, characters 4-48 Re-raised at Liquidsoap_lang__Typechecking.check in file "src/lang/typechecking.ml", line 428, characters 4-38 Called from Liquidsoap_lang__Startup.time in file "src/lang/startup.ml", line 30, characters 12-16 Called from Liquidsoap_lang__Runtime.type_and_run.(fun) in file "src/lang/runtime.ml", line 33, characters 9-105 Called from Stdlib__Fun.protect in file "fun.ml", line 33, characters 8-15 Re-raised at Stdlib__Fun.protect in file "fun.ml", line 38, characters 6-52 Called from Liquidsoap_lang__Runtime.type_and_run in file "src/lang/runtime.ml", line 30, characters 4-612 Called from Liquidsoap_lang__Runtime.report in file "src/lang/runtime.ml" (inlined), line 215, characters 12-23 Called from Liquidsoap_lang__Runtime.from_lexbuf in file "src/lang/runtime.ml", line 229, characters 2-159 ```

To Reproduce

def a(_, res)
    res.json({a=null()})
end
def b(_, res)
    res.json({b=null()})
end
harbor.http.register("/a", a)
harbor.http.register("/b", b)

Expected behavior Different handlers don't interfere each other.

Version details

Install method Deb package from liquidsoap releases at github

Common issues N/A

vitoyucepi commented 9 months ago

I haven't tested this for a while, but here's an update for 2.2.2.

def a(_, res)
    res.json({a=null()})
end
def b(_, res)
    res.json({b=null()})
end
harbor.http.register("/a", a)
harbor.http.register("/b", b)
Log ``` At main.liq, line 21, char 27: harbor.http.register("/b", b) Error 5: this value has type (_, _.{json : (_.{a : 'A}, ...) -> _}, ...) -> _ (inferred at main.liq, line 13 char 0 - line 19 char 3) but it should be a subtype of the type of the value at http.liq, line 391, char 71-78 (_, _.{json : (_.{a? : 'A?}, ...) -> _}, ...) -> _ (inferred at http.liq, line 364, char 45-53) ```

But it's possible to get around this problem.

def response_json(~compact=true, res, data)
    res.content_type("application/json; charset=utf-8")
    res.data(json.stringify(data, compact=compact) ^ "\n")
end
def a(_, res)
    response_json(res, {a=null()})
end
def b(_, res)
    response_json(res, {b=null()})
end
harbor.http.register("/a", a)
harbor.http.register("/b", b)
Alternative style ```ruby def http.response.json(~compact=true, res, data) res.content_type("application/json; charset=utf-8") res.data(json.stringify(data, compact=compact) ^ "\n") end def a(_, res) http.response.json(res, {a=null()}) end def b(_, res) http.response.json(res, {b=null()}) end harbor.http.register("/a", a) harbor.http.register("/b", b) ```
toots commented 9 months ago

Here's a shorter version of the bug:

def f() = fun (_) -> 1 end
fn = f()
fn(1)
fn("aabb")
toots commented 8 months ago

Update on this one: the source of the issue has been found. We are not properly generalizing function applications so, in the example above, the generic function fun (_) -> 1 is not returned as taking any argument.

This can be fixed but we will need to handle the counter part, which is when the generalization should not happen. This happens because of references. For instance:

def f(x=null()) = ref(x) end
r = f()
r.set(1)
f.set("aabb")