X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/aa14a4cddcb96b681d5c19a2ec8bad382f43b264..e895be217c3be6769708da17c9ae87cb22db040e:/src/class-make-impl.lisp diff --git a/src/class-make-impl.lisp b/src/class-make-impl.lisp index 4470416..1da8bac 100644 --- a/src/class-make-impl.lisp +++ b/src/class-make-impl.lisp @@ -7,7 +7,7 @@ ;;;----- Licensing notice --------------------------------------------------- ;;; -;;; This file is part of the Sensble Object Design, an object system for C. +;;; This file is part of the Sensible Object Design, an object system for C. ;;; ;;; SOD is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by @@ -33,40 +33,47 @@ Return the most specific metaclass of any of the CLASS's direct superclasses." - (do ((supers (sod-class-direct-superclasses class) (cdr supers)) - (meta nil (let ((candidate (sod-class-metaclass (car supers)))) - (cond ((null meta) candidate) - ((sod-subclass-p meta candidate) meta) - ((sod-subclass-p candidate meta) candidate) - (t (error "Unable to choose metaclass for `~A'" - class)))))) - ((endp supers) meta))) + + (select-minimal-class-property (sod-class-direct-superclasses class) + #'sod-class-metaclass + #'sod-subclass-p class "metaclass")) (defmethod shared-initialize :after ((class sod-class) slot-names &key pset) "Specific behaviour for SOD class initialization. Properties inspected are as follows: - * `:metaclass' names the metaclass to use. If unspecified, nil is - stored, and (unless you intervene later) `guess-metaclass' will be - called by `finalize-sod-class' to find a suitable default. + * `:metaclass' names the metaclass to use. If unspecified, this will be + left unbound, and (unless you intervene later) `guess-metaclass' will + be called by `finalize-sod-class' to find a suitable default. * `:nick' provides a nickname for the class. If unspecified, a default (the class's name, forced to lowercase) will be chosen in `finalize-sod-class'. * `:link' names the chained superclass. If unspecified, this class will - be left at the head of its chain." + be left at the head of its chain. + + Usually, the class's metaclass is determined here, either direcly from the + `:metaclass' property or by calling `guess-metaclass'. Guessing is + inhibited if the `:%bootstrapping' property is non-nil." ;; If no nickname, copy the class name. It won't be pretty, though. (default-slot-from-property (class 'nickname slot-names) (pset :nick :id) (string-downcase (slot-value class 'name))) - ;; If no metaclass, guess one in a (Lisp) class-specific way. - (default-slot-from-property (class 'metaclass slot-names) - (pset :metaclass :id meta (find-sod-class meta)) - (guess-metaclass class)) + ;; Set the metaclass if the appropriate property has been provided or we're + ;; not bootstreapping; otherwise leave it unbound for now, and trust the + ;; caller to sort out the mess. + (multiple-value-bind (meta floc) (get-property pset :metaclass :id) + (cond (floc + (setf (slot-value class 'metaclass) + (with-default-error-location (floc) + (find-sod-class meta)))) + ((not (get-property pset :%bootstrapping :boolean)) + (default-slot (class 'metaclass slot-names) + (guess-metaclass class))))) ;; If no chain-link, then start a new chain here. (default-slot-from-property (class 'chain-link slot-names) @@ -77,18 +84,24 @@ ;;; Slots. (defmethod make-sod-slot - ((class sod-class) name type pset &optional location) + ((class sod-class) name type pset &key location) (with-default-error-location (location) - (let ((slot (make-instance (get-property pset :lisp-class :symbol + (when (typep type 'c-function-type) + (error "Slot declarations cannot have function type")) + (let ((slot (make-instance (get-property pset :slot-class :symbol 'sod-slot) :class class :name name :type type :location (file-location location) - :pset pset))) + :pset pset)) + (initarg-name (get-property pset :initarg :id))) (with-slots (slots) class (setf slots (append slots (list slot)))) - (check-unused-properties pset)))) + (when initarg-name + (make-sod-slot-initarg-using-slot class initarg-name slot pset + :location location)) + slot))) (defmethod shared-initialize :after ((slot sod-slot) slot-names &key pset) "This method does nothing. @@ -102,42 +115,44 @@ ;;; Slot initializers. (defmethod make-sod-instance-initializer - ((class sod-class) nick name value-kind value-form pset - &optional location) + ((class sod-class) nick name value pset &key location inhibit-initargs) (with-default-error-location (location) (let* ((slot (find-instance-slot-by-name class nick name)) - (initializer (make-sod-initializer-using-slot - class slot 'sod-instance-initializer - value-kind value-form pset - (file-location location)))) + (initarg-name (get-property pset :initarg :id)) + (initializer (and value + (make-sod-initializer-using-slot + class slot 'sod-instance-initializer + value pset (file-location location))))) (with-slots (instance-initializers) class - (setf instance-initializers - (append instance-initializers (list initializer)))) - (check-unused-properties pset)))) + (unless (or initarg-name initializer) + (error "Slot initializer declaration with no effect")) + (when (and initarg-name (not inhibit-initargs)) + (make-sod-slot-initarg-using-slot class initarg-name slot pset + :location location)) + (when initializer + (setf instance-initializers + (append instance-initializers (list initializer))))) + initializer))) (defmethod make-sod-class-initializer - ((class sod-class) nick name value-kind value-form pset - &optional location) + ((class sod-class) nick name value pset &key location) (with-default-error-location (location) (let* ((slot (find-class-slot-by-name class nick name)) (initializer (make-sod-initializer-using-slot class slot 'sod-class-initializer - value-kind value-form pset - (file-location location)))) + value pset (file-location location)))) (with-slots (class-initializers) class (setf class-initializers (append class-initializers (list initializer)))) - (check-unused-properties pset)))) + initializer))) (defmethod make-sod-initializer-using-slot - ((class sod-class) (slot sod-slot) - init-class value-kind value-form pset location) - (make-instance (get-property pset :lisp-class :symbol init-class) + ((class sod-class) (slot sod-slot) init-class value pset location) + (make-instance (get-property pset :initializer-class :symbol init-class) :class class :slot slot - :value-kind value-kind - :value-form value-form - :location location + :value value + :location (file-location location) :pset pset)) (defmethod shared-initialize :after @@ -149,28 +164,84 @@ (declare (ignore slot-names pset)) nil) +(defmethod make-sod-user-initarg + ((class sod-class) name type pset &key default location) + (with-slots (initargs) class + (push (make-instance (get-property pset :initarg-class :symbol + 'sod-user-initarg) + :location (file-location location) + :class class :name name :type type :default default) + initargs))) + +(defmethod make-sod-slot-initarg + ((class sod-class) name nick slot-name pset &key location) + (let ((slot (find-instance-slot-by-name class nick slot-name))) + (make-sod-slot-initarg-using-slot class name slot pset + :location location))) + +(defmethod make-sod-slot-initarg-using-slot + ((class sod-class) name (slot sod-slot) pset &key location) + (with-slots (initargs) class + (with-slots ((type %type)) slot + (setf initargs + (append initargs + (cons (make-instance (get-property pset :initarg-class + :symbol + 'sod-slot-initarg) + :location (file-location location) + :class class :name name + :type type :slot slot) + nil)))))) + +(defmethod sod-initarg-default ((initarg sod-initarg)) nil) + +(defmethod sod-initarg-argument ((initarg sod-initarg)) + (make-argument (sod-initarg-name initarg) + (sod-initarg-type initarg) + (sod-initarg-default initarg))) + +;;;-------------------------------------------------------------------------- +;;; Initialization and teardown fragments. + +(defmethod make-sod-class-initfrag + ((class sod-class) frag pset &key location) + (declare (ignore pset location)) + (with-slots (initfrags) class + (setf initfrags (append initfrags (list frag))))) + +(defmethod make-sod-class-tearfrag + ((class sod-class) frag pset &key location) + (declare (ignore pset location)) + (with-slots (tearfrags) class + (setf tearfrags (append tearfrags (list frag))))) + ;;;-------------------------------------------------------------------------- ;;; Messages. (defmethod make-sod-message - ((class sod-class) name type pset &optional location) + ((class sod-class) name type pset &key location) (with-default-error-location (location) - (let ((message (make-instance (get-property pset :lisp-class :symbol - 'standard-message) - :class class - :name name - :type type - :location (file-location location) - :pset pset))) + (let* ((msg-class (or (get-property pset :message-class :symbol) + (and (get-property pset :combination :keyword) + 'aggregating-message) + 'standard-message)) + (message (make-instance msg-class + :class class + :name name + :type type + :location (file-location location) + :pset pset))) (with-slots (messages) class (setf messages (append messages (list message)))) - (check-unused-properties pset)))) + message))) (defmethod shared-initialize :after ((message sod-message) slot-names &key pset) - (declare (ignore slot-names pset)) - (with-slots (type) message - (check-message-type message type))) + (with-slots ((type %type)) message + (check-message-type message type)) + (default-slot-from-property (message 'readonlyp slot-names) + (pset :readonly :boolean) + nil)) (defmethod check-message-type ((message sod-message) (type c-function-type)) nil) @@ -182,25 +253,25 @@ ;;; Methods. (defmethod make-sod-method - ((class sod-class) nick name type body pset &optional location) + ((class sod-class) nick name type body pset &key location) (with-default-error-location (location) (let* ((message (find-message-by-name class nick name)) (method (make-sod-method-using-message message class type body pset (file-location location)))) (with-slots (methods) class - (setf methods (append methods (list method))))) - (check-unused-properties pset))) + (setf methods (append methods (list method)))) + method))) (defmethod make-sod-method-using-message ((message sod-message) (class sod-class) type body pset location) - (make-instance (or (get-property pset :lisp-class :symbol) + (make-instance (or (get-property pset :method-class :symbol) (sod-message-method-class message class pset)) :message message :class class :type type :body body - :location location + :location (file-location location) :pset pset)) (defmethod sod-message-method-class @@ -213,28 +284,65 @@ (declare (ignore slot-names pset)) ;; Check that the arguments are named if we have a method body. - (with-slots (body type) method + (with-slots (body (type %type)) method (unless (or (not body) - (every #'argument-name (c-function-arguments type))) + (every (lambda (arg) + (or (eq arg :ellipsis) + (argument-name arg) + (c-type-equal-p (argument-type arg) + c-type-void))) + (c-function-arguments type))) (error "Abstract declarators not permitted in method definitions"))) ;; Check the method type. - (with-slots (message type) method + (with-slots (message (type %type)) method (check-method-type method message type))) (defmethod check-method-type ((method sod-method) (message sod-message) (type c-type)) (error "Methods must have function type, not ~A" type)) +(export 'check-method-return-type) +(defun check-method-return-type (method-type wanted-type) + "Signal an error unless METHOD-TYPE does not return the WANTED-TYPE." + (let ((method-returns (c-type-subtype method-type))) + (unless (c-type-equal-p method-returns wanted-type) + (error "Method return type ~A should be ~A" + method-returns wanted-type)))) + +(export 'check-method-return-type-against-message) +(defun check-method-return-type-against-message (method-type message-type) + "Signal an error unless METHOD-TYPE and MESSAGE-TYPE return the same type." + (let ((message-returns (c-type-subtype message-type)) + (method-returns (c-type-subtype method-type))) + (unless (c-type-equal-p message-returns method-returns) + (error "Method return type ~A doesn't match message ~A" + method-returns message-returns)))) + +(export 'check-method-argument-lists) +(defun check-method-argument-lists (method-type message-type) + "Signal an error unless METHOD-TYPE and MESSAGE-TYPE have matching argument + lists. + + This checks (a) that the two types have matching lists of mandatory + arguments, and (b) that either both or neither types accept keyword + arguments." + (let ((message-keywords-p (typep message-type 'c-keyword-function-type)) + (method-keywords-p (typep method-type 'c-keyword-function-type))) + (cond (message-keywords-p + (unless method-keywords-p + (error "Method must declare a keyword argument list"))) + (method-keywords-p + (error "Method must not declare a keyword argument list")))) + (unless (argument-lists-compatible-p (c-function-arguments message-type) + (c-function-arguments method-type)) + (error "Method arguments ~A don't match message ~A" + method-type message-type))) + (defmethod check-method-type ((method sod-method) (message sod-message) (type c-function-type)) - (with-slots ((msgtype type)) message - (unless (c-type-equal-p (c-type-subtype msgtype) - (c-type-subtype type)) - (error "Method return type ~A doesn't match message ~A" - (c-type-subtype msgtype) (c-type-subtype type))) - (unless (argument-lists-compatible-p (c-function-arguments msgtype) - (c-function-arguments type)) - (error "Method arguments ~A don't match message ~A" type msgtype)))) + (with-slots ((msgtype %type)) message + (check-method-return-type-against-message type msgtype) + (check-method-argument-lists type msgtype))) ;;;----- That's all, folks --------------------------------------------------