predlib.lsp is loaded before the clos module, but constants used by predlib were
defined in the latter. That worked because the C compiler inlines constants that
are fixnums, although it is a bit shaky to rely on particular optimizations.
To avoid this dependency we hardcode relevant constants in symbols_list.h and
later we assert in the clos module ethat their values are correct.
For empty structs, the size was off by one. While this did not affect
structs that were created directly with the structure constructor, for
structs that were externalized with MAKE-LOAD-FORM-SAVING-SLOTS
instances which were one element too large were created.
The pattern in the long form of the define-method-combination may contain * as
a list element meaning "any" qualifier. For example:
(define-method-combination foo ()
((bar (:xxx * :yyy)))
...)
In this case qualifiers `:xxx 3 :yyy' and `:xxx :zzz :yyy' will match.
The function SORT-APPLICABLE-METHODS accepts as the third argument called
ARGS-SPECIALIZERS however this function assumed that the argument was a list
of argument's classes (i.e not EQL specializers) - see COMPARE-SPECIALIZERS.
This commit doesn't change the function signature but conses a new list that
is ensured to be a list of classes and passes them to COMPARE-METHODS.
(Local) functions COMPARE-METHODS, COMPARE-SPECIALIZERS-LISTS and
COMPARE-SPECIALIZERS have the argument name changed to reflect their true
expectations.
The function COMPARE-SPECIALIZERS takes the CLASS-PRECEDENCE-LIST of the class
of the argument to break ties when there is no direct relationship between
method specializers.
Previously a local function APPLICABLE-METHOD-P returned (values nil nil) when
it found an EQL-specializer where the object was of a matching class. This is
premature because some later specializer may make the method not applicable
based on one of the argument classes, so there is no need to resort to
COMPUTE-APPLICABLE-METHODS in such case.
Previously this function stored a list of elements
(cons list-or-random-atom argument-position)
ARGUMENT-POSITION was preasumbly stored because authors anticipated denoting
unspecialized arguments, however all positions were always filled because
unspecializer argument had non-nil specializer #<BUILTIN-CLASS T>.
LIST-OR-RANDOM-ATOM contained either a list of EQL specializers or a random
specializer from all method specializers. The second value was not clear.
This change simplifies the code and the interface, we additionally maintain
additional information. From now on the list stores elements:
(cons class-specializer-p eql-specializers)
CLASS-SPECIALIZER-P is either NIL or T denoting whether the generic function
has a method specialized on this argument on a class other than T.
EQL-SPECIALIZERS without change, is a list of all EQL specializers.
Previously we didn't call it due to bootstrapping issues, but now we convert
functions to methods after early methods are fixed up and their classes are
also updated, so we can. This fix improves conformance.
The most notable change applies to the file fixup.lsp. Functions destined to
be generic are converted to their final version.
Previously this conversion was done in a few steps in order to avoid issues
with infinite recursion in dispatch. We achieve this by assigning to these new
generic function a simplified discriminating function:
(lamda (&rest args)
(unless (or (null *clos-booted*)
(specializers-match-p args specializers))
(apply #'no-applicable-method generic-function args))
(apply old-function args))
The old function is also seeded as a primary method for the generic
function. This works correctly because functions have only one method so we
may directly call it (it is also a fine optimization strategy we do not
incorporate generally yet), and because the discriminating function will be
recomputed when other methods are added etc.
This way we may use these generic functions without issues directly with newly
redefined versions and the file is now ordered as follows:
- fixup early methods
- redefine functions to their final version
- convert functions to generics
- define missing methods
- implement the dependant maintenance protocol
After this file is loaded it is possible to use generic functions as usual.
Previously we've checked whether the new defstruct is compatible with the old
one like this:
(let ((old-desc (old-descriptions struct)))
(when (and old-desc (null (compat old-desc new-desc)))
(error "incompatible")))
This was to allow new definitions. This is incorrect, because allows first
defining a structure without slots and then adding some, like
(defstruct foo)
(defstruct foo xxx)
The new check verifies whether the structure is a structure and then compares
slot, so the verification is not inhibited when the first definition doesn't
have slots.
Moreover we now test for slot names being string= because:
a) initargs and functions ignore the package (so functions will be redefined)
b) we want to match gensymed slot names
This is compatible with what sbcl does.
On top of that check for duplicated names and signal an error if there are
such.
- we did not distinguish between classes that had no slots and classes that
had no been iniutialized - that led to incorrect class stamps
- structures had no initial class stamp matching their structure
- structures when slot names chagned had their stamp increased despite not
really changing
- don't assume that any keyword is an option
- don't process the same keyword twice
New behavior could be summarized in these two cases:
(restart-case t
(retry ()
:retired ; <- form
))
(restart-case t
(retry ()
:report report ; <- expression
:report "foo" ; <- form
:test test ; <- form
))
Fixes#666.
The forward-referenced-class metaclass has no superclasses.
Moreover:
- check superclasses after they are added (removes one fixme)
- don't map validate-superclass during clos booting
Accessors are fuctions not macros. While using macros is fine in most
cases, we can't use them for example in higher-order functions. The
only reason this worked in the first place is due to our compiler
allowing expressions such as
`(macrolet ((x (...) ...)) (funcall #'x ...))
even though this is invalid.
The spec says:
The generic function make-instances-obsolete is invoked
automatically by the system when defclass has been used to
redefine an existing standard class and the set of local slots
accessible in an instance is changed or the order of slots in
storage is changed. It can also be explicitly invoked by the user.
If the local slot's class is changed then indeed the set has
changed. We also check whether the slot class is S-D-S-D or S-E-S-D
and in both cases we also decide that layouts are not compatible.
Fixes#586.
Slot definitions are no longer a signature, but they are still needed
to update obsolete instances. Reader function name is also changed to
SI:INSTANCE-SLOTDS. SI:INSTANCE-SIG-SET name does not change, because
it sets both SLOTDS and the STAMP.
We should call make-instances-obsolete from finalize-inheritance if we
want to be conforming, because user may have added their own auxiliary
methods.
This change while being last in a serie of commits was locally the
first change which solved problems. It will enable us to implement the
fast generic dispatch after the release.