I don't have very much CL experience, but I've done a lot of work in Scheme.
In the second version (sans setf a) the remove-if expression is evaluated, but it does nothing to actually change a. loop is a macro in CL, it just evaluates expressions, but doesnt use the results of those expressions like a recursive function would.
So in the first version, the value of a is changed each time the loop runs due to the setf, but in the second, the value of a is constant throughout. Thus (car a) never changes and the loop never terminates.
we can compare the results of macroexpand on both the loop statements:
without setf:
(MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR)))
(BLOCK NIL
(LET NIL
(MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP)))
(TAGBODY SYSTEM::BEGIN-LOOP
(PROGN (UNLESS (< (CAR A) X) (LOOP-FINISH))
(PROGN (PUSH (CAR A) B) (REMOVE-IF #'(LAMBDA (M) (= 0 (MOD M (CAR A)))) A)))
(GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP
(MACROLET
((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN) '(GO SYSTEM::END-LOOP))))))))) ;
with setf:
(MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR)))
(BLOCK NIL
(LET NIL
(MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP)))
(TAGBODY SYSTEM::BEGIN-LOOP
(PROGN (UNLESS (< (CAR A) X) (LOOP-FINISH))
(PROGN (PUSH (CAR A) B)
(SETF A (REMOVE-IF #'(LAMBDA (M) (= 0 (MOD M (CAR A))))) A)))
(GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP
(MACROLET
((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN) '(GO SYSTEM::END-LOOP))))))))) ;
you can see that in the first loop the remove-if expression is evaluated, but its result isn't used.