<< Back to previous view

[CTYP-132] typechecker fails when checking a Protocol whose method gets called using dot notation Created: 12/Apr/14  Updated: 13/Apr/14  Resolved: 13/Apr/14

Status: Closed
Project: core.typed
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Daniel Ziltener Assignee: Ambrose Bonnaire-Sergeant
Resolution: Completed Votes: 0
Labels: bug

Clojure 1.6.0, core.typed 0.2.44


The error message is

No method in multimethod 'check' for dispatch value: :host-interop

The following is a minimal test case which fails:

(ns core-typed-bug.core
    (:require [clojure.core.typed :refer :all]))
(defprotocol> ITypedTest
    (get-data [this]))
(defn> testfn :- Any
    [asdf :- Keyword, in :- ITypedTest]
    (.get-data in))

It works, however, when calling (get-data in) instead of (.get-data in).

Comment by Nicola Mometto [ 12/Apr/14 7:13 PM ]

tools.analyzer.jvm returns a :host-interop node when, like in this case, it encounters an interop form of the form (.foo bar) and can't determine whether it's a no-arg method call or a field-access.

I don't know enough about core.typed internals but it looks like there should be an add-check-method for :host-interop that behaves like check methods for :instance-field/:instance-call that resolve to runtime reflection (not :validated)

Comment by Ambrose Bonnaire-Sergeant [ 13/Apr/14 12:44 AM ]

Thanks Daniel & Nicola, fixed https://github.com/clojure/core.typed/commit/fa22d52d8e9855ebbaf2593ec5e848ba714b25fc

[CTYP-130] add HSequential type Created: 31/Mar/14  Updated: 10/Apr/14  Resolved: 10/Apr/14

Status: Resolved
Project: core.typed
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Di Xu Assignee: Ambrose Bonnaire-Sergeant
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File ambrose-hsequential-v3-1.patch     Text File ambrose-various-fixes.patch     File hsequential-demo.diff     File hsequential.diff     File hsequential-v2.diff     File hsequential-v3.diff    


Hi, Ambrose

I've implemented part of HSequential as we discussed in CTYP-126.

But currently

(t/cf (fn [& y] (if (empty? y) nil (first y))))

will got Type error, saying

(I (HSequential [Any *]) clojure.core.typed/NonEmptyCount)

can't be applied to

(HSequential [x Any *])

it's easy to fix via adding special case in `upcast-to-HSequential` in patch, but this maybe not very general, I also think even `upcast-to-HSequential` isn't general at all.

How should I solve this error?

Comment by Ambrose Bonnaire-Sergeant [ 31/Mar/14 6:01 AM ]

You should call subtype directly with those two types and add printlns until the culprit is obvious.

There's a nice sub? macro in c.c.t.test.core

There's a bug in your second subtype? case; the test should be (HSequential? s), not (HSequential? t).

Comment by Ambrose Bonnaire-Sergeant [ 31/Mar/14 6:03 AM ]

I believe if you fix the subtype case, the left hand side will have a CountRange that satisfies the right.

Comment by Ambrose Bonnaire-Sergeant [ 31/Mar/14 6:08 AM ]

Hmm I see what you were trying to do. I suggest following HMap's subtyping.

Make `upcast-HSequential` which is [HSequential -> Type]. It always returns a slightly less accurate type, which is still useful to continue the subtyping check.

The subtyping case should test (HSequential? s) and then (subtype (upcast-HSequential s) t).

Comment by Ambrose Bonnaire-Sergeant [ 31/Mar/14 6:12 AM ]

I think upcast-HSequential should do something like

(HSequential [Any])
(I (t/Coll Any) clojure.lang.Sequential (ExactCount 1))

Comment by Ambrose Bonnaire-Sergeant [ 31/Mar/14 6:23 AM ]

A nice thing we can do with HSequential is move most of the custom logic for (subtype HVec HVec), (subtype HList HList) and (subtype HSeq HSeq) into one place.

All we need is {HVec,Hlist,HSeq}->HSequential functions, and then we can have subtyping cases like

(and (HVec? s) (HVec? t))
(subtype (HVec->HSequential s) (HVec->HSequential t))

This also applies to cs_gen.

Comment by Di Xu [ 31/Mar/14 8:19 AM ]

Or, could we add special handler in `In` to handle the case of intersection of HSequential and CountRange, like change (In (HSequential [a b c *]) (CountRange 1 5)) -> (HSequential [a b c c c]) and (In (HSequential [a *]) (CountRange 1)) -> (HSequential [a a *])

something like that?

Comment by Ambrose Bonnaire-Sergeant [ 31/Mar/14 9:08 AM ]

I'm not sure. The first example would actually be (U (HSequential [a b]) (HSequential [a b c]) (HSequential [a b c c]) (HSequential [a b c c c]).

It might generate very large types.

Either way the subtype style I described is still needed.

Comment by Di Xu [ 01/Apr/14 10:55 PM ]

Well, I think we're talking about two different problem here, the problem you're talking about is how to compare HSequential with other type, the one I'm talking about is how to use CountRange to extends HSequential type, because

(HSequential [Any *])

can never be the subtype of

(HSequential [Any Any *])

, only when we put constraint that the count of first one is at least 1. Right?

So, I'll first extends `In` as I described here, and leave your solution to future. For the efficience consideration, I think it's not a big problem, because most CountRange just specified low bound as `empty?`, so we don't need to generate a large set very often.

Comment by Ambrose Bonnaire-Sergeant [ 03/Apr/14 6:36 AM ]

I was responding that (HSequential [a b c *]) (CountRange 1 5)) -> (HSequential [a b c c c])
is unsound, and should be (U (HSequential [a b]) (HSequential [a b c]) (HSequential [a b c c]) (HSequential [a b c c c]).

Comment by Ambrose Bonnaire-Sergeant [ 03/Apr/14 6:43 AM ]

Perhaps a better idea is to add an extra field in HSequential/HList etc. for a CountRange. Then (HSequential [a b c *]) (CountRange 1 5)) -> (HSequential [a b c *] :count (CountRange 1 5))

That way there's no chance of generating massive types, and we can utilise that information in subtyping to simplify the HSequential at the last minute.

Comment by Di Xu [ 03/Apr/14 9:59 AM ]

Wow, that's a good idea. So when (> (:lower c) (count (:types t))) we should extends the types with (repeat (:rest t)), but I don't know what to do with drest. I will mark it unsupport.

There're a problem makes me complete lost. With hsequential-demo.diff patch, after changing the r/-hsequential into r/-hvec there'll be no problem testing

lein test :only clojure.core.typed.test.core/annotate-user-defined-ploydot

but after changing back to r/-hsequential, there'll be a type error. I don't know what I'm missing, I've debugged it for a whole day and still feel lost. I've greped the code that support HVec in fold_default.clj, frees.clj, promote_demote.clj and subst.clj. And copy the code to implement HSequential, but still got type error. I don't know what's going on here.

ps. the bug you mentioned in the first comment is not a bug, I was going to test if t is HSequential or not, and upcast s to HSequential if so, just like the {HVec,Hlist,HSeq}->HSequential you menntioned.

Comment by Ambrose Bonnaire-Sergeant [ 03/Apr/14 1:18 PM ]

I found a bunch of (pre-existing) bugs and added a few cases you were missing. I started getting the same type error for HVec/HSequential; hopefully the attached patch will help you continue.

Comment by Di Xu [ 07/Apr/14 3:02 AM ]

Well, turns out we don't need CountRange to pass the test case, but we also can't pass it with `first`, because both function and arguments have free variable, so change the test case to concrete type.

Comment by Ambrose Bonnaire-Sergeant [ 07/Apr/14 5:07 AM ]

The rest arguments of a function are actually ISeq's, not just Sequentials. We should be calling -hseq instead of -hsequential in the :fn check method.

If you `mvn test` you'll see what this breaks.

Next step:

  • add rest/drest to HSeq (and HList if you're enthusiastic)
  • change -hsequential to -hseq
Comment by Di Xu [ 08/Apr/14 9:06 AM ]

added, but failed in core/mapentry-first-test, core/hvec-ops and core/variable-hvec-test

I think those cases is beyond my current ability, it involve infer type variable in HSequential.

also failed in clojure.core.typed.test.fail.recur-non-seq-rest and clojure.core.typed.test.fail.recur-empty-seq

I don't know why should these cases fail. And confused by check-recur, which requires rest-arg-type has at least one element.

Comment by Ambrose Bonnaire-Sergeant [ 08/Apr/14 9:44 AM ]

Thanks, having a look.

You've added a few subtyping cases that are unsound. I'll fix them in my next patch.

It's important that (subtype? S T) only returns true if we can insert S in all places T is accepted.

(subtype? (HVec [1]) (HSequential [1]))
;=> true

(subtype? (HSequential [1]) (HVec [1]))
;=> false

(subtype? (HList [1] (HSeq [1]))
;=> true

(subtype? (HSeq [1]) (HList [1]))
;=> false

Comment by Ambrose Bonnaire-Sergeant [ 08/Apr/14 10:39 AM ]

Added ambrose-hsequential-v3-1.patch

Should pass all the tests. Please try it out.

Comment by Di Xu [ 09/Apr/14 2:27 AM ]

Oh, right.. it's unsound to do that in subtype, I was foolish that moment, sorry.

It's seems that you solved (first [1 'a]) by

(and (r/RClass? S)
     ((some-fn r/HeterogeneousVector? r/HSequential?) T))
         (if-let [[Sv] (seq
                         (filter (some-fn r/HeterogeneousVector? r/HSequential?)
                                 (map c/fully-resolve-type (c/RClass-supers* S))))]


Well, I think we could close this ticket now. It spend such long time

Comment by Ambrose Bonnaire-Sergeant [ 09/Apr/14 2:40 AM ]

Actually I believe I added some extra cases for (cs-gen HVec HSeq) to fix (first [1 'a]).

That particular addition fixed (cf (-> {:a 1} first second) Number), because AMapEntry has a HVec ancestor and we used to rely on `second` having a HVec arity. Now `second` has a HSequential arity, we want to trigger this case if there's a (cs-gen RClass HSequential), instead of just (cs-gen RClass HVec).

Fantastic work, I'll merge this soon.

Comment by Ambrose Bonnaire-Sergeant [ 10/Apr/14 10:47 AM ]


Generated at Wed Apr 16 23:08:42 CDT 2014 using JIRA 4.4#649-r158309.