From 433fe17fce929d8a4e49ec98fecb6c66cb24e691 Mon Sep 17 00:00:00 2001 From: Loi Chyan Date: Fri, 30 May 2025 02:03:36 +0000 Subject: [PATCH 1/3] Forbit using trait aliases to implement an interface --- src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e1d712..34869de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro2::Span; use quote::{format_ident, quote}; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; -use syn::{parenthesized, parse_macro_input, Token}; +use syn::{parenthesized, parse_macro_input, parse_quote, Token}; use syn::{ Expr, FnArg, ImplItem, ImplItemFn, ItemImpl, ItemTrait, Path, PathArguments, PathSegment, TraitItem, Type, @@ -33,7 +33,7 @@ pub fn def_interface(attr: TokenStream, item: TokenStream) -> TokenStream { )); } - let ast = syn::parse_macro_input!(item as ItemTrait); + let mut ast = syn::parse_macro_input!(item as ItemTrait); let trait_name = &ast.ident; let vis = &ast.vis; @@ -58,6 +58,14 @@ pub fn def_interface(attr: TokenStream, item: TokenStream) -> TokenStream { } } + let alias_guard_name = format_ident!("__MustNotAnAlias__{}", trait_name); + let alias_guard = parse_quote!( + #[allow(non_upper_case_globals)] + #[doc(hidden)] + const #alias_guard_name: () = (); + ); + ast.items.push(alias_guard); + let mod_name = format_ident!("__{}_mod", trait_name); quote! { #ast @@ -77,13 +85,33 @@ pub fn def_interface(attr: TokenStream, item: TokenStream) -> TokenStream { /// Implement the interface for a struct. /// /// This attribute should be added above the implementation of a trait for a -/// struct, and the trait must be defined with -/// [`#[def_interface]`](macro@crate::def_interface). +/// struct, and the trait must be defined with [`#[def_interface]`](def_interface!). /// /// It is not necessary to implement it in the same crate as the definition, but /// it is required that these crates are linked together. /// /// See the [crate-level documentation](crate) for more details. +/// +/// # Caveat +/// +/// The specified trait name must not be an alias to the originally defined +/// name; otherwise, it will result in a compile error. +/// +/// ```rust,compile_fail +/// # use crate_interface::*; +/// #[def_interface] +/// trait MyIf { +/// fn foo(); +/// } +/// +/// use MyIf as MyIf2; +/// struct MyImpl; +/// #[impl_interface] +/// impl MyIf2 for MyImpl { +/// fn foo() {} +/// } +/// ``` +/// #[proc_macro_attribute] pub fn impl_interface(attr: TokenStream, item: TokenStream) -> TokenStream { if !attr.is_empty() { @@ -158,6 +186,10 @@ pub fn impl_interface(attr: TokenStream, item: TokenStream) -> TokenStream { } } + let alias_guard_name = format_ident!("__MustNotAnAlias__{}", trait_name); + let alias_guard = parse_quote!(const #alias_guard_name: () = ();); + ast.items.push(alias_guard); + quote! { #ast }.into() } From dcd535e851fcfd58092bc18023fec011c4a472fe Mon Sep 17 00:00:00 2001 From: Loi Chyan Date: Fri, 30 May 2025 02:18:24 +0000 Subject: [PATCH 2/3] Explain why using trait aliases is unsound --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 34869de..67aa792 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,9 @@ pub fn def_interface(attr: TokenStream, item: TokenStream) -> TokenStream { } } + // Enforce no alias is used to implement an interface, as this makes it + // possible to link the function called by `call_interface` to an + // implementation with a different signature, which is extremely unsound. let alias_guard_name = format_ident!("__MustNotAnAlias__{}", trait_name); let alias_guard = parse_quote!( #[allow(non_upper_case_globals)] @@ -111,7 +114,6 @@ pub fn def_interface(attr: TokenStream, item: TokenStream) -> TokenStream { /// fn foo() {} /// } /// ``` -/// #[proc_macro_attribute] pub fn impl_interface(attr: TokenStream, item: TokenStream) -> TokenStream { if !attr.is_empty() { From 0359ad67d69da41a327f440259ca8c3193f29693 Mon Sep 17 00:00:00 2001 From: Loi Chyan Date: Fri, 30 May 2025 03:06:39 +0000 Subject: [PATCH 3/3] Fix broken doc links --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 67aa792..9a98a36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,8 @@ pub fn def_interface(attr: TokenStream, item: TokenStream) -> TokenStream { /// Implement the interface for a struct. /// /// This attribute should be added above the implementation of a trait for a -/// struct, and the trait must be defined with [`#[def_interface]`](def_interface!). +/// struct, and the trait must be defined with +/// [`#[def_interface]`](macro@crate::def_interface). /// /// It is not necessary to implement it in the same crate as the definition, but /// it is required that these crates are linked together.