chiark / gitweb /
src/class-make-impl.lisp: Forbid slots with function types.
[sod] / src / class-make-impl.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Class construction protocol implementation
4;;;
5;;; (c) 2009 Straylight/Edgeware
6;;;
7
8;;;----- Licensing notice ---------------------------------------------------
9;;;
e0808c47 10;;; This file is part of the Sensible Object Design, an object system for C.
dea4d055
MW
11;;;
12;;; SOD is free software; you can redistribute it and/or modify
13;;; it under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 2 of the License, or
15;;; (at your option) any later version.
16;;;
17;;; SOD is distributed in the hope that it will be useful,
18;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with SOD; if not, write to the Free Software Foundation,
24;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26(cl:in-package #:sod)
27
28;;;--------------------------------------------------------------------------
29;;; Classes.
30
dea4d055
MW
31(defmethod shared-initialize :after ((class sod-class) slot-names &key pset)
32 "Specific behaviour for SOD class initialization.
33
34 Properties inspected are as follows:
35
36 * `:metaclass' names the metaclass to use. If unspecified, nil is
37 stored, and (unless you intervene later) `guess-metaclass' will be
38 called by `finalize-sod-class' to find a suitable default.
39
40 * `:nick' provides a nickname for the class. If unspecified, a default
41 (the class's name, forced to lowercase) will be chosen in
42 `finalize-sod-class'.
43
44 * `:link' names the chained superclass. If unspecified, this class will
45 be left at the head of its chain."
46
47 ;; If no nickname, copy the class name. It won't be pretty, though.
48 (default-slot-from-property (class 'nickname slot-names)
49 (pset :nick :id)
50 (string-downcase (slot-value class 'name)))
51
981b6fb6
MW
52 ;; Set the metaclass if the appropriate property has been provided;
53 ;; otherwise leave it unbound for now, and we'll sort out the mess during
54 ;; finalization.
dea4d055 55 (default-slot-from-property (class 'metaclass slot-names)
981b6fb6 56 (pset :metaclass :id meta (find-sod-class meta)))
dea4d055
MW
57
58 ;; If no chain-link, then start a new chain here.
59 (default-slot-from-property (class 'chain-link slot-names)
60 (pset :link :id link (find-sod-class link))
61 nil))
62
63;;;--------------------------------------------------------------------------
64;;; Slots.
65
66(defmethod make-sod-slot
67 ((class sod-class) name type pset &optional location)
68 (with-default-error-location (location)
eeb8cc3f
MW
69 (when (typep type 'c-function-type)
70 (error "Slot declarations cannot have function type"))
52a79ab8 71 (let ((slot (make-instance (get-property pset :slot-class :symbol
dea4d055
MW
72 'sod-slot)
73 :class class
74 :name name
75 :type type
76 :location (file-location location)
b2983f35
MW
77 :pset pset))
78 (initarg-name (get-property pset :initarg :id)))
dea4d055 79 (with-slots (slots) class
2e1a785d 80 (setf slots (append slots (list slot))))
b2983f35
MW
81 (when initarg-name
82 (make-sod-slot-initarg-using-slot class initarg-name
83 slot pset location))
2e1a785d 84 slot)))
dea4d055
MW
85
86(defmethod shared-initialize :after ((slot sod-slot) slot-names &key pset)
87 "This method does nothing.
88
89 It only exists so that it isn't an error to provide a `:pset' initarg
90 to (make-instance 'sod-slot ...)."
91
92 (declare (ignore slot-names pset)))
93
94;;;--------------------------------------------------------------------------
95;;; Slot initializers.
96
97(defmethod make-sod-instance-initializer
a888e3ac 98 ((class sod-class) nick name value pset &optional location)
dea4d055
MW
99 (with-default-error-location (location)
100 (let* ((slot (find-instance-slot-by-name class nick name))
b2983f35 101 (initarg-name (get-property pset :initarg :id))
a888e3ac
MW
102 (initializer (and value
103 (make-sod-initializer-using-slot
104 class slot 'sod-instance-initializer
105 value pset (file-location location)))))
dea4d055 106 (with-slots (instance-initializers) class
b2983f35
MW
107 (unless (or initarg-name initializer)
108 (error "Slot initializer declaration with no effect"))
109 (when initarg-name
110 (make-sod-slot-initarg-using-slot class initarg-name slot
111 pset location))
112 (when initializer
113 (setf instance-initializers
114 (append instance-initializers (list initializer)))))
2e1a785d 115 initializer)))
dea4d055
MW
116
117(defmethod make-sod-class-initializer
a888e3ac 118 ((class sod-class) nick name value pset &optional location)
dea4d055
MW
119 (with-default-error-location (location)
120 (let* ((slot (find-class-slot-by-name class nick name))
121 (initializer (make-sod-initializer-using-slot
122 class slot 'sod-class-initializer
a888e3ac 123 value pset (file-location location))))
dea4d055
MW
124 (with-slots (class-initializers) class
125 (setf class-initializers
2e1a785d
MW
126 (append class-initializers (list initializer))))
127 initializer)))
dea4d055
MW
128
129(defmethod make-sod-initializer-using-slot
a888e3ac 130 ((class sod-class) (slot sod-slot) init-class value pset location)
52a79ab8 131 (make-instance (get-property pset :initializer-class :symbol init-class)
dea4d055
MW
132 :class class
133 :slot slot
a888e3ac 134 :value value
29ad689c 135 :location (file-location location)
dea4d055
MW
136 :pset pset))
137
138(defmethod shared-initialize :after
139 ((init sod-initializer) slot-names &key pset)
140 "This method does nothing.
141
142 It only exists so that it isn't an error to provide a `:pset' initarg
143 to (make-instance 'sod-initializer ...)."
144 (declare (ignore slot-names pset))
145 nil)
146
b2983f35
MW
147(defmethod make-sod-user-initarg
148 ((class sod-class) name type pset &optional default location)
149 (declare (ignore pset))
150 (with-slots (initargs) class
151 (push (make-instance 'sod-user-initarg :location (file-location location)
152 :class class :name name :type type :default default)
153 initargs)))
154
155(defmethod make-sod-slot-initarg
156 ((class sod-class) name nick slot-name pset &optional location)
157 (let ((slot (find-instance-slot-by-name class nick slot-name)))
158 (make-sod-slot-initarg-using-slot class name slot pset location)))
159
160(defmethod make-sod-slot-initarg-using-slot
161 ((class sod-class) name (slot sod-slot) pset &optional location)
162 (declare (ignore pset))
163 (with-slots (initargs) class
164 (with-slots ((type %type)) slot
165 (push (make-instance 'sod-slot-initarg
166 :location (file-location location)
167 :class class :name name :type type :slot slot)
168 initargs))))
169
170(defmethod sod-initarg-default ((initarg sod-initarg)) nil)
171
172(defmethod sod-initarg-argument ((initarg sod-initarg))
173 (make-argument (sod-initarg-name initarg)
174 (sod-initarg-type initarg)
175 (sod-initarg-default initarg)))
176
a42893dd
MW
177;;;--------------------------------------------------------------------------
178;;; Initialization and teardown fragments.
179
180(defmethod make-sod-class-initfrag
181 ((class sod-class) frag pset &optional location)
182 (declare (ignore pset location))
183 (with-slots (initfrags) class
184 (setf initfrags (append initfrags (list frag)))))
185
186(defmethod make-sod-class-tearfrag
187 ((class sod-class) frag pset &optional location)
188 (declare (ignore pset location))
189 (with-slots (tearfrags) class
190 (setf tearfrags (append tearfrags (list frag)))))
191
dea4d055
MW
192;;;--------------------------------------------------------------------------
193;;; Messages.
194
195(defmethod make-sod-message
196 ((class sod-class) name type pset &optional location)
197 (with-default-error-location (location)
d145f6df
MW
198 (let* ((msg-class (or (get-property pset :message-class :symbol)
199 (and (get-property pset :combination :keyword)
200 'aggregating-message)
201 'standard-message))
202 (message (make-instance msg-class
203 :class class
204 :name name
205 :type type
206 :location (file-location location)
207 :pset pset)))
dea4d055 208 (with-slots (messages) class
2e1a785d
MW
209 (setf messages (append messages (list message))))
210 message)))
dea4d055
MW
211
212(defmethod shared-initialize :after
213 ((message sod-message) slot-names &key pset)
214 (declare (ignore slot-names pset))
4b8e5c03 215 (with-slots ((type %type)) message
dea4d055
MW
216 (check-message-type message type)))
217
218(defmethod check-message-type ((message sod-message) (type c-function-type))
219 nil)
220
221(defmethod check-message-type ((message sod-message) (type c-type))
222 (error "Messages must have function type, not ~A" type))
223
224;;;--------------------------------------------------------------------------
225;;; Methods.
226
227(defmethod make-sod-method
228 ((class sod-class) nick name type body pset &optional location)
229 (with-default-error-location (location)
230 (let* ((message (find-message-by-name class nick name))
231 (method (make-sod-method-using-message message class
232 type body pset
233 (file-location location))))
234 (with-slots (methods) class
2e1a785d
MW
235 (setf methods (append methods (list method))))
236 method)))
dea4d055
MW
237
238(defmethod make-sod-method-using-message
239 ((message sod-message) (class sod-class) type body pset location)
52a79ab8 240 (make-instance (or (get-property pset :method-class :symbol)
dea4d055
MW
241 (sod-message-method-class message class pset))
242 :message message
243 :class class
244 :type type
245 :body body
29ad689c 246 :location (file-location location)
dea4d055
MW
247 :pset pset))
248
249(defmethod sod-message-method-class
250 ((message sod-message) (class sod-class) pset)
251 (declare (ignore pset))
252 'sod-method)
253
254(defmethod shared-initialize :after
255 ((method sod-method) slot-names &key pset)
256 (declare (ignore slot-names pset))
257
258 ;; Check that the arguments are named if we have a method body.
4b8e5c03 259 (with-slots (body (type %type)) method
dea4d055 260 (unless (or (not body)
9ec578d9 261 (every (lambda (arg)
c07860af
MW
262 (or (eq arg :ellipsis)
263 (argument-name arg)
e85df3ff
MW
264 (c-type-equal-p (argument-type arg)
265 c-type-void)))
9ec578d9 266 (c-function-arguments type)))
dea4d055
MW
267 (error "Abstract declarators not permitted in method definitions")))
268
269 ;; Check the method type.
4b8e5c03 270 (with-slots (message (type %type)) method
dea4d055
MW
271 (check-method-type method message type)))
272
273(defmethod check-method-type
274 ((method sod-method) (message sod-message) (type c-type))
275 (error "Methods must have function type, not ~A" type))
276
b70cb6d8
MW
277(export 'check-method-return-type)
278(defun check-method-return-type (method-type wanted-type)
279 "Signal an error unless METHOD-TYPE does not return the WANTED-TYPE."
280 (let ((method-returns (c-type-subtype method-type)))
281 (unless (c-type-equal-p method-returns wanted-type)
282 (error "Method return type ~A should be ~A"
283 method-returns wanted-type))))
284
285(export 'check-method-return-type-against-message)
286(defun check-method-return-type-against-message (method-type message-type)
287 "Signal an error unless METHOD-TYPE and MESSAGE-TYPE return the same type."
288 (let ((message-returns (c-type-subtype message-type))
289 (method-returns (c-type-subtype method-type)))
290 (unless (c-type-equal-p message-returns method-returns)
291 (error "Method return type ~A doesn't match message ~A"
292 method-returns message-returns))))
293
294(export 'check-method-argument-lists)
295(defun check-method-argument-lists (method-type message-type)
296 "Signal an error unless METHOD-TYPE and MESSAGE-TYPE have matching argument
297 lists.
298
43073476
MW
299 This checks that (a) the two types have matching lists of mandatory
300 arguments, and (b) that either both or neither types accept keyword
301 arguments."
302 (let ((message-keywords-p (typep message-type 'c-keyword-function-type))
303 (method-keywords-p (typep method-type 'c-keyword-function-type)))
304 (cond (message-keywords-p
305 (unless method-keywords-p
306 (error "Method must declare a keyword argument list")))
307 (method-keywords-p
308 (error "Method must not declare a keyword argument list"))))
b70cb6d8
MW
309 (unless (argument-lists-compatible-p (c-function-arguments message-type)
310 (c-function-arguments method-type))
311 (error "Method arguments ~A don't match message ~A"
312 method-type message-type)))
313
dea4d055
MW
314(defmethod check-method-type
315 ((method sod-method) (message sod-message) (type c-function-type))
4b8e5c03 316 (with-slots ((msgtype %type)) message
b70cb6d8
MW
317 (check-method-return-type-against-message type msgtype)
318 (check-method-argument-lists type msgtype)))
dea4d055
MW
319
320;;;----- That's all, folks --------------------------------------------------