From 1655b6e2bbfe7a883694183515b2a8be6b262f5b Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 19 May 2020 07:32:28 -0600 Subject: [PATCH] ffi/unsafe: match `i`, `o`, or `io` as symbols Changes `_ptr`, `_list`, and `_vector` syntax to be more permissive. Matching by symbol rather than binding is usually the wrong choice, but the position where `i`, `o`, or `io` is recognized in `_ptr` and company is not an expression position. Also, `i`, `o`, and `io` are not otherwise bound. So, it's difficult to justify the current behavior. If `_ptr` and company were defined today, we'd use keywords like `#:i` instead of symbols like `i`. A potential drawback of this change is that a `i`, `o`, or `io` might be written with the intent of referring to a binding, and this change makes such a reference silently allwowed instead of an errror. That seems relatively unlikely, however, while having to avoid `i`, `o`, or `io` as a binding is occassionally a pain --- something I've run into once or maybe twice. Closes #887 --- .../racket-doc/scribblings/foreign/types.scrbl | 18 ++++++++++++++---- .../tests/racket/foreign-test.rktl | 16 ++++++++++++++++ racket/collects/ffi/unsafe.rkt | 12 +++++++++--- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/pkgs/racket-doc/scribblings/foreign/types.scrbl b/pkgs/racket-doc/scribblings/foreign/types.scrbl index 5e671355f0..8f20fa5eb3 100644 --- a/pkgs/racket-doc/scribblings/foreign/types.scrbl +++ b/pkgs/racket-doc/scribblings/foreign/types.scrbl @@ -1016,7 +1016,7 @@ Examples: Creates a C pointer type, where @racket[mode] indicates input or output pointers (or both). The @racket[mode] can be one of the -following: +following (matched as a symbol independent of binding): @itemize[ @@ -1058,7 +1058,11 @@ following type: creates a function that calls the foreign function with a fresh integer pointer, and use the value that is placed there as a second -return value.} +return value. + +@history[#:changed "7.7.0.6" @elem{The modes @racket[i], @racket[o], + and @racket[io] match as symbols + instead of free identifiers.}]} @defform[(_box type)]{ @@ -1124,7 +1128,10 @@ return two values, the vector and the boolean. -> (values vec res)) ] -@history[#:changed "7.7.0.2" @elem{Added @racket[maybe-mode].}]} +@history[#:changed "7.7.0.2" @elem{Added @racket[maybe-mode].}] + #:changed "7.7.0.6" @elem{The modes @racket[i], @racket[o], + and @racket[io] match as symbols + instead of free identifiers.}]} @defform[(_vector mode type maybe-len maybe-mode)]{ @@ -1145,7 +1152,10 @@ Examples: See @racket[_list] for more explanation about the examples. -@history[#:changed "7.7.0.2" @elem{Added @racket[maybe-mode].}]} +@history[#:changed "7.7.0.2" @elem{Added @racket[maybe-mode].} + #:changed "7.7.0.6" @elem{The modes @racket[i], @racket[o], + and @racket[io] match as symbols + instead of free identifiers.}]} @defform*[#:id _bytes diff --git a/pkgs/racket-test-core/tests/racket/foreign-test.rktl b/pkgs/racket-test-core/tests/racket/foreign-test.rktl index 4d1884e271..632059fb0b 100644 --- a/pkgs/racket-test-core/tests/racket/foreign-test.rktl +++ b/pkgs/racket-test-core/tests/racket/foreign-test.rktl @@ -1145,6 +1145,22 @@ (define-serializable-cstruct _serializable-example-1 ([a _int])) (test 17 serializable-example-1-a (deserialize (serialize (make-serializable-example-1 17)))) +;; ---------------------------------------- +;; Check that `i`, `o`, and `io` are matched as symbols, not by binding: + +(let ([i 'no] + [o 'no] + [io 'no]) + (test #t ctype? (_ptr i _int)) + (test #t ctype? (_ptr o _int)) + (test #t ctype? (_ptr io _int)) + (test #t ctype? (_list i _int)) + (test #t ctype? (_list o _int 10)) + (test #t ctype? (_list io _int 10)) + (test #t ctype? (_vector i _int)) + (test #t ctype? (_vector o _int 10)) + (test #t ctype? (_vector io _int 10))) + ;; ---------------------------------------- (define-cpointer-type _foo) diff --git a/racket/collects/ffi/unsafe.rkt b/racket/collects/ffi/unsafe.rkt index 7c44a712dc..de34f80289 100644 --- a/racket/collects/ffi/unsafe.rkt +++ b/racket/collects/ffi/unsafe.rkt @@ -1054,13 +1054,19 @@ (define-fun-syntax _? (syntax-id-rules () [(_ . xs) ((type: #f) . xs)] [_ (type: #f)])) +(begin-for-syntax + (define-syntax-rule (syntax-rules/symbol-literals (lit ...) [pat tmpl] ...) + (lambda (stx) + (syntax-case* stx (lit ...) (lambda (a b) (eq? (syntax-e a) (syntax-e b))) + [pat (syntax-protect (syntax/loc stx tmpl))] ...)))) + ;; (_ptr ) ;; This is for pointers, where mode indicates input or output pointers (or ;; both). If the mode is `o' (output), then the wrapper will not get an ;; argument for it, instead it generates the matching argument. (provide _ptr) (define-fun-syntax _ptr - (syntax-rules (i o io) + (syntax-rules/symbol-literals (i o io) [(_ i t) (type: _pointer pre: (x => (let ([p (malloc t)]) (ptr-set! p t x) p)))] [(_ o t) (type: _pointer @@ -1090,7 +1096,7 @@ ;; the C function will most likely require. (provide _list) (define-fun-syntax _list - (syntax-rules (i o io) + (syntax-rules/symbol-literals (i o io) [(_ i t ) (type: _pointer pre: (x => (list->cblock x t)))] [(_ i t mode) (type: _pointer @@ -1112,7 +1118,7 @@ ;; Same as _list, except that it uses Scheme vectors. (provide _vector) (define-fun-syntax _vector - (syntax-rules (i o io) + (syntax-rules/symbol-literals (i o io) [(_ i t ) (type: _pointer pre: (x => (vector->cblock x t)))] [(_ i t mode) (type: _pointer