;;; comp-tests.el --- unit tests for src/comp.c -*- lexical-binding: t; -*- ;; Copyright (C) 2019-2021 Free Software Foundation, Inc. ;; Author: Andrea Corallo ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; Unit tests for src/comp.c. ;;; Code: (require 'ert) (require 'ert-x) (require 'cl-lib) (defconst comp-test-src (ert-resource-file "comp-test-funcs.el")) (defconst comp-test-dyn-src (ert-resource-file "comp-test-funcs-dyn.el")) (when (featurep 'native-compile) (require 'comp) (message "Compiling tests...") (load (native-compile comp-test-src)) (load (native-compile comp-test-dyn-src))) (defmacro comp-deftest (name args &rest docstring-and-body) "Define a test for the native compiler tagging it as :nativecomp." (declare (indent defun) (doc-string 3)) `(ert-deftest ,(intern (concat "comp-tests-" (symbol-name name))) ,args :tags '(:nativecomp) ,@docstring-and-body)) (ert-deftest comp-tests-bootstrap () "Compile the compiler and load it to compile it-self. Check that the resulting binaries do not differ." :tags '(:expensive-test :nativecomp) (ert-with-temp-file comp1-src :suffix "-comp-stage1.el" (ert-with-temp-file comp2-src :suffix "-comp-stage2.el" (let* ((byte+native-compile t) ; FIXME HACK (comp-src (expand-file-name "../../../lisp/emacs-lisp/comp.el" (ert-resource-directory))) ;; Can't use debug symbols. (native-comp-debug 0)) (copy-file comp-src comp1-src t) (copy-file comp-src comp2-src t) (let ((load-no-native t)) (load (concat comp-src "c") nil nil t t)) (should-not (subr-native-elisp-p (symbol-function #'native-compile))) (message "Compiling stage1...") (let* ((t0 (current-time)) (comp1-eln (native-compile comp1-src))) (message "Done in %d secs" (float-time (time-since t0))) (load comp1-eln nil nil t t) (should (subr-native-elisp-p (symbol-function 'native-compile))) (message "Compiling stage2...") (let ((t0 (current-time)) (comp2-eln (native-compile comp2-src))) (message "Done in %d secs" (float-time (time-since t0))) (message "Comparing %s %s" comp1-eln comp2-eln) (should (= (call-process "cmp" nil nil nil comp1-eln comp2-eln) 0)))))))) (comp-deftest provide () "Testing top level provide." (should (featurep 'comp-test-funcs))) (comp-deftest varref () "Testing varref." (should (= (comp-tests-varref-f) 3))) (comp-deftest list () "Testing cons car cdr." (should (equal (comp-tests-list-f) '(1 2 3))) (should (equal (comp-tests-list2-f 1 2 3) '(1 2 3))) (should (= (comp-tests-car-f '(1 . 2)) 1)) (should (null (comp-tests-car-f nil))) (should-error (comp-tests-car-f 3) :type 'wrong-type-argument) (should (= (comp-tests-cdr-f '(1 . 2)) 2)) (should (null (comp-tests-cdr-f nil))) (should-error (comp-tests-cdr-f 3) :type 'wrong-type-argument) (should (= (comp-tests-car-safe-f '(1 . 2)) 1)) (should (null (comp-tests-car-safe-f 'a))) (should (= (comp-tests-cdr-safe-f '(1 . 2)) 2)) (should (null (comp-tests-cdr-safe-f 'a)))) (comp-deftest comp-tests-cons-car-cdr () "Testing cons car cdr." (should (= (comp-tests-cons-car-f) 1)) (should (= (comp-tests-cons-cdr-f 3) 3))) (comp-deftest varset () "Testing varset." (comp-tests-varset0-f) (should (= comp-tests-var1 55)) (should (= (comp-tests-varset1-f) 4)) (should (= comp-tests-var1 66))) (comp-deftest length () "Testing length." (should (= (comp-tests-length-f) 3))) (comp-deftest aref-aset () "Testing aref and aset." (should (= (comp-tests-aref-aset-f) 100))) (comp-deftest symbol-value () "Testing aref and aset." (should (= (comp-tests-symbol-value-f) 3))) (comp-deftest concat () "Testing concatX opcodes." (should (string= (comp-tests-concat-f "bar") "abcdabcabfoobar"))) (comp-deftest ffuncall () "Test calling conventions." ;; (defun comp-tests-ffuncall-caller-f () ;; (comp-tests-ffuncall-callee-f 1 2 3)) ;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3))) ;; ;; After it gets compiled ;; (native-compile #'comp-tests-ffuncall-callee-f) ;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3))) ;; ;; Recompiling the caller once with callee already compiled ;; (defun comp-tests-ffuncall-caller-f () ;; (comp-tests-ffuncall-callee-f 1 2 3)) ;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3))) (should (equal (comp-tests-ffuncall-callee-optional-f 1 2 3 4) '(1 2 3 4))) (should (equal (comp-tests-ffuncall-callee-optional-f 1 2 3) '(1 2 3 nil))) (should (equal (comp-tests-ffuncall-callee-optional-f 1 2) '(1 2 nil nil))) (should (equal (comp-tests-ffuncall-callee-rest-f 1 2) '(1 2 nil))) (should (equal (comp-tests-ffuncall-callee-rest-f 1 2 3) '(1 2 (3)))) (should (equal (comp-tests-ffuncall-callee-rest-f 1 2 3 4) '(1 2 (3 4)))) (should (equal (comp-tests-ffuncall-callee-more8-f 1 2 3 4 5 6 7 8 9 10) '(1 2 3 4 5 6 7 8 9 10))) (should (equal (comp-tests-ffuncall-callee-more8-rest-f 1 2 3 4 5 6 7 8 9 10 11) '(1 2 3 4 5 6 7 8 9 (10 11)))) (should (equal (comp-tests-ffuncall-native-f) [nil])) (should (equal (comp-tests-ffuncall-native-rest-f) [1 2 3])) (should (equal (comp-tests-ffuncall-apply-many-f '(1 2 3)) '(1 2 3))) (should (= (comp-tests-ffuncall-lambda-f 1) 2))) (comp-deftest jump-table () "Testing jump tables" (should (eq (comp-tests-jump-table-1-f 'x) 'a)) (should (eq (comp-tests-jump-table-1-f 'y) 'b)) (should (eq (comp-tests-jump-table-1-f 'xxx) 'c)) ;; Jump table not with eq as test (should (eq (comp-tests-jump-table-2-f "aaa") 'a)) (should (eq (comp-tests-jump-table-2-f "bbb") 'b))) (comp-deftest conditionals () "Testing conditionals." (should (= (comp-tests-conditionals-1-f t) 1)) (should (= (comp-tests-conditionals-1-f nil) 2)) (should (= (comp-tests-conditionals-2-f t) 1340)) (should (eq (comp-tests-conditionals-2-f nil) nil))) (comp-deftest fixnum () "Testing some fixnum inline operation." (should (= (comp-tests-fixnum-1-minus-f 10) 9)) (should (= (comp-tests-fixnum-1-minus-f most-negative-fixnum) (1- most-negative-fixnum))) (should-error (comp-tests-fixnum-1-minus-f 'a) :type 'wrong-type-argument) (should (= (comp-tests-fixnum-1-plus-f 10) 11)) (should (= (comp-tests-fixnum-1-plus-f most-positive-fixnum) (1+ most-positive-fixnum))) (should-error (comp-tests-fixnum-1-plus-f 'a) :type 'wrong-type-argument) (should (= (comp-tests-fixnum-minus-f 10) -10)) (should (= (comp-tests-fixnum-minus-f most-negative-fixnum) (- most-negative-fixnum))) (should-error (comp-tests-fixnum-minus-f 'a) :type 'wrong-type-argument)) (comp-deftest type-hints () "Just test compiler hints are transparent in this case." ;; FIXME we should really check they are also effective. (should (= (comp-tests-hint-fixnum-f 3) 4)) (should (= (comp-tests-hint-cons-f (cons 1 2)) 1))) (comp-deftest arith-comp () "Testing arithmetic comparisons." (should (eq (comp-tests-eqlsign-f 4 3) nil)) (should (eq (comp-tests-eqlsign-f 3 3) t)) (should (eq (comp-tests-eqlsign-f 2 3) nil)) (should (eq (comp-tests-gtr-f 4 3) t)) (should (eq (comp-tests-gtr-f 3 3) nil)) (should (eq (comp-tests-gtr-f 2 3) nil)) (should (eq (comp-tests-lss-f 4 3) nil)) (should (eq (comp-tests-lss-f 3 3) nil)) (should (eq (comp-tests-lss-f 2 3) t)) (should (eq (comp-tests-les-f 4 3) nil)) (should (eq (comp-tests-les-f 3 3) t)) (should (eq (comp-tests-les-f 2 3) t)) (should (eq (comp-tests-geq-f 4 3) t)) (should (eq (comp-tests-geq-f 3 3) t)) (should (eq (comp-tests-geq-f 2 3) nil))) (comp-deftest setcarcdr () "Testing setcar setcdr." (should (equal (comp-tests-setcar-f '(10 . 10) 3) '(3 . 10))) (should (equal (comp-tests-setcdr-f '(10 . 10) 3) '(10 . 3))) (should-error (comp-tests-setcar-f 3 10) :type 'wrong-type-argument) (should-error (comp-tests-setcdr-f 3 10) :type 'wrong-type-argument)) (comp-deftest bubble-sort () "Run bubble sort." (let* ((list1 (mapcar #'random (make-list 1000 most-positive-fixnum))) (list2 (copy-sequence list1))) (should (equal (comp-bubble-sort-f list1) (sort list2 #'<))))) (comp-deftest apply () "Test some inlined list functions." (should (eq (comp-tests-consp-f '(1)) t)) (should (eq (comp-tests-consp-f 1) nil)) (let ((x (cons 1 2))) (should (= (comp-tests-setcar2-f x) 3)) (should (equal x '(3 . 2))))) (comp-deftest num-inline () "Test some inlined number functions." (should (eq (comp-tests-integerp-f 1) t)) (should (eq (comp-tests-integerp-f '(1)) nil)) (should (eq (comp-tests-integerp-f 3.5) nil)) (should (eq (comp-tests-integerp-f (1+ most-negative-fixnum)) t)) (should (eq (comp-tests-numberp-f 1) t)) (should (eq (comp-tests-numberp-f 'a) nil)) (should (eq (comp-tests-numberp-f 3.5) t))) (comp-deftest stack () "Test some stack operation." (should (= (comp-tests-discardn-f 10) 2)) (should (string= (with-temp-buffer (comp-tests-insertn-f "a" "b" "c" "d") (buffer-string)) "abcd"))) (comp-deftest non-locals () "Test non locals." (should (string= (comp-tests-condition-case-0-f) "arith-error Arithmetic error catched")) (should (string= (comp-tests-condition-case-1-f) "error Foo catched")) (should (= (comp-tests-catch-f (lambda () (throw 'foo 3))) 3)) (should (= (catch 'foo (comp-tests-throw-f 3))))) (comp-deftest gc () "Try to do some longer computation to let the GC kick in." (dotimes (_ 100000) (comp-tests-cons-cdr-f 3)) (should (= (comp-tests-cons-cdr-f 3) 3))) (comp-deftest buffer () (should (string= (comp-tests-buff0-f) "foo"))) (comp-deftest lambda-return () (let ((f (comp-tests-lambda-return-f))) (should (subr-native-elisp-p f)) (should (= (funcall f 3) 4)))) (comp-deftest recursive () (should (= (comp-tests-fib-f 10) 55))) (comp-deftest macro () "Just check we can define macros" (should (macrop (symbol-function 'comp-tests-macro-m)))) (comp-deftest string-trim () (should (string= (comp-tests-string-trim-f "dsaf ") "dsaf"))) (comp-deftest trampoline-removal () ;; This tests that we can call primitives with no dedicated bytecode. ;; At speed >= 2 the trampoline will not be used. (should (hash-table-p (comp-tests-trampoline-removal-f)))) (comp-deftest signal () (should (equal (condition-case err (comp-tests-signal-f) (t err)) '(foo . t)))) (comp-deftest func-call-removal () ;; See `comp-propagate-insn' `comp-function-call-remove'. (should (= (comp-tests-func-call-removal-f) 1))) (comp-deftest doc () (should (string= (documentation #'comp-tests-doc-f) "A nice docstring.")) ;; Check a preloaded function, we can't use `comp-tests-doc-f' now ;; as this is loaded manually with no .elc. (should (string-match "\\.*.elc\\'" (symbol-file #'error)))) (comp-deftest interactive-form () (should (equal (interactive-form #'comp-test-interactive-form0-f) '(interactive "D"))) (should (equal (interactive-form #'comp-test-interactive-form1-f) '(interactive '(1 2)))) (should (equal (interactive-form #'comp-test-interactive-form2-f) '(interactive nil))) (should (cl-every #'commandp '(comp-test-interactive-form0-f comp-test-interactive-form1-f comp-test-interactive-form2-f))) (should-not (commandp #'comp-tests-doc-f))) (comp-deftest free-fun () "Check we are able to compile a single function." (eval '(defun comp-tests-free-fun-f () "Some doc." (interactive) 3) t) (native-compile #'comp-tests-free-fun-f) (should (subr-native-elisp-p (symbol-function #'comp-tests-free-fun-f))) (should (= (comp-tests-free-fun-f) 3)) (should (string= (documentation #'comp-tests-free-fun-f) "Some doc.")) (should (commandp #'comp-tests-free-fun-f)) (should (equal (interactive-form #'comp-tests-free-fun-f) '(interactive)))) (comp-deftest free-fun-silly-name () "Check we are able to compile a single function." (eval '(defun comp-tests/free\fun-f ()) t) (native-compile #'comp-tests/free\fun-f) (should (subr-native-elisp-p (symbol-function #'comp-tests/free\fun-f)))) (comp-deftest bug-40187 () "Check function name shadowing. https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-03/msg00914.html." (should (eq (comp-test-40187-1-f) 'foo)) (should (eq (comp-test-40187-2-f) 'bar))) (comp-deftest speed--1 () "Check that at speed -1 we do not native compile." (should (= (comp-test-speed--1-f) 3)) (should-not (subr-native-elisp-p (symbol-function #'comp-test-speed--1-f)))) (comp-deftest bug-42360 () "." (should (string= (comp-test-42360-f "Nel mezzo del " 18 0 32 "yyy" nil) "Nel mezzo del yyy"))) (comp-deftest bug-44968 () "" (comp-test-44968-f "/tmp/test/foo" "/tmp")) (comp-deftest bug-45342 () "Preserve multibyte immediate strings. " (should (string= " ➊" (comp-test-45342-f 1)))) (comp-deftest assume-double-neg () "In fwprop assumptions (not (not (member x))) /= (member x)." (should-not (comp-test-assume-double-neg-f "bar" "foo"))) (comp-deftest assume-in-loop-1 () "Broken call args assumptions lead to infinite loop." (should (equal (comp-test-assume-in-loop-1-f "cd") '("cd")))) (comp-deftest bug-45376-1 () "" (should (equal (comp-test-45376-1-f) '(1 0)))) (comp-deftest bug-45376-2 () "" (should (equal (comp-test-45376-2-f) '(0 2 1 0 1 0 1 0 0 0 0 0)))) (defvar comp-test-primitive-advice) (comp-deftest primitive-advice () "Test effectiveness of primitive advising." (let (comp-test-primitive-advice (f (lambda (&rest args) (setq comp-test-primitive-advice args)))) (advice-add #'+ :before f) (unwind-protect (progn (should (= (comp-test-primitive-advice-f 3 4) 7)) (should (equal comp-test-primitive-advice '(3 4)))) (advice-remove #'+ f)))) (defvar comp-test-primitive-redefine-args) (comp-deftest primitive-redefine () "Test effectiveness of primitive redefinition." (cl-letf ((comp-test-primitive-redefine-args nil) ((symbol-function #'-) (lambda (&rest args) (setq comp-test-primitive-redefine-args args) 'xxx))) (should (eq (comp-test-primitive-redefine-f 10 2) 'xxx)) (should (equal comp-test-primitive-redefine-args '(10 2))))) (comp-deftest compile-forms () "Verify lambda form native compilation." (should-error (native-compile '(+ 1 foo))) (let ((lexical-binding t) (f (native-compile '(lambda (x) (1+ x))))) (should (subr-native-elisp-p f)) (should (= (funcall f 2) 3))) (let* ((lexical-binding nil) (f (native-compile '(lambda (x) (1+ x))))) (should (subr-native-elisp-p f)) (should (= (funcall f 2) 3)))) (comp-deftest comp-test-defsubst () ;; Bug#42664, Bug#43280, Bug#44209. (should-not (subr-native-elisp-p (symbol-function #'comp-test-defsubst-f)))) (comp-deftest primitive-redefine-compile-44221 () "Test the compiler still works while primitives are redefined (bug#44221)." (cl-letf (((symbol-function #'delete-region) (lambda (_ _)))) (should (subr-native-elisp-p (native-compile '(lambda () (delete-region (point-min) (point-max)))))))) (comp-deftest and-3 () (should (= (comp-test-and-3-f t) 2)) (should (null (comp-test-and-3-f '(1 2))))) (comp-deftest copy-insn () (should (equal (comp-test-copy-insn-f '(1 2 3 (4 5 6))) '(1 2 3 (4 5 6)))) (should (null (comp-test-copy-insn-f nil)))) (comp-deftest cond-rw-1 () "Check cond-rw does not break target blocks with multiple predecessor." (should (null (comp-test-cond-rw-1-2-f)))) (comp-deftest not-cons-1 () (should-not (comp-test-not-cons-f nil))) (comp-deftest 45576-1 () "Functionp satisfies also symbols. ." (should (eq (comp-test-45576-f) 'eval))) (comp-deftest 45635-1 () "." (should (string= (comp-test-45635-f :height 180 :family "PragmataPro Liga") "PragmataPro Liga"))) (comp-deftest 45603-1 () "" (load (native-compile (ert-resource-file "comp-test-45603.el"))) (should (fboundp #'comp-test-45603--file-local-name))) (comp-deftest 46670-1 () "" (should (string= (comp-test-46670-2-f "foo") "foo")) (should (equal (subr-type (symbol-function #'comp-test-46670-2-f)) '(function (t) t)))) (comp-deftest 46824-1 () "" (should (equal (comp-test-46824-1-f) nil))) (comp-deftest comp-test-47868-1 () "Verify string hash consing strategy. " (should-not (equal-including-properties (comp-test-47868-1-f) (comp-test-47868-2-f))) (should (eq (comp-test-47868-1-f) (comp-test-47868-3-f))) (should (eq (comp-test-47868-2-f) (comp-test-47868-4-f)))) ;;;;;;;;;;;;;;;;;;;;; ;; Tromey's tests. ;; ;;;;;;;;;;;;;;;;;;;;; (comp-deftest consp () (should-not (comp-test-consp 23)) (should-not (comp-test-consp nil)) (should (comp-test-consp '(1 . 2)))) (comp-deftest listp () (should-not (comp-test-listp 23)) (should (comp-test-listp nil)) (should (comp-test-listp '(1 . 2)))) (comp-deftest stringp () (should-not (comp-test-stringp 23)) (should-not (comp-test-stringp nil)) (should (comp-test-stringp "hi"))) (comp-deftest symbolp () (should-not (comp-test-symbolp 23)) (should-not (comp-test-symbolp "hi")) (should (comp-test-symbolp 'whatever))) (comp-deftest integerp () (should (comp-test-integerp 23)) (should-not (comp-test-integerp 57.5)) (should-not (comp-test-integerp "hi")) (should-not (comp-test-integerp 'whatever))) (comp-deftest numberp () (should (comp-test-numberp 23)) (should (comp-test-numberp 57.5)) (should-not (comp-test-numberp "hi")) (should-not (comp-test-numberp 'whatever))) (comp-deftest add1 () (should (eq (comp-test-add1 23) 24)) (should (eq (comp-test-add1 -17) -16)) (should (eql (comp-test-add1 1.0) 2.0)) (should-error (comp-test-add1 nil) :type 'wrong-type-argument)) (comp-deftest sub1 () (should (eq (comp-test-sub1 23) 22)) (should (eq (comp-test-sub1 -17) -18)) (should (eql (comp-test-sub1 1.0) 0.0)) (should-error (comp-test-sub1 nil) :type 'wrong-type-argument)) (comp-deftest negate () (should (eq (comp-test-negate 23) -23)) (should (eq (comp-test-negate -17) 17)) (should (eql (comp-test-negate 1.0) -1.0)) (should-error (comp-test-negate nil) :type 'wrong-type-argument)) (comp-deftest not () (should (eq (comp-test-not 23) nil)) (should (eq (comp-test-not nil) t)) (should (eq (comp-test-not t) nil))) (comp-deftest bobp-and-eobp () (with-temp-buffer (should (comp-test-bobp)) (should (comp-test-eobp)) (insert "hi") (goto-char (point-min)) (should (eq (comp-test-point-min) (point-min))) (should (eq (comp-test-point) (point-min))) (should (comp-test-bobp)) (should-not (comp-test-eobp)) (goto-char (point-max)) (should (eq (comp-test-point-max) (point-max))) (should (eq (comp-test-point) (point-max))) (should-not (comp-test-bobp)) (should (comp-test-eobp)))) (comp-deftest car-cdr () (let ((pair '(1 . b))) (should (eq (comp-test-car pair) 1)) (should (eq (comp-test-car nil) nil)) (should-error (comp-test-car 23) :type 'wrong-type-argument) (should (eq (comp-test-cdr pair) 'b)) (should (eq (comp-test-cdr nil) nil)) (should-error (comp-test-cdr 23) :type 'wrong-type-argument))) (comp-deftest car-cdr-safe () (let ((pair '(1 . b))) (should (eq (comp-test-car-safe pair) 1)) (should (eq (comp-test-car-safe nil) nil)) (should (eq (comp-test-car-safe 23) nil)) (should (eq (comp-test-cdr-safe pair) 'b)) (should (eq (comp-test-cdr-safe nil) nil)) (should (eq (comp-test-cdr-safe 23) nil)))) (comp-deftest eq () (should (comp-test-eq 'a 'a)) (should (comp-test-eq 5 5)) (should-not (comp-test-eq 'a 'b))) (comp-deftest if () (should (eq (comp-test-if 'a 'b) 'a)) (should (eq (comp-test-if 0 23) 0)) (should (eq (comp-test-if nil 'b) 'b))) (comp-deftest and () (should (eq (comp-test-and 'a 'b) 'b)) (should (eq (comp-test-and 0 23) 23)) (should (eq (comp-test-and nil 'b) nil))) (comp-deftest or () (should (eq (comp-test-or 'a 'b) 'a)) (should (eq (comp-test-or 0 23) 0)) (should (eq (comp-test-or nil 'b) 'b))) (comp-deftest save-excursion () (with-temp-buffer (comp-test-save-excursion) (should (eq (point) (point-min))) (should (eq (comp-test-current-buffer) (current-buffer))))) (comp-deftest > () (should (eq (comp-test-> 0 23) nil)) (should (eq (comp-test-> 23 0) t))) (comp-deftest catch () (should (eq (comp-test-catch 0 1 2 3 4) nil)) (should (eq (comp-test-catch 20 21 22 23 24 25 26 27 28) 24))) (comp-deftest memq () (should (equal (comp-test-memq 0 '(5 4 3 2 1 0)) '(0))) (should (eq (comp-test-memq 72 '(5 4 3 2 1 0)) nil))) (comp-deftest listN () (should (equal (comp-test-listN 57) '(57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57)))) (comp-deftest concatN () (should (equal (comp-test-concatN "x") "xxxxxx"))) (comp-deftest opt-rest () (should (equal (comp-test-opt-rest 1) '(1 nil nil))) (should (equal (comp-test-opt-rest 1 2) '(1 2 nil))) (should (equal (comp-test-opt-rest 1 2 3) '(1 2 (3)))) (should (equal (comp-test-opt-rest 1 2 56 57 58) '(1 2 (56 57 58))))) (comp-deftest opt () (should (equal (comp-test-opt 23) '(23))) (should (equal (comp-test-opt 23 24) '(23 . 24))) (should-error (comp-test-opt) :type 'wrong-number-of-arguments) (should-error (comp-test-opt nil 24 97) :type 'wrong-number-of-arguments)) (comp-deftest unwind-protect () (comp-test-unwind-protect 'ignore) (should (eq comp-test-up-val 999)) (condition-case nil (comp-test-unwind-protect (lambda () (error "HI"))) (error nil)) (should (eq comp-test-up-val 999))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Tests for dynamic scope. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (comp-deftest dynamic-ffuncall () "Test calling convention for dynamic binding." (should (equal (comp-tests-ffuncall-callee-dyn-f 1 2) '(1 2))) (should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2 3 4) '(1 2 3 4))) (should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2 3) '(1 2 3 nil))) (should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2) '(1 2 nil nil))) (should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2) '(1 2 nil))) (should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2 3) '(1 2 (3)))) (should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2 3 4) '(1 2 (3 4)))) (should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2) '(1 2 nil nil))) (should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2 3) '(1 2 3 nil))) (should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2 3 4) '(1 2 3 (4))))) (comp-deftest dynamic-arity () "Test func-arity on dynamic scope functions." (should (equal '(2 . 2) (func-arity #'comp-tests-ffuncall-callee-dyn-f))) (should (equal '(2 . 4) (func-arity #'comp-tests-ffuncall-callee-opt-dyn-f))) (should (equal '(2 . many) (func-arity #'comp-tests-ffuncall-callee-rest-dyn-f))) (should (equal '(2 . many) (func-arity #'comp-tests-ffuncall-callee-opt-rest-dyn-f)))) (comp-deftest dynamic-help-arglist () "Test `help-function-arglist' works on lisp/d (bug#42572)." (should (equal (help-function-arglist (symbol-function #'comp-tests-ffuncall-callee-opt-rest-dyn-f) t) '(a b &optional c &rest d)))) (comp-deftest cl-macro-exp () "Verify CL macro expansion (bug#42088)." (should (equal (comp-tests-cl-macro-exp-f) '(a b)))) (comp-deftest cl-uninterned-arg-parse-f () "Verify the parsing of a lambda list with uninterned symbols (bug#42120)." (should (equal (comp-tests-cl-uninterned-arg-parse-f 1 2) '(1 2)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Middle-end specific tests. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun comp-tests-mentioned-p-1 (x insn) (cl-loop for y in insn when (cond ((consp y) (comp-tests-mentioned-p x y)) ((and (comp-mvar-p y) (comp-cstr-imm-vld-p y)) (equal (comp-cstr-imm y) x)) (t (equal x y))) return t)) (defun comp-tests-mentioned-p (x insn) "Check if X is actively mentioned in INSN." (unless (eq (car-safe insn) 'comment) (comp-tests-mentioned-p-1 x insn))) (defun comp-tests-map-checker (func-name checker) "Apply CHECKER to each insn of FUNC-NAME. Return a list of results." (cl-loop with func-c-name = (comp-c-func-name (or func-name 'anonymous-lambda) "F" t) with f = (gethash func-c-name (comp-ctxt-funcs-h comp-ctxt)) for bb being each hash-value of (comp-func-blocks f) nconc (cl-loop for insn in (comp-block-insns bb) collect (funcall checker insn)))) (defun comp-tests-tco-checker (_) "Check that inside `comp-tests-tco-f' we have no recursion." (should (cl-notany #'identity (comp-tests-map-checker 'comp-tests-tco-f (lambda (insn) (or (comp-tests-mentioned-p 'comp-tests-tco-f insn) (comp-tests-mentioned-p (comp-c-func-name 'comp-tests-tco-f "F" t) insn))))))) (comp-deftest tco () "Check for tail recursion elimination." (let ((native-comp-speed 3) ;; Disable ipa-pure otherwise `comp-tests-tco-f' gets ;; optimized-out. (comp-disabled-passes '(comp-ipa-pure)) (comp-post-pass-hooks '((comp-tco comp-tests-tco-checker) (comp-final comp-tests-tco-checker)))) (eval '(defun comp-tests-tco-f (a b count) (if (= count 0) b (comp-tests-tco-f (+ a b) a (- count 1)))) t) (native-compile #'comp-tests-tco-f) (should (subr-native-elisp-p (symbol-function #'comp-tests-tco-f))) (should (= (comp-tests-tco-f 1 0 10) 55)))) (defun comp-tests-fw-prop-checker-1 (_) "Check that inside `comp-tests-fw-prop-f' `concat' and `length' are folded." (should (cl-notany #'identity (comp-tests-map-checker 'comp-tests-fw-prop-1-f (lambda (insn) (or (comp-tests-mentioned-p 'concat insn) (comp-tests-mentioned-p 'length insn))))))) (comp-deftest fw-prop-1 () "Some tests for forward propagation." (let ((native-comp-speed 2) (comp-post-pass-hooks '((comp-final comp-tests-fw-prop-checker-1)))) (eval '(defun comp-tests-fw-prop-1-f () (let* ((a "xxx") (b "yyy") (c (concat a b))) ; <= has to optimize (length c))) ; <= has to optimize t) (native-compile #'comp-tests-fw-prop-1-f) (should (subr-native-elisp-p (symbol-function #'comp-tests-fw-prop-1-f))) (should (= (comp-tests-fw-prop-1-f) 6)))) (defun comp-tests-check-ret-type-spec (func-form ret-type) (let ((lexical-binding t) (native-comp-speed 2) (f-name (cl-second func-form))) (eval func-form t) (native-compile f-name) (should (equal (cl-third (subr-type (symbol-function f-name))) ret-type)))) (cl-eval-when (compile eval load) (defconst comp-tests-type-spec-tests `( ;; 1 ((defun comp-tests-ret-type-spec-f (x) x) t) ;; 2 ((defun comp-tests-ret-type-spec-f () 1) (integer 1 1)) ;; 3 ((defun comp-tests-ret-type-spec-f (x) (if x 1 3)) (or (integer 1 1) (integer 3 3))) ;; 4 ((defun comp-tests-ret-type-spec-f (x) (let (y) (if x (setf y 1) (setf y 2)) y)) (integer 1 2)) ;; 5 ((defun comp-tests-ret-type-spec-f (x) (let (y) (if x (setf y 1) (setf y 3)) y)) (or (integer 1 1) (integer 3 3))) ;; 6 ((defun comp-tests-ret-type-spec-f (x) (if x (list x) 3)) (or cons (integer 3 3))) ;; 7 ((defun comp-tests-ret-type-spec-f (x) (if x 'foo 3)) (or (member foo) (integer 3 3))) ;; 8 ((defun comp-tests-ret-type-spec-f (x) (if (eq x 3) x 'foo)) (or (member foo) (integer 3 3))) ;; 9 ((defun comp-tests-ret-type-spec-f (x) (if (eq 3 x) x 'foo)) (or (member foo) (integer 3 3))) ;; 10 ((defun comp-tests-ret-type-spec-f (x) (if (eql x 3) x 'foo)) (or (member foo) (integer 3 3))) ;; 11 ((defun comp-tests-ret-type-spec-f (x) (if (eql 3 x) x 'foo)) (or (member foo) (integer 3 3))) ;; 12 ((defun comp-tests-ret-type-spec-f (x) (if (eql x 3) 'foo x)) (not (integer 3 3))) ;; 13 ((defun comp-tests-ret-type-spec-f (x y) (if (= x y) x 'foo)) (or (member foo) marker number)) ;; 14 ((defun comp-tests-ret-type-spec-f (x) (comp-hint-fixnum x)) (integer ,most-negative-fixnum ,most-positive-fixnum)) ;; 15 ((defun comp-tests-ret-type-spec-f (x) (comp-hint-cons x)) cons) ;; 16 ((defun comp-tests-ret-type-spec-f (x) (let (y) (when x (setf y 4)) y)) (or null (integer 4 4))) ;; 17 ((defun comp-tests-ret-type-spec-f () (let (x (y 3)) (setf x y) y)) (integer 3 3)) ;; 18 ((defun comp-tests-ret-type-spec-f (x) (let ((y 3)) (when x (setf y x)) y)) t) ;; 19 ((defun comp-tests-ret-type-spec-f (x y) (eq x y)) boolean) ;; 20 ((defun comp-tests-ret-type-spec-f (x) (when x 'foo)) (or (member foo) null)) ;; 21 ((defun comp-tests-ret-type-spec-f (x) (unless x 'foo)) (or (member foo) null)) ;; 22 ((defun comp-tests-ret-type-spec-f (x) (when (> x 3) x)) (or null float (integer 4 *))) ;; 23 ((defun comp-tests-ret-type-spec-f (x) (when (>= x 3) x)) (or null float (integer 3 *))) ;; 24 ((defun comp-tests-ret-type-spec-f (x) (when (< x 3) x)) (or null float (integer * 2))) ;; 25 ((defun comp-tests-ret-type-spec-f (x) (when (<= x 3) x)) (or null float (integer * 3))) ;; 26 ((defun comp-tests-ret-type-spec-f (x) (when (> 3 x) x)) (or null float (integer * 2))) ;; 27 ((defun comp-tests-ret-type-spec-f (x) (when (>= 3 x) x)) (or null float (integer * 3))) ;; 28 ((defun comp-tests-ret-type-spec-f (x) (when (< 3 x) x)) (or null float (integer 4 *))) ;; 29 ((defun comp-tests-ret-type-spec-f (x) (when (<= 3 x) x)) (or null float (integer 3 *))) ;; 30 ((defun comp-tests-ret-type-spec-f (x) (let ((y 3)) (when (> x y) x))) (or null float (integer 4 *))) ;; 31 ((defun comp-tests-ret-type-spec-f (x) (let ((y 3)) (when (> y x) x))) (or null float (integer * 2))) ;; 32 ((defun comp-tests-ret-type-spec-f (x) (when (and (> x 3) (< x 10)) x)) (or null float (integer 4 9))) ;; 33 ((defun comp-tests-ret-type-spec-f (x) (when (or (> x 3) (< x 10)) x)) (or null float integer)) ;; 34 ((defun comp-tests-ret-type-spec-f (x) (when (or (< x 3) (> x 10)) x)) (or null float (integer * 2) (integer 11 *))) ;; 35 No float range support. ((defun comp-tests-ret-type-spec-f (x) (when (> x 1.0) x)) (or null marker number)) ;; 36 ((defun comp-tests-ret-type-spec-f (x y) (when (and (> x 3) (> y 2)) (+ x y))) (or null float (integer 7 *))) ;; 37 ;; SBCL: (OR REAL NULL) ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= x 3) (<= y 2)) (+ x y))) (or null float (integer * 5))) ;; 38 ((defun comp-tests-ret-type-spec-f (x y) (when (and (< 1 x 5) (< 1 y 5)) (+ x y))) (or null float (integer 4 8))) ;; 39 ;; SBCL gives: (OR REAL NULL) ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= 1 x 10) (<= 2 y 3)) (+ x y))) (or null float (integer 3 13))) ;; 40 ;; SBCL: (OR REAL NULL) ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= 1 x 10) (<= 2 y 3)) (- x y))) (or null float (integer -2 8))) ;; 41 ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= 1 x) (<= 2 y 3)) (- x y))) (or null float (integer -2 *))) ;; 42 ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= 1 x 10) (<= 2 y)) (- x y))) (or null float (integer * 8))) ;; 43 ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= x 10) (<= 2 y)) (- x y))) (or null float (integer * 8))) ;; 44 ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= x 10) (<= y 3)) (- x y))) (or null float integer)) ;; 45 ((defun comp-tests-ret-type-spec-f (x y) (when (and (<= 2 x) (<= 3 y)) (- x y))) (or null float integer)) ;; 46 ;; SBCL: (OR (RATIONAL (6) (30)) (SINGLE-FLOAT 6.0 30.0) ;; (DOUBLE-FLOAT 6.0d0 30.0d0) NULL) ((defun comp-tests-ret-type-spec-f (x y z i j k) (when (and (< 1 x 5) (< 1 y 5) (< 1 z 5) (< 1 i 5) (< 1 j 5) (< 1 k 5)) (+ x y z i j k))) (or null float (integer 12 24))) ;; 47 ((defun comp-tests-ret-type-spec-f (x) (when (<= 1 x 5) (1+ x))) (or null float (integer 2 6))) ;;48 ((defun comp-tests-ret-type-spec-f (x) (when (<= 1 x 5) (1- x))) (or null float (integer 0 4))) ;; 49 ((defun comp-tests-ret-type-spec-f () (error "Foo")) nil) ;; 50 ((defun comp-tests-ret-type-spec-f (x) (if (stringp x) x 'bar)) (or (member bar) string)) ;; 51 ((defun comp-tests-ret-type-spec-f (x) (if (stringp x) 'bar x)) (not string)) ;; 52 ((defun comp-tests-ret-type-spec-f (x) (if (integerp x) x 'bar)) (or (member bar) integer)) ;; 53 ((defun comp-tests-ret-type-spec-f (x) (when (integerp x) x)) (or null integer)) ;; 54 ((defun comp-tests-ret-type-spec-f (x) (unless (symbolp x) x)) t) ;; 55 ((defun comp-tests-ret-type-spec-f (x) (unless (integerp x) x)) (not integer)) ;; 56 ((defun comp-tests-ret-type-spec-f (x) (cl-ecase x (1 (message "one")) (5 (message "five"))) x) t ;; FIXME improve `comp-cond-cstrs-target-mvar' to cross block ;; boundary if necessary as this should return: ;; (or (integer 1 1) (integer 5 5)) ) ;; 57 ((defun comp-tests-ret-type-spec-f (x) (unless (or (eq x 'foo) (eql x 3)) (error "Not foo or 3")) x) (or (member foo) (integer 3 3))) ;;58 ((defun comp-tests-ret-type-spec-f (x y) (if (and (natnump x) (natnump y) (<= x y)) x (error ""))) (integer 0 *)) ;; 59 ((defun comp-tests-ret-type-spec-f (x y) (if (and (>= x 3) (<= y 10) (<= x y)) x (error ""))) (or float (integer 3 10))) ;; 60 ((defun comp-tests-ret-type-spec-f (x y) (if (and (<= x 10) (>= y 3) (>= x y)) x (error ""))) (or float (integer 3 10))) ;; 61 ((defun comp-tests-ret-type-spec-f (x) (if (= x 1.0) x (error ""))) (or (member 1.0) (integer 1 1))) ;; 62 ((defun comp-tests-ret-type-spec-f (x) (if (= x 1.0) x (error ""))) (or (member 1.0) (integer 1 1))) ;; 63 ((defun comp-tests-ret-type-spec-f (x) (if (= x 1.1) x (error ""))) (member 1.1)) ;; 64 ((defun comp-tests-ret-type-spec-f (x) (if (= x 1) x (error ""))) (or (member 1.0) (integer 1 1))) ;; 65 ((defun comp-tests-ret-type-spec-f (x) (if (= x 1) x (error ""))) (or (member 1.0) (integer 1 1))) ;; 66 ((defun comp-tests-ret-type-spec-f (x) (if (eql x 0.0) x (error ""))) float) ;; 67 ((defun comp-tests-ret-type-spec-f (x) (if (equal x '(1 2 3)) x (error ""))) cons) ;; 68 ((defun comp-tests-ret-type-spec-f (x) (if (and (floatp x) (= x 1)) x (error ""))) ;; Conservative (see cstr relax in `comp-cstr-='). (or (member 1.0) (integer 1 1))) ;; 69 ((defun comp-tests-ret-type-spec-f (x) (if (and (integer x) (= x 1)) x (error ""))) ;; Conservative (see cstr relax in `comp-cstr-='). (or (member 1.0) (integer 1 1))) ;; 70 ((defun comp-tests-ret-type-spec-f (x y) (if (and (floatp x) (integerp y) (= x y)) x (error ""))) (or float integer)) ;; 71 ((defun comp-tests-ret-type-spec-f (x) (if (= x 0.0) x (error ""))) (or (member -0.0 0.0) (integer 0 0))) ;; 72 ((defun comp-tests-ret-type-spec-f (x) (unless (= x 0.0) (error "")) (unless (eql x -0.0) (error "")) x) float) ;; 73 ((defun comp-tests-ret-type-spec-f (x) (when (eql x 1.0) (error "")) x) t))) (defun comp-tests-define-type-spec-test (number x) `(comp-deftest ,(intern (format "ret-type-spec-%d" number)) () ,(format "Type specifier test number %d." number) (let ((comp-ctxt (make-comp-cstr-ctxt))) (comp-tests-check-ret-type-spec ',(car x) ',(cadr x)))))) (defmacro comp-tests-define-type-spec-tests () "Define all type specifier tests." `(progn ,@(cl-loop for test in comp-tests-type-spec-tests for n from 1 collect (comp-tests-define-type-spec-test n test)))) (comp-tests-define-type-spec-tests) (defun comp-tests-pure-checker-1 (_) "Check that inside `comp-tests-pure-caller-f' `comp-tests-pure-callee-f' is folded." (should (cl-notany #'identity (comp-tests-map-checker 'comp-tests-pure-caller-f (lambda (insn) (or (comp-tests-mentioned-p 'comp-tests-pure-callee-f insn) (comp-tests-mentioned-p (comp-c-func-name 'comp-tests-pure-callee-f "F" t) insn))))))) (defun comp-tests-pure-checker-2 (_) "Check that `comp-tests-pure-fibn-f' is folded." (should (cl-notany #'identity (comp-tests-map-checker 'comp-tests-pure-fibn-entry-f (lambda (insn) (or (comp-tests-mentioned-p 'comp-tests-pure-fibn-f insn) (comp-tests-mentioned-p (comp-c-func-name 'comp-tests-pure-fibn-f "F" t) insn))))))) (comp-deftest pure () "Some tests for pure functions optimization." (let ((native-comp-speed 3) (comp-post-pass-hooks '((comp-final comp-tests-pure-checker-1 comp-tests-pure-checker-2)))) (load (native-compile (ert-resource-file "comp-test-pure.el"))) (should (subr-native-elisp-p (symbol-function #'comp-tests-pure-caller-f))) (should (= (comp-tests-pure-caller-f) 4)) (should (subr-native-elisp-p (symbol-function #'comp-tests-pure-fibn-entry-f))) (should (= (comp-tests-pure-fibn-entry-f) 6765)))) (defvar comp-tests-cond-rw-checked-function nil "Function to be checked.") (defun comp-tests-cond-rw-checker-val (_) "Check we manage to propagate the correct return value." (should (cl-some #'identity (comp-tests-map-checker comp-tests-cond-rw-checked-function (lambda (insn) (pcase insn (`(return ,mvar) (and (comp-cstr-imm-vld-p mvar) (eql (comp-cstr-imm mvar) 123))))))))) (defvar comp-tests-cond-rw-expected-type nil "Type to expect in `comp-tests-cond-rw-checker-type'.") (defun comp-tests-cond-rw-checker-type (_) "Check we manage to propagate the correct return type." (should (cl-some #'identity (comp-tests-map-checker comp-tests-cond-rw-checked-function (lambda (insn) (pcase insn (`(return ,mvar) (equal (comp-mvar-typeset mvar) comp-tests-cond-rw-expected-type)))))))) ;;; comp-tests.el ends here