From 347a323444b998be30cb5cc048474b4e83f913da Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Sat, 21 Mar 2015 15:37:33 +0800 Subject: [PATCH 01/12] RFC: Function pointers reform --- text/0000-reformed-fn-pointers.md | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 text/0000-reformed-fn-pointers.md diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md new file mode 100644 index 00000000000..2e6429f4aca --- /dev/null +++ b/text/0000-reformed-fn-pointers.md @@ -0,0 +1,90 @@ +- Feature Name: reformed_fn_pointers +- Start Date: 2015-03-21 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Make Rust's current function pointer types unsized, and introduce function reference types and new function pointer types, in order to make function references/pointers work more like value references/pointers. + +This is based on [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and related discussions, where the following design is already largely agreed upon, but the author of RFC 883 doesn't have time to revise it. Therefore, this RFC is created to push for the changes. + +# Motivation + +Currently in Rust, there are two kinds of types that represent functions: + +1. Function item types: the types of function items, `fn(arg_list) -> ret_type {foo}`s. +2. Function pointer types: the types of pointers to functions, `fn(arg_list) -> ret_type`s. + +Though called "function pointers", the function pointer types are considered *value* types (not reference/pointer types) by the language, which has the following problems: + +1. Inconsistencies in the language, especially when it comes to FFI codes that deal with nullable foreign function pointers. +2. There is currently no easy way to express the lifetime constraint on a function pointer to a non-`'static` function, which in particular makes it harder to implement type-safe JIT-ed functions/hot-loaded plugins in the future. + +Thus, this RFC proposes the following solution. + +# Detailed design + +Make the current function pointer types unsized, and introduce function reference types of the form `&fn(arg_list) -> ret_type` and new (const) function pointer types of the form `&const fn(arg_list) -> ret_type`. + +In the following section, `F`s denote function item types, `fn()`s, `&fn()`s and `*const fn()`s denote current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `arg_list -> ret_type` parts match. + +The following rules will apply: + +1. `F`s are still the function item types. +2. `fn()`s are no longer function pointer types, but unsized types representing the bodies of functions. +3. `&fn()`s are function reference types, DST pointers with "auxiliary data" of type `()`. +4. `&fn()`s and `*const fn()`s work like normal references/pointers and casting between compatible `&fn()`s and `*const fn()`s is valid. +5. There are no `&mut fn()`s, also no `*mut fn()`s. +6. `F`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). +7. `fn()`s still implement the closure traits, for keeping `&fn()`s coercible to closure trait objects. +8. `&fn()`s will implement the closure traits. +9. The `F -> fn()` coercions between compatible `F`s and `fn()`s are no longer valid. +10. The `&F -> &fn()` coercions between compatible `F`s and `fn()`s are valid as unsizing coercions. + +Notes: + +1. Currently, both `&F`s and `&fn()`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn()`s) or coercible to closures (`&F`s). +2. Source codes using `fn()`s will have to use `&fn()`s or `*const fn()`s instead. + +Optional changes that can be applied now or after Rust's final stabilization: + +1. Make `F`s zero-sized. +2. Implement `Deref` on `F`s. This enables the `*` operator on `F` values, which doesn't seem to have practical uses. However, depending on how one interprets the nature of `F`s and `F -> fn()` coercions, this can be a desirable change. This change can stress the fact that `F`s are pointer-like (they are copyable handles to function bodies, not the function bodies themselves) and they see `&F -> &fn()`s as deref coercions. + +Examples: + +```rust +fn foo() { ... } +fn unboxed_hof(f: F) { ... } +fn boxed_hof(f: &Fn()) { ... } + +let bar = foo; // still valid +let old_ptr_to_foo: fn() = foo; // currently valid, but will be invalid +let ref_to_foo: &fn() = &foo; +let ptr_to_foo = ref_to_foo as *const fn(); + +unboxed_hof(foo); // still valid +unboxed_hof(&foo); // currently invalid, but will be valid, `&foo` coerced to `&fn()`, a closure +boxed_hof(&foo); // still valid, `&foo` coerced to `&Fn()`, a closure trait object + +let nullable_ptr_to_value: *const ForeignType = ...; // for comparison +let old_nullable_ptr_to_fn: Option = ...; // currently valid, but a workaround, will be invalid +let nullable_ref_to_fn: Option<&fn()> = ...; // directly replaces the above after the changes +let nullable_ptr_to_fn: baz: *const fn() = ...; // consistent with nullable value pointers after the changes + // (currently a nullable pointer to a non-null function pointer, not to a function) +``` + +# Drawbacks + +This involves breaking changes. + +However, currently function pointers are not used much. (See [this comment](https://github.com/rust-lang/rfcs/pull/883#issuecomment-76291284) for some statistics.) + +# Alternatives + +Please see [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and related discussions for the various alternatives. + +# Unresolved questions + +None. From 70728ed1b4be04cd74ffff620f24d18132f21baa Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Sat, 21 Mar 2015 16:28:46 +0800 Subject: [PATCH 02/12] Change the shorthands for types in discussions. Also change `ForeignType` to `ValueType`. --- text/0000-reformed-fn-pointers.md | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 2e6429f4aca..e1a0b925d38 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -27,30 +27,30 @@ Thus, this RFC proposes the following solution. Make the current function pointer types unsized, and introduce function reference types of the form `&fn(arg_list) -> ret_type` and new (const) function pointer types of the form `&const fn(arg_list) -> ret_type`. -In the following section, `F`s denote function item types, `fn()`s, `&fn()`s and `*const fn()`s denote current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `arg_list -> ret_type` parts match. +In the following section, `fn{f}`s denote function item types, `fn`s, `&fn`s and `*const fn`s denote current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `arg_list -> ret_type` parts match. The following rules will apply: -1. `F`s are still the function item types. -2. `fn()`s are no longer function pointer types, but unsized types representing the bodies of functions. -3. `&fn()`s are function reference types, DST pointers with "auxiliary data" of type `()`. -4. `&fn()`s and `*const fn()`s work like normal references/pointers and casting between compatible `&fn()`s and `*const fn()`s is valid. -5. There are no `&mut fn()`s, also no `*mut fn()`s. -6. `F`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). -7. `fn()`s still implement the closure traits, for keeping `&fn()`s coercible to closure trait objects. -8. `&fn()`s will implement the closure traits. -9. The `F -> fn()` coercions between compatible `F`s and `fn()`s are no longer valid. -10. The `&F -> &fn()` coercions between compatible `F`s and `fn()`s are valid as unsizing coercions. +1. `fn{f}`s are still the function item types. +2. `fn`s are no longer function pointer types, but unsized types representing the bodies of functions. +3. `&fn`s are function reference types, DST pointers with "auxiliary data" of type `()`. +4. `&fn`s and `*const fn`s work like normal references/pointers and casting between compatible `&fn`s and `*const fn`s is valid. +5. There are no `&mut fn`s, also no `*mut fn`s. +6. `fn{f}`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). +7. `fn`s still implement the closure traits, for keeping `&fn`s coercible to closure trait objects. +8. `&fn`s will implement the closure traits. +9. The `fn{f} -> fn` coercions between compatible `fn{f}`s and `fn`s are no longer valid. +10. The `&fn{f} -> &fn` coercions between compatible `fn{f}`s and `fn`s are valid as unsizing coercions. Notes: -1. Currently, both `&F`s and `&fn()`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn()`s) or coercible to closures (`&F`s). -2. Source codes using `fn()`s will have to use `&fn()`s or `*const fn()`s instead. +1. Currently, both `&fn{f}`s and `&fn`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn`s) or coercible to closures (`&fn{f}`s). +2. Source codes using `fn`s will have to use `&fn`s or `*const fn`s instead. Optional changes that can be applied now or after Rust's final stabilization: -1. Make `F`s zero-sized. -2. Implement `Deref` on `F`s. This enables the `*` operator on `F` values, which doesn't seem to have practical uses. However, depending on how one interprets the nature of `F`s and `F -> fn()` coercions, this can be a desirable change. This change can stress the fact that `F`s are pointer-like (they are copyable handles to function bodies, not the function bodies themselves) and they see `&F -> &fn()`s as deref coercions. +1. Make `fn{f}`s zero-sized. +2. Implement `Deref` on `fn{f}`s. This enables the `*` operator on `fn{f}` values, which doesn't seem to have practical uses. However, depending on how one interprets the nature of `fn{f}`s and `fn{f} -> fn` coercions, this can be a desirable change. For some, this change can stress the fact that `fn{f}`s are pointer-like (they are copyable handles to function bodies, not the function bodies themselves) and they see `&fn{f} -> &fn`s as deref coercions. Examples: @@ -68,7 +68,7 @@ unboxed_hof(foo); // still valid unboxed_hof(&foo); // currently invalid, but will be valid, `&foo` coerced to `&fn()`, a closure boxed_hof(&foo); // still valid, `&foo` coerced to `&Fn()`, a closure trait object -let nullable_ptr_to_value: *const ForeignType = ...; // for comparison +let nullable_ptr_to_value: *const ValueType = ...; // for comparison let old_nullable_ptr_to_fn: Option = ...; // currently valid, but a workaround, will be invalid let nullable_ref_to_fn: Option<&fn()> = ...; // directly replaces the above after the changes let nullable_ptr_to_fn: baz: *const fn() = ...; // consistent with nullable value pointers after the changes From 417a06dea133fc238e4db295b46e0e7570264aa9 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Sat, 21 Mar 2015 17:38:02 +0800 Subject: [PATCH 03/12] Remove a `baz` that shouldn't be there. --- text/0000-reformed-fn-pointers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index e1a0b925d38..6ea8c55f13a 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -71,7 +71,7 @@ boxed_hof(&foo); // still valid, `&foo` coerced to `&Fn()`, a closure trait obje let nullable_ptr_to_value: *const ValueType = ...; // for comparison let old_nullable_ptr_to_fn: Option = ...; // currently valid, but a workaround, will be invalid let nullable_ref_to_fn: Option<&fn()> = ...; // directly replaces the above after the changes -let nullable_ptr_to_fn: baz: *const fn() = ...; // consistent with nullable value pointers after the changes +let nullable_ptr_to_fn: *const fn() = ...; // consistent with nullable value pointers after the changes // (currently a nullable pointer to a non-null function pointer, not to a function) ``` From 720e97f07d3d98f5db54765281834e0d2d5f4fc1 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Sat, 21 Mar 2015 19:22:40 +0800 Subject: [PATCH 04/12] Fix typos and revise the examples. --- text/0000-reformed-fn-pointers.md | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 6ea8c55f13a..608931d4433 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -25,9 +25,9 @@ Thus, this RFC proposes the following solution. # Detailed design -Make the current function pointer types unsized, and introduce function reference types of the form `&fn(arg_list) -> ret_type` and new (const) function pointer types of the form `&const fn(arg_list) -> ret_type`. +Make the current function pointer types unsized, and introduce function reference types of the form `&fn(arg_list) -> ret_type` and new (const) function pointer types of the form `*const fn(arg_list) -> ret_type`. -In the following section, `fn{f}`s denote function item types, `fn`s, `&fn`s and `*const fn`s denote current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `arg_list -> ret_type` parts match. +In the following section, `fn{f}`s, `fn`s, `&fn`s and `*const fn`s denote function item types, current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `(arg_list) -> ret_type` parts match. The following rules will apply: @@ -50,7 +50,7 @@ Notes: Optional changes that can be applied now or after Rust's final stabilization: 1. Make `fn{f}`s zero-sized. -2. Implement `Deref` on `fn{f}`s. This enables the `*` operator on `fn{f}` values, which doesn't seem to have practical uses. However, depending on how one interprets the nature of `fn{f}`s and `fn{f} -> fn` coercions, this can be a desirable change. For some, this change can stress the fact that `fn{f}`s are pointer-like (they are copyable handles to function bodies, not the function bodies themselves) and they see `&fn{f} -> &fn`s as deref coercions. +2. Implement `Deref` on `fn{f}`s. This enables the `*` operator on `fn{f}` values, which doesn't seem to have practical uses. However, depending on how one interprets the nature of `fn{f}`s and `&fn{f} -> &fn` coercions, this can be a desirable change. For some, this change can stress the fact that `fn{f}`s are pointer-like (they are copyable handles to function bodies, not the function bodies themselves) and they see `&fn{f} -> &fn`s as deref coercions. Examples: @@ -59,20 +59,24 @@ fn foo() { ... } fn unboxed_hof(f: F) { ... } fn boxed_hof(f: &Fn()) { ... } -let bar = foo; // still valid -let old_ptr_to_foo: fn() = foo; // currently valid, but will be invalid -let ref_to_foo: &fn() = &foo; -let ptr_to_foo = ref_to_foo as *const fn(); +let bar = foo; // valid and unchanged +let old_fn_ptr: fn() = foo; // currently valid, but will be invalid +let fn_ref: &fn() = &foo; // the new `&fn{f} -> &fn` coercion +let fn_ptr = fn_ref as *const fn(); // the new `&fn -> *const fn` cast -unboxed_hof(foo); // still valid +unboxed_hof(foo); // valid and unchanged unboxed_hof(&foo); // currently invalid, but will be valid, `&foo` coerced to `&fn()`, a closure -boxed_hof(&foo); // still valid, `&foo` coerced to `&Fn()`, a closure trait object +boxed_hof(foo); // invalid both before and after the changes +boxed_hof(&foo); // valid and unchanged, `&foo` coerced to `&Fn()`, a closure trait object -let nullable_ptr_to_value: *const ValueType = ...; // for comparison -let old_nullable_ptr_to_fn: Option = ...; // currently valid, but a workaround, will be invalid -let nullable_ref_to_fn: Option<&fn()> = ...; // directly replaces the above after the changes -let nullable_ptr_to_fn: *const fn() = ...; // consistent with nullable value pointers after the changes - // (currently a nullable pointer to a non-null function pointer, not to a function) +let nullable_value_ptr: *const ValueType = ...; // for comparison +let old_nullable_fn_ptr: Option = ...; // currently valid, but a workaround, will be invalid +let nullable_fn_ref: Option<&fn()> = ...; // directly replaces the above after the changes +let nullable_fn_ptr: *const fn() = ...; // consistent with nullable value pointers after the changes + +// Note: +// Some of the lines above are valid currently and their semantics will not be changed, +// but some others, while still valid, will take on new meanings. ``` # Drawbacks From 36bcc9b8d683c33f4b82a289928bd083328484a2 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Sat, 21 Mar 2015 22:47:51 +0800 Subject: [PATCH 05/12] Specify that `fn`s are dynamically zero-sized. And also reinterpret them as "incomplete function items". This change is made in order to deal with the fact that all Rust types must have dynamically determinable sizes. --- text/0000-reformed-fn-pointers.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 608931d4433..4d941c70a6c 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -31,8 +31,8 @@ In the following section, `fn{f}`s, `fn`s, `&fn`s and `*const fn`s denote functi The following rules will apply: -1. `fn{f}`s are still the function item types. -2. `fn`s are no longer function pointer types, but unsized types representing the bodies of functions. +1. `fn{f}`s are still the function item types, or "function handles/proxies/values". +2. `fn`s are no longer function pointer types, but types representing "incomplete function items", which are unsized statically and zero-sized dynamically. 3. `&fn`s are function reference types, DST pointers with "auxiliary data" of type `()`. 4. `&fn`s and `*const fn`s work like normal references/pointers and casting between compatible `&fn`s and `*const fn`s is valid. 5. There are no `&mut fn`s, also no `*mut fn`s. @@ -42,15 +42,16 @@ The following rules will apply: 9. The `fn{f} -> fn` coercions between compatible `fn{f}`s and `fn`s are no longer valid. 10. The `&fn{f} -> &fn` coercions between compatible `fn{f}`s and `fn`s are valid as unsizing coercions. +An optional change that can be applied now or after Rust's final stabilization: + +Make `fn{f}`s zero-sized statically to save space and better align with the fact that `fn`s are zero-sized dynamically. + Notes: 1. Currently, both `&fn{f}`s and `&fn`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn`s) or coercible to closures (`&fn{f}`s). 2. Source codes using `fn`s will have to use `&fn`s or `*const fn`s instead. - -Optional changes that can be applied now or after Rust's final stabilization: - -1. Make `fn{f}`s zero-sized. -2. Implement `Deref` on `fn{f}`s. This enables the `*` operator on `fn{f}` values, which doesn't seem to have practical uses. However, depending on how one interprets the nature of `fn{f}`s and `&fn{f} -> &fn` coercions, this can be a desirable change. For some, this change can stress the fact that `fn{f}`s are pointer-like (they are copyable handles to function bodies, not the function bodies themselves) and they see `&fn{f} -> &fn`s as deref coercions. +3. In previous revisions of this RFC, `fn`s were going to be interpreted as types representing "function bodies", not "incomplete function items". But then it was pointed out that every type in Rust must have dynamically determinable sizes. This means, for `fn`s, the sizes must all be the same, because `&fn`s cannot actually carry any auxiliary data, or they will not be thin pointers. The obvious special size value here is zero. It would be weird for function bodies to be considered zero-sized, so `fn`s are reinterpreted as "incomplete function items". +4. In previous revisions of this RFC, there were another optional change: implementing `Deref`s on `fn{f}`s. This were intended to stress the fact that `fn{f}`s were themselves pointer-like constructs "pointing to" function bodies. But now that `fn`s will not be interpreted as "function bodies", this optional change will make no sense. Therefore it is dropped. Examples: From 154553cd9eaae9910e1ef7407da98784c2feabc8 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Sat, 21 Mar 2015 23:15:41 +0800 Subject: [PATCH 06/12] Add an optional change to make `&fn{f}`s closures. --- text/0000-reformed-fn-pointers.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 4d941c70a6c..8a9fc518f86 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -42,13 +42,14 @@ The following rules will apply: 9. The `fn{f} -> fn` coercions between compatible `fn{f}`s and `fn`s are no longer valid. 10. The `&fn{f} -> &fn` coercions between compatible `fn{f}`s and `fn`s are valid as unsizing coercions. -An optional change that can be applied now or after Rust's final stabilization: +Optional changes that can be applied now or after Rust's final stabilization: -Make `fn{f}`s zero-sized statically to save space and better align with the fact that `fn`s are zero-sized dynamically. +1. Make `fn{f}`s zero-sized statically to save space and better align with the fact that `fn`s are zero-sized dynamically. +2. Make `&fn{f}`s implement closure traits for better symmetry between `fn{f}`s and `fn`s. Notes: -1. Currently, both `&fn{f}`s and `&fn`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn`s) or coercible to closures (`&fn{f}`s). +1. Currently, both `&fn{f}`s and `&fn`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn`s) or coercible to closures (`&fn{f}`s). If the second optional change happens, then `&fn{f}`s will also be closures, without coercions. 2. Source codes using `fn`s will have to use `&fn`s or `*const fn`s instead. 3. In previous revisions of this RFC, `fn`s were going to be interpreted as types representing "function bodies", not "incomplete function items". But then it was pointed out that every type in Rust must have dynamically determinable sizes. This means, for `fn`s, the sizes must all be the same, because `&fn`s cannot actually carry any auxiliary data, or they will not be thin pointers. The obvious special size value here is zero. It would be weird for function bodies to be considered zero-sized, so `fn`s are reinterpreted as "incomplete function items". 4. In previous revisions of this RFC, there were another optional change: implementing `Deref`s on `fn{f}`s. This were intended to stress the fact that `fn{f}`s were themselves pointer-like constructs "pointing to" function bodies. But now that `fn`s will not be interpreted as "function bodies", this optional change will make no sense. Therefore it is dropped. From ed90b31cd8faf987eb1d21a6dafbb982e3bc2d6a Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Mon, 23 Mar 2015 21:23:21 +0800 Subject: [PATCH 07/12] Various adjustments and clarifications. --- text/0000-reformed-fn-pointers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 8a9fc518f86..18bcdfad54c 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -5,7 +5,7 @@ # Summary -Make Rust's current function pointer types unsized, and introduce function reference types and new function pointer types, in order to make function references/pointers work more like value references/pointers. +Repurpose Rust's current function pointer types to denote "incomplete function items", and introduce function reference types and new function pointer types, so that function references/pointers work more like value references/pointers. This is based on [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and related discussions, where the following design is already largely agreed upon, but the author of RFC 883 doesn't have time to revise it. Therefore, this RFC is created to push for the changes. @@ -25,16 +25,16 @@ Thus, this RFC proposes the following solution. # Detailed design -Make the current function pointer types unsized, and introduce function reference types of the form `&fn(arg_list) -> ret_type` and new (const) function pointer types of the form `*const fn(arg_list) -> ret_type`. +Repurpose current function pointer types to denote "incomplete function items". Introduce function reference types of the form `&fn(arg_list) -> ret_type` and new function pointer types of the form `*const fn(arg_list) -> ret_type`. In the following section, `fn{f}`s, `fn`s, `&fn`s and `*const fn`s denote function item types, current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `(arg_list) -> ret_type` parts match. The following rules will apply: -1. `fn{f}`s are still the function item types, or "function handles/proxies/values". +1. `fn{f}`s are still function item types, or "function handles/proxies/values", whose representations and semantics remain unchanged. 2. `fn`s are no longer function pointer types, but types representing "incomplete function items", which are unsized statically and zero-sized dynamically. -3. `&fn`s are function reference types, DST pointers with "auxiliary data" of type `()`. -4. `&fn`s and `*const fn`s work like normal references/pointers and casting between compatible `&fn`s and `*const fn`s is valid. +3. `&fn`s are function reference types, DST pointers with auxiliary data of type `()`, and work like other references. +4. `*const fn`s are raw function pointer types that work as normally expected, and casting between compatible `&fn`s and `*const fn`s is valid. 5. There are no `&mut fn`s, also no `*mut fn`s. 6. `fn{f}`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). 7. `fn`s still implement the closure traits, for keeping `&fn`s coercible to closure trait objects. From 0a41fb76dd23d5217f2461e738946dfe7307bd04 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Tue, 24 Mar 2015 22:16:12 +0800 Subject: [PATCH 08/12] Major revision. --- text/0000-reformed-fn-pointers.md | 69 ++++++++++++++++++------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 18bcdfad54c..3d8b24585e8 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -5,7 +5,7 @@ # Summary -Repurpose Rust's current function pointer types to denote "incomplete function items", and introduce function reference types and new function pointer types, so that function references/pointers work more like value references/pointers. +Repurpose current function pointer types to mean "function bodies" or "incomplete function items". Introduce function reference types and new function pointer types, so that function references/pointers work more like value references/pointers. This is based on [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and related discussions, where the following design is already largely agreed upon, but the author of RFC 883 doesn't have time to revise it. Therefore, this RFC is created to push for the changes. @@ -25,34 +25,29 @@ Thus, this RFC proposes the following solution. # Detailed design -Repurpose current function pointer types to denote "incomplete function items". Introduce function reference types of the form `&fn(arg_list) -> ret_type` and new function pointer types of the form `*const fn(arg_list) -> ret_type`. +Repurpose current function pointer types to mean "function bodies". Introduce shared function reference types of the form `&fn(arg_list) -> ret_type`, and the variations: mutable function references, const function pointers, and mutable function pointers. -In the following section, `fn{f}`s, `fn`s, `&fn`s and `*const fn`s denote function item types, current function pointer types, function reference types and new function pointer types, respectively. Those types are considered "compatible" if their `(arg_list) -> ret_type` parts match. +In the following section, `fn{f}`s denote function item types, `fn`s denote current function pointer types, `&fn`s, `&mut fn`s, `*const fn`s and `*mut fn`s are function references and their variations. Those types are considered "compatible" if their `(arg_list) -> ret_type` parts match. The following rules will apply: -1. `fn{f}`s are still function item types, or "function handles/proxies/values", whose representations and semantics remain unchanged. -2. `fn`s are no longer function pointer types, but types representing "incomplete function items", which are unsized statically and zero-sized dynamically. -3. `&fn`s are function reference types, DST pointers with auxiliary data of type `()`, and work like other references. -4. `*const fn`s are raw function pointer types that work as normally expected, and casting between compatible `&fn`s and `*const fn`s is valid. -5. There are no `&mut fn`s, also no `*mut fn`s. -6. `fn{f}`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). -7. `fn`s still implement the closure traits, for keeping `&fn`s coercible to closure trait objects. -8. `&fn`s will implement the closure traits. -9. The `fn{f} -> fn` coercions between compatible `fn{f}`s and `fn`s are no longer valid. -10. The `&fn{f} -> &fn` coercions between compatible `fn{f}`s and `fn`s are valid as unsizing coercions. - -Optional changes that can be applied now or after Rust's final stabilization: - -1. Make `fn{f}`s zero-sized statically to save space and better align with the fact that `fn`s are zero-sized dynamically. -2. Make `&fn{f}`s implement closure traits for better symmetry between `fn{f}`s and `fn`s. +1. `fn{f}`s' representations and semantics remain unchanged. +2. `fn`s become unsized statically and zero sized dynamically. +3. `&fn`s are DST pointers with auxiliary data of type `()`. +4. `*const fn`s, `&mut fn`s and `*mut fn`s work as expected, though `&mut fn`s and `*mut fn`s may not have practical uses. +5. `fn{f}`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). +6. `fn`s still implement the closure traits, for keeping `&fn`s coercible to closure trait objects. +7. `&fn`s implement the closure traits, so they can be used in places expecting `fn`s currently. +8. The `fn{f} -> fn` coercions between compatible `fn{f}`s and `fn`s are no longer valid. +9. The `&fn{f} -> &fn` coercions between compatible `fn{f}`s and `fn`s are valid as unsizing coercions. +10. Optional: `&fn{f}`s can implement the closure traits for better symmetry with `&fn`. +11. Optional: `&fn{f}`s can implement `Deref` to stress the fact that `fn`s represent function bodies and `fn{f}`s are handles/proxies "to" `fn`s. Notes: -1. Currently, both `&fn{f}`s and `&fn`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn`s) or coercible to closures (`&fn{f}`s). If the second optional change happens, then `&fn{f}`s will also be closures, without coercions. -2. Source codes using `fn`s will have to use `&fn`s or `*const fn`s instead. -3. In previous revisions of this RFC, `fn`s were going to be interpreted as types representing "function bodies", not "incomplete function items". But then it was pointed out that every type in Rust must have dynamically determinable sizes. This means, for `fn`s, the sizes must all be the same, because `&fn`s cannot actually carry any auxiliary data, or they will not be thin pointers. The obvious special size value here is zero. It would be weird for function bodies to be considered zero-sized, so `fn`s are reinterpreted as "incomplete function items". -4. In previous revisions of this RFC, there were another optional change: implementing `Deref`s on `fn{f}`s. This were intended to stress the fact that `fn{f}`s were themselves pointer-like constructs "pointing to" function bodies. But now that `fn`s will not be interpreted as "function bodies", this optional change will make no sense. Therefore it is dropped. +1. Currently, both `&fn{f}`s and `&fn`s are coercible to closure trait objects, but are not closures themselves. After the changes, they will be closures (`&fn`s) or coercible to closures (`&fn{f}`s). If the first optional change happens, then `&fn{f}`s will also be closures, without coercions. +2. Source codes using `fn`s will have to use `&fn`s or `*const fn`s instead. Due to the inference rules of the language, in practice, most uses of `&fn` would be `&'static fn`. +3. It is an *implementation detail* that `fn`s are zero sized dynamically. The actual intention is for `fn`s to be "truly unsized" when the necessary language support for those types are designed and implemented. (Please see [RFC 709](https://github.com/rust-lang/rfcs/pull/709) and [RFC Issue 813](https://github.com/rust-lang/rfcs/pull/813) for discussions about truly unsized types.) Examples: @@ -73,7 +68,7 @@ boxed_hof(&foo); // valid and unchanged, `&foo` coerced to `&Fn()`, a closure tr let nullable_value_ptr: *const ValueType = ...; // for comparison let old_nullable_fn_ptr: Option = ...; // currently valid, but a workaround, will be invalid -let nullable_fn_ref: Option<&fn()> = ...; // directly replaces the above after the changes +let nullable_fn_ref: Option<&'static fn()> = ...; // directly replaces the above after the changes let nullable_fn_ptr: *const fn() = ...; // consistent with nullable value pointers after the changes // Note: @@ -83,14 +78,32 @@ let nullable_fn_ptr: *const fn() = ...; // consistent with nullable value pointe # Drawbacks -This involves breaking changes. - -However, currently function pointers are not used much. (See [this comment](https://github.com/rust-lang/rfcs/pull/883#issuecomment-76291284) for some statistics.) +1. This involves breaking changes. However, currently function pointers are not used much. (See [this comment](https://github.com/rust-lang/rfcs/pull/883#issuecomment-76291284) for some statistics.) +2. This goes down a particular path in the type system that may have unforeseen interactions. +3. In order to hide the fact that `fn`s are zero-sized dynamically, functions like `size_of_val` would not be made usable on unsized types in the near future. # Alternatives -Please see [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and related discussions for the various alternatives. +## A. Keep the status quo. + +And stick with function pointers that aren't quite function pointers. + +## B. Allow `fn{f} -> &'static fn`, not `&fn{f} -> &fn`. + +It is a bit strange that a value type can be coerced to a reference type, though `fn{f}`s are indeed pointer-like in a way. + +## B. Make function item types truly donate functions. + +This alternative makes values of the type `fn{f}`s not copyable, and only `&fn{f}`s (and variations) can be passed around. + +This has the advantage of having dedicated types for representing functions (new `fn{f}`s), and there will be no pointer-like handle types (current `fn{f}`s), only true function references/pointers (`&fn{f}`s and variations). + +However, unlike function handles, function references/pointers cannot be zero-sized. Also, if `fn{f}`s are no longer `Copy`, more code will have to be changed to use `&fn{f}`, making this alternative a much larger-scale breaking change. + +## C. Make function item types `&'static fn{f}`s. + +Like Alternative B, this also rules out the possibility of zero-sized function handle types. # Unresolved questions -None. +What exactly are the "unforeseen interactions" in Drawback 2, if any? From e79cdb4c979534156286e57279bee643031dec10 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Tue, 24 Mar 2015 22:33:33 +0800 Subject: [PATCH 09/12] Add back "incomplete function items" as an alternative. Also make some other adjustments. --- text/0000-reformed-fn-pointers.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 3d8b24585e8..b98f1f95668 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -5,7 +5,7 @@ # Summary -Repurpose current function pointer types to mean "function bodies" or "incomplete function items". Introduce function reference types and new function pointer types, so that function references/pointers work more like value references/pointers. +Repurpose current function pointer types to mean "function bodies". Introduce function reference types and new function pointer types, so that function references/pointers work more like value references/pointers. This is based on [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and related discussions, where the following design is already largely agreed upon, but the author of RFC 883 doesn't have time to revise it. Therefore, this RFC is created to push for the changes. @@ -88,11 +88,17 @@ let nullable_fn_ptr: *const fn() = ...; // consistent with nullable value pointe And stick with function pointers that aren't quite function pointers. -## B. Allow `fn{f} -> &'static fn`, not `&fn{f} -> &fn`. +## B. Make "`fn`s are dynamically zero sized" externally visible. -It is a bit strange that a value type can be coerced to a reference type, though `fn{f}`s are indeed pointer-like in a way. +And interpret `fn`s as "incomplete function items". -## B. Make function item types truly donate functions. +However, given that there are other "truly unsized" types, it is better to keep the implementation detail hidden, instead of coming up with an explanation for a special cased type that is "unsized statically but zero sized dynamically". + +## C. Allow `fn{f} -> &'static fn`, not `&fn{f} -> &fn`. + +It is a bit strange that a value type can be coerced to a reference type and the symmetry will be lost, though `fn{f}`s are indeed pointer-like in a way. + +## D. Make function item types truly denote functions. This alternative makes values of the type `fn{f}`s not copyable, and only `&fn{f}`s (and variations) can be passed around. @@ -100,9 +106,9 @@ This has the advantage of having dedicated types for representing functions (new However, unlike function handles, function references/pointers cannot be zero-sized. Also, if `fn{f}`s are no longer `Copy`, more code will have to be changed to use `&fn{f}`, making this alternative a much larger-scale breaking change. -## C. Make function item types `&'static fn{f}`s. +## E. Make function item types `&'static fn{f}`s. -Like Alternative B, this also rules out the possibility of zero-sized function handle types. +Like Alternative D, this also rules out the possibility of zero-sized function handle types. # Unresolved questions From 991f4f6e9b0b50aab3ce5b45f87c76eac2b0faa5 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Tue, 24 Mar 2015 22:39:56 +0800 Subject: [PATCH 10/12] Make the titles of alternatives smaller. --- text/0000-reformed-fn-pointers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index b98f1f95668..f9df2ad8f12 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -84,21 +84,21 @@ let nullable_fn_ptr: *const fn() = ...; // consistent with nullable value pointe # Alternatives -## A. Keep the status quo. +#### A. Keep the status quo. And stick with function pointers that aren't quite function pointers. -## B. Make "`fn`s are dynamically zero sized" externally visible. +#### B. Make "`fn`s are dynamically zero sized" externally visible. And interpret `fn`s as "incomplete function items". However, given that there are other "truly unsized" types, it is better to keep the implementation detail hidden, instead of coming up with an explanation for a special cased type that is "unsized statically but zero sized dynamically". -## C. Allow `fn{f} -> &'static fn`, not `&fn{f} -> &fn`. +#### C. Allow `fn{f} -> &'static fn`, not `&fn{f} -> &fn`. It is a bit strange that a value type can be coerced to a reference type and the symmetry will be lost, though `fn{f}`s are indeed pointer-like in a way. -## D. Make function item types truly denote functions. +#### D. Make function item types truly denote functions. This alternative makes values of the type `fn{f}`s not copyable, and only `&fn{f}`s (and variations) can be passed around. @@ -106,7 +106,7 @@ This has the advantage of having dedicated types for representing functions (new However, unlike function handles, function references/pointers cannot be zero-sized. Also, if `fn{f}`s are no longer `Copy`, more code will have to be changed to use `&fn{f}`, making this alternative a much larger-scale breaking change. -## E. Make function item types `&'static fn{f}`s. +#### E. Make function item types `&'static fn{f}`s. Like Alternative D, this also rules out the possibility of zero-sized function handle types. From 5a7fc334e87b9d9aee456f9fec51a5c5305a8667 Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Tue, 24 Mar 2015 22:44:24 +0800 Subject: [PATCH 11/12] Add `'static` to one example. --- text/0000-reformed-fn-pointers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index f9df2ad8f12..1f3fc438267 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -58,7 +58,7 @@ fn boxed_hof(f: &Fn()) { ... } let bar = foo; // valid and unchanged let old_fn_ptr: fn() = foo; // currently valid, but will be invalid -let fn_ref: &fn() = &foo; // the new `&fn{f} -> &fn` coercion +let fn_ref: &'static fn() = &foo; // the new `&fn{f} -> &fn` coercion let fn_ptr = fn_ref as *const fn(); // the new `&fn -> *const fn` cast unboxed_hof(foo); // valid and unchanged From 86591a2df9865d0ca13f68adaa8612251344bf9d Mon Sep 17 00:00:00 2001 From: Richard Zhang Date: Wed, 25 Mar 2015 20:12:36 +0800 Subject: [PATCH 12/12] More discussions about some alternatives. And some other adjustments. --- text/0000-reformed-fn-pointers.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/text/0000-reformed-fn-pointers.md b/text/0000-reformed-fn-pointers.md index 1f3fc438267..abc1e396166 100644 --- a/text/0000-reformed-fn-pointers.md +++ b/text/0000-reformed-fn-pointers.md @@ -13,7 +13,7 @@ This is based on [RFC 883](https://github.com/rust-lang/rfcs/pull/883) and relat Currently in Rust, there are two kinds of types that represent functions: -1. Function item types: the types of function items, `fn(arg_list) -> ret_type {foo}`s. +1. Function item types, or function handle/proxy/value types: the types of function items, `fn(arg_list) -> ret_type {foo}`s. 2. Function pointer types: the types of pointers to functions, `fn(arg_list) -> ret_type`s. Though called "function pointers", the function pointer types are considered *value* types (not reference/pointer types) by the language, which has the following problems: @@ -32,7 +32,7 @@ In the following section, `fn{f}`s denote function item types, `fn`s denote curr The following rules will apply: 1. `fn{f}`s' representations and semantics remain unchanged. -2. `fn`s become unsized statically and zero sized dynamically. +2. `fn`s become unsized statically and (as an implementation detail) zero sized dynamically. 3. `&fn`s are DST pointers with auxiliary data of type `()`. 4. `*const fn`s, `&mut fn`s and `*mut fn`s work as expected, though `&mut fn`s and `*mut fn`s may not have practical uses. 5. `fn{f}`s still implement the closure traits (`Fn`/`FnMut`/`FnOnce`). @@ -90,25 +90,32 @@ And stick with function pointers that aren't quite function pointers. #### B. Make "`fn`s are dynamically zero sized" externally visible. -And interpret `fn`s as "incomplete function items". +And interpret `fn`s as "incomplete function items", which aligns well with the plan to make `fn{f}`s (the "complete" function items) zero sized statically. (See [Rust Issue 19925](https://github.com/rust-lang/rust/issues/19925).) -However, given that there are other "truly unsized" types, it is better to keep the implementation detail hidden, instead of coming up with an explanation for a special cased type that is "unsized statically but zero sized dynamically". +However, it is likely that Rust will gain (other) truly unsized types one day, which is a more generally applicable solution. If possible, it is better to avoid special cases like "statically unsized but dynamically zero sized types". Thus it is better to avoid exposing the dynamic sizes of `fn`s for now. #### C. Allow `fn{f} -> &'static fn`, not `&fn{f} -> &fn`. -It is a bit strange that a value type can be coerced to a reference type and the symmetry will be lost, though `fn{f}`s are indeed pointer-like in a way. +Though `fn{f}`s are indeed pointer-like in a way, it is a bit strange that a value type can be coerced to a reference type. Also, the symmetry between `fn{f}`s and `fn`s will be lost. #### D. Make function item types truly denote functions. This alternative makes values of the type `fn{f}`s not copyable, and only `&fn{f}`s (and variations) can be passed around. -This has the advantage of having dedicated types for representing functions (new `fn{f}`s), and there will be no pointer-like handle types (current `fn{f}`s), only true function references/pointers (`&fn{f}`s and variations). +This has the advantage of having dedicated types for representing functions (new `fn{f}`s), and there will be no pointer-like handle types (current `fn{f}`s), only true function references/pointers (`&fn{f}`s and variations). This is theoretically purer. -However, unlike function handles, function references/pointers cannot be zero-sized. Also, if `fn{f}`s are no longer `Copy`, more code will have to be changed to use `&fn{f}`, making this alternative a much larger-scale breaking change. +However, function handles do have their advantages over function references/pointers: -#### E. Make function item types `&'static fn{f}`s. +1. Function handles can be zero-sized. +2. Function handles do not involve the `&` sigil which usually has something to do with indirections (not considering optimizations). The lack of `&` is a visual hint for the lack of indirection when calling functions through the handles. -Like Alternative D, this also rules out the possibility of zero-sized function handle types. +Also, if `fn{f}`s are no longer `Copy`, more code will have to be changed to use `&fn{f}`, making this alternative a much larger-scale breaking change. + +#### E. Make function item types `&'static fn{f}`s instead of `fn{f}`s. + +This alternative eliminates the pointer-like handle types just like Alternative D does, without breaking every piece of code currently taking function handles as arguments. + +This also has the pros and cons of having no function handles. # Unresolved questions