@@ -263,64 +263,113 @@ Returns (VALUES deferred-preds retained-preds defaultable-preds)"
263263; ;; "transitive `closure'" of the known types.
264264
265265(defun fundep-entail (env expr-preds preds known-tyvars)
266- (loop :with _expr-preds
267- := (alexandria :mappend (alexandria :curry #' by-super env) expr-preds)
268- :with subs := nil
269- :for pred :in preds
270- :for new-subs := (fundep-entail% env _expr-preds pred known-tyvars)
271- :do (setf subs (compose-substitution-lists subs new-subs))
272- :finally (return subs)))
266+ " Produce a list of type substitutions that reduce the generality of
267+ PREDS with respect to EXPR-PREDS based on functional dependencies.
268+
269+ For example, consider the following,
270+
271+ ```
272+ (define-class (C :a :b (:a -> :b)))
273+ (define-class (C :a :b => D :a :b)
274+ (m :a))
275+
276+ (declare f (D :a :b => Unit -> :a))
277+ (define (f) m)
278+ ```
279+
280+ For the example above, EXPR-PREDS includes only D #T1 #T2 and PREDS
281+ includes only D #T1 #T3, and KNOWN-TYVARS includes only #T1. In the
282+ absence of functional dependencies, the second type variable in the
283+ predicate for class D is ambiguous, i.e., it cannot be determined.
284+ But, functional dependencies allow us to determine that #T2 and #T3
285+ must in fact be the same type, so FUNDEP-ENTAIL will return the
286+ substitution #T3 +-> #T2. Note that in the absence of functional
287+ dependencies, the class definition for D itself would not have passed
288+ through the typechecker, because the method M does not contain all of
289+ the type variables of the class D.
290+ "
291+ (flet ((expand (context)
292+ (alexandria :mappend (alexandria :curry #' by-super env) context)))
293+ (loop :with expr-preds := (expand expr-preds)
294+ :with preds := (expand preds)
295+ :with subs := nil
296+ :for pred :in preds
297+ :for new-subs := (fundep-entail% env expr-preds pred known-tyvars)
298+ :do (setf subs (compose-substitution-lists subs new-subs))
299+ :finally (return subs))))
273300
274301(defun fundep-entail% (env expr-preds pred known-tyvars)
302+ " A helper function for FUNDEP-ENTAIL to be applied iteratively, binding
303+ PRED to each element in a list of predicates. Note that we do not
304+ consider superclass functional dependencies here. That is because the
305+ predicates were expanded into their superclasses prior to the
306+ applications of this function such that any relevant superclass
307+ functional dependencies will be considered directly when the
308+ respective superclass predicate is passed to this function."
275309 (let ((class (lookup-class env (ty-predicate-class pred))))
310+
276311 (unless (ty-class-fundeps class )
277312 (return-from fundep-entail% nil ))
278313
279- (let* ((unknown-indices nil )
280-
281- (known-indices
314+ (let* ((partitioned-indices
282315 (loop :for ty :in (ty-predicate-types pred)
316+ :for tyvars := (type-variables ty)
283317 :for i :from 0
284- :if (intersection (type-variables ty) known-tyvars :test #' eq )
285- :collect i
318+ :if (consp ( intersection tyvars known-tyvars :test #' ty=) )
319+ :collect i :into known-indices
286320 :else
287- :do (push i unknown-indices)))
321+ :collect i :into unknown-indices
322+ :finally (return (cons known-indices unknown-indices))))
323+
324+ (known-indices (car partitioned-indices))
325+ (unknown-indices (cdr partitioned-indices))
288326
289- (unknown-indices (reverse unknown-indices))
290-
291- (known-class-vars (util :project-indices known-indices (ty-class-class-variables class )))
327+ (class-vars (ty-class-class-variables class ))
292328
329+ (known-class-vars
330+ (util :project-indices known-indices class-vars))
293331 (unknown-class-vars
294- (util :project-indices unknown-indices (ty- class-class-variables class ) ))
332+ (util :project-indices unknown-indices class-vars ))
295333
334+ ; ; We expand the list of known variables by computing the
335+ ; ; closure of the known class variables over the class's
336+ ; ; functional dependencies. See the docstring for CLOSURE
337+ ; ; for more details.
296338 (closure (closure known-class-vars (ty-class-fundeps class ))))
297339
298- (unless (subsetp unknown-class-vars closure)
340+ ; ; If the unknown class variables are not a subset of the
341+ ; ; closure of the known class variables, then the predicate is
342+ ; ; not fully determined by the known class variables and the
343+ ; ; class functional dependencies. In this case, we ignore any
344+ ; ; substitutions that can be made, since they would only be
345+ ; ; relevant in the case that we allow ambiguous methods to be
346+ ; ; determined by the functional dependencies of another type
347+ ; ; class.
348+ (unless (subsetp unknown-class-vars closure :test #' eq )
299349 (return-from fundep-entail% nil ))
300350
301- (loop :for expr-pred :in expr-preds
351+ (loop :for expr-pred
352+ ; ; We only consider expression predicates for the same
353+ ; ; class as that of PRED.
354+ :in (remove (ty-predicate-class pred) expr-preds :key #' ty-predicate-class
355+ :test (complement #' eq ))
302356
303- :for expr-pred-indices
304- := (loop :for ty :in (ty-predicate-types pred)
357+ :for expr-pred-known-indices
358+ := (loop :for ty :in (ty-predicate-types expr-pred)
359+ :for tyvars := (type-variables ty)
305360 :for i :from 0
306- :when (intersection (type-variables ty) known-tyvars :test #' eq )
361+ :when (consp ( intersection tyvars known-tyvars :test #' ty=) )
307362 :collect i)
308363
309- :when (and (eq (ty-predicate-class pred) (ty-predicate-class expr-pred))
310- (equalp known-indices expr-pred-indices))
311-
312- :do
313- (return-from
314- fundep-entail%
315- (loop :for ty :in (util :project-indices
316- unknown-indices
317- (ty-predicate-types expr-pred))
318-
319- :for ty_ :in (util :project-indices
320- unknown-indices
321- (ty-predicate-types pred))
322-
323- :when (and (tyvar-p ty) (tyvar-p ty_))
324- :collect (make-substitution :from ty_ :to ty))))
325-
326- nil )))
364+ :when (every #' = known-indices expr-pred-known-indices)
365+ ; ; Create and return substitutions for the unknown - but now
366+ ; ; determined by functional dependencies - types.
367+ :do (flet ((project (pred)
368+ (let ((types (ty-predicate-types pred)))
369+ (util :project-indices unknown-indices types))))
370+ (loop :for from-ty :in (project pred)
371+ :for to-ty :in (project expr-pred)
372+ :append (match from-ty to-ty) :into new-subs
373+ :finally (return-from fundep-entail% new-subs)))
374+
375+ :finally (return nil )))))
0 commit comments