Opened 10 days ago

Last modified 10 days ago

#1533 new defect

Scrutinizer seems to get confused about list length

Reported by: sjamaan Owned by:
Priority: major Milestone: 5.0
Component: scrutinizer Version: 5.0.0rc2
Keywords: flow analysis Cc:
Estimated difficulty: hard


Seen when compiling awful-salmonella-tar:

(/usr/home/chicken/src/awful-salmonella-tar/ast-cache-manager.scm:86) in procedure call to `scheme#car', expected argument #1 of type `pair' but was given an argument of type `null'

Here's a stripped-down example that triggers it:

(module ast-cache-manager ()

(import scheme (chicken base) (chicken process-context))

(define (usage #!optional exit-code)
  (when exit-code
    (exit exit-code)))

(let ((args (command-line-arguments)))

  (when (or (member "-h" args)
            (member "-help" args)
            (member "--help" args))
    (usage 0))

  (when (< (length args) 3)
    (usage 1))

  (let ((cache-dir (car args))
        (awful-pid (string->number (cadr args)))
        (max-items (and (not (null? (cddr args)))
                        (string->number (caddr args)))))
    (assert awful-pid)))

) ;; end module

It seems that when you remove the when, the warning goes away.

Change History (1)

comment:1 Changed 10 days ago by megane

There's at least two things going on here.

First is type smashing. The calls to usage, which has unknown type
smashes the type of args to (or pair null). This can be avoided by
moving the usage calls to tail call positions.

In theory, defining usage like this should help:

(define usage
  (the (#!optional fixnum -> noreturn)
        (lambda (#!optional exit-code)
          (when exit-code
            (exit exit-code)))))

But doesn't, for some reason.

Secondly, there's an issue with refinement:

(import scheme (chicken base) (chicken type))

(let ([a (the (or pair null) (cons 1 '()))])
  (length a) ; refine (or pair null) with list (= (or pair null))
  (compiler-typecase a ((not *) 1)))
;; Error: at toplevel:
;;   (hoi.scm:33) no clause applies in `compiler-typecase' for expression of type `null':
;;     (not *)

The type should stay (or pair null), but is refined to null.

Note: See TracTickets for help on using tickets.