Carp icon indicating copy to clipboard operation
Carp copied to clipboard

EDIT: `set!` sometimes gets untyped forms.

Open scolsen opened this issue 5 years ago • 17 comments

Dynamic.mod will cause loops on some arguments, the following code causes a hang:

;; assume arr is ['x 'y]
(= 0 (mod (length arr) 2)))

Changing mod to imod fixes the code above. However, the definition of Dynamic.mod itself seems fine, so this is likely stemming from some compiler issue.

scolsen avatar Mar 04 '21 15:03 scolsen

The underlying problem is coming from the length call. Using a plain old number works fine:

(= 0 (mod 2 2))

scolsen avatar Mar 04 '21 15:03 scolsen

Here's the fix: call eval on length

鲤 (= 0 (mod (eval (length ['x 'y])) 2))
=> true
鲤 

scolsen avatar Mar 04 '21 15:03 scolsen

This is likely a regression from some other change—seems related to some other differences re: the necessity of calling eval. What's strange is that it doesn't seem to have affected things uniformly.

scolsen avatar Mar 04 '21 15:03 scolsen

Taking a quick look at the code, there's no obvious reason for this issue. length is defined as a command, which afaict, is still evaluated immediately in the evaluator 🤔

scolsen avatar Mar 04 '21 15:03 scolsen

If I had to guess, we might need to reconsider the evaluator's symbol lookup clause, since this seems to be the only place where the hang might be happening (command handling hasn't changed)—we don't evaluate enough when retrieving some symbol or something.

scolsen avatar Mar 04 '21 15:03 scolsen

Is it possible length is recomputed every time the first argument is used/looked up (maybe this is related to laziness in a subtle way)?

hellerve avatar Mar 04 '21 15:03 hellerve

Is it possible length is recomputed every time the first argument is used/looked up (maybe this is related to laziness in a subtle way)?

Ah that's a good thought. I do think the issue is definitely too much laziness somewhere. Tinkering now.

scolsen avatar Mar 04 '21 15:03 scolsen

Ah, it's the final clause in the Evaluator symbol lookup (use modules lookup) that's causing the problem; in rare cases, e.g. this one, the use module name winds up conflicting with a parent module name and causes a loop. Yet another issue related to module relationship mgmt 😢

( foldl                                                                                                                                                                                       
                    (<|>)                                                                                                                                                                                     
                    Nothing                                                                                                                                                                                   
                    ( map                                                                                                                                                                                     
                        ( \(SymPath p' n') ->                                                                                                                                                                 
                            lookupBinder (SymPath (p' ++ (n' : p)) n) (contextGlobalEnv ctx)  -- this is the problematic part.  depending on symbol qualification, parent env paths, and use paths, this can intro a loop                                                                                                           
                              >>= \(Binder meta found) -> checkPrivate meta found                                                                                                                             
                        )                                                                                                                                                                                     
                        (Set.toList (envUseModules (contextGlobalEnv ctx)))                                                                                                                                   
                    )                                                                                                                                                                                         
                

scolsen avatar Mar 04 '21 15:03 scolsen

Hopefully the fix is as simple as nubbing the Use modules from the parent modules or some such. This might be another case where the approach of requiring qualification of a sympath as an explicit step before lookup is performed (like I did recently for contexts) helps.

scolsen avatar Mar 04 '21 15:03 scolsen

@scolsen Can we enforce that at the type level?

eriksvedang avatar Mar 04 '21 15:03 eriksvedang

Yep, That's what I tried to do recently with the QualifiedPath type in Context.hs and elsewhere

scolsen avatar Mar 04 '21 15:03 scolsen

In terms of lookups, the problem that occurs somewhat consistently is that the sympaths we wind up passing to the env lookup routines and what we actually expect to be passing differ or wind up containing duplications/conflict w/ parent paths or other qualifications being performed. I can't totally pinpoint where the duplication is creeping in for this case but, what basically seems to be happening in this code path for length is that we somehow lookup length in something like Dynamic.Dynamic.length, we find the first Dynamic, but not the second, the Dynamics parent is the global env, and somehow we perform the same exact lookup in the global env all over again, causing a loop.

scolsen avatar Mar 04 '21 15:03 scolsen

@scolsen Hmm, yes it's annoying that it keeps popping up. Perhaps we could add some pretty big (expensive) checks in the lookup functions to validate the inputs, and have a way of turning those off "in production"? The type based solution is of course much neater, but maybe it's too hard to prove correctness here given the unlimited recursion.

eriksvedang avatar Mar 04 '21 15:03 eriksvedang

There's actually something more nuanced happening here. Turns out the loop is being triggered by (= m b) and only when the input is something like (length ['x 'y])

scolsen avatar May 21 '21 21:05 scolsen

so, it turns out, the values being passed to set are untyped, while they should be typed as dynamic

scolsen avatar May 21 '21 21:05 scolsen

In particular, this seems to happen when calling a command, such as length. #1209 is a temporary fix, but we should really figure out why some forms are coming into set! completely untyped.

scolsen avatar May 21 '21 22:05 scolsen

@scolsen Sounds like a missing clause for the set! form somewhere perhaps?

eriksvedang avatar May 22 '21 13:05 eriksvedang