├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── macros ├── Cargo.toml └── src │ ├── generic_visitor.rs │ └── lib.rs ├── publish.sh ├── src ├── lib.rs └── traits.rs └── tests ├── generated.rs └── tests.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI Checks 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | jobs: 8 | cargo-test: 9 | name: cargo test 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | - name: Set up Rust Toolchain 15 | run: curl https://sh.rustup.rs -sSf | sh -s -- -y 16 | - name: cargo test 17 | run: cargo test --workspace 18 | cargo-fmt: 19 | name: cargo fmt 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | - name: Set up Rust Toolchain 25 | run: curl https://sh.rustup.rs -sSf | sh -s -- -y 26 | - name: cargo fmt 27 | run: cargo fmt -- --check 28 | cargo-doc: 29 | name: cargo doc 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v3 34 | - name: Set up Rust Toolchain 35 | run: curl https://sh.rustup.rs -sSf | sh -s -- -y 36 | - name: cargo doc 37 | run: cargo doc --workspace --all-features 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["macros"] 3 | 4 | [package] 5 | name = "supertrait" 6 | version = "0.2.1" 7 | edition = "2024" 8 | authors = ["sam0x17"] 9 | description = "Supertrait enables default associated types and const fn trait items in stable Rust" 10 | license = "MIT" 11 | repository = "https://github.com/sam0x17/supertrait" 12 | keywords = ["traits", "supertrait", "stable", "macros", "trait"] 13 | documentation = "https://docs.rs/crate/supertrait/latest" 14 | 15 | [dependencies] 16 | supertrait-macros = { path = "macros", version = "0.2.1" } 17 | macro_magic = "0.6" 18 | 19 | [features] 20 | default = [] 21 | debug = ["supertrait-macros/debug"] 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Sam Johnson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | and associated documentation files (the “Software”), to deal in the Software without 5 | restriction, including without limitation the rights to use, copy, modify, merge, publish, 6 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 7 | Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or 10 | substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 13 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 15 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Supertrait 🦹 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/supertrait)](https://crates.io/crates/supertrait) 4 | [![docs.rs](https://img.shields.io/docsrs/supertrait?label=docs)](https://docs.rs/supertrait/latest/supertrait/) 5 | [![Build Status](https://img.shields.io/github/actions/workflow/status/sam0x17/supertrait/ci.yaml)](https://github.com/sam0x17/supertrait/actions/workflows/ci.yaml?query=branch%3Amain) 6 | [![MIT License](https://img.shields.io/github/license/sam0x17/supertrait)](https://github.com/sam0x17/supertrait/blob/main/LICENSE) 7 | 8 | Supertrait is a revolutionary crate that enables _default associated types_ and _const fn trait 9 | items_ in stable Rust since July 2023. Supertrait accomplishes this through a variety of 10 | macro-related techniques including the use of 11 | [macro_magic](https://crates.io/crates/macro_magic) as well as the "module wormhole" technique 12 | demonstrated in the docs for `#[supertrait]` and `#[impl_supertrait]`. 13 | 14 | Here is an end-to-end example: 15 | 16 | ```rust 17 | #[supertrait] 18 | pub trait Fizz: Copy + Sized { 19 | type Foo = Option; 20 | type Bar; 21 | 22 | const fn double_value(val: T) -> (T, T) { 23 | (val, val) 24 | } 25 | 26 | const fn triple_value(val: T) -> (T, T, T); 27 | 28 | fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) { 29 | (*self, *self, plus) 30 | } 31 | 32 | const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 33 | } 34 | 35 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 36 | struct Buzz; 37 | 38 | #[impl_supertrait] 39 | impl Fizz for Buzz { 40 | type Bar = usize; 41 | 42 | const fn triple_value(val: T) -> (T, T, T) { 43 | (val, val, val) 44 | } 45 | 46 | const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T) { 47 | (b, Some(a), a) 48 | } 49 | } 50 | 51 | #[test] 52 | const fn test_buzz_const() { 53 | assert!(Buzz::triple_value(3).0 == 3); 54 | let buzz = Buzz {}; 55 | match buzz.interleave('h', false).1 { 56 | Some(c) => assert!(c == 'h'), 57 | None => unreachable!(), 58 | } 59 | } 60 | 61 | #[test] 62 | fn test_buzz_default_associated_types() { 63 | let buzz = Buzz {}; 64 | assert_eq!(buzz.double_self_plus(Some(3)), (buzz, buzz, Some(3))) 65 | } 66 | ``` 67 | 68 | Notice that in the above supertrait we are able to use both default associated types and const 69 | fn trait items with full generics support. 70 | 71 | Supertraits are also sealed such that a trait created via `#[supertrait]` can only be impled if 72 | `#[impl_supertrait]` is attached to the impl statement. 73 | 74 | Default associated types are implemented in a way that should be nearly identical with how 75 | default associated types will function when they are eventually added to stable rust. 76 | 77 | Const fn trait items are implemented as _inherents_ on the underlying type, however their 78 | presence is enforced by `#[impl_supertrait]` and their type bounds are enforced by the 79 | requirement for shadow non-const implementations of each const fn trait item that are filled in 80 | by the expansion of `#[impl_supertrait]`. These two mechanisms along with the trait sealing 81 | technique mentioned above collectively ensure that const fn trait items presence and 82 | correctness is enforced just as strongly as that of regular trait items. 83 | 84 | Using inherents as the vehicle for implementing const fn trait items has a few drawbacks due to 85 | the naming collisions that can occur with existing inherent items as well as the inability to 86 | blanket impl supertraits containing const fns (because it is impossible in stable Rust to 87 | blanket impl anything other than a real trait). 88 | 89 | That said, inherents are a convenient fallback when you find yourself reaching for const fn 90 | items in traits, and supertrait contains the most convenient implementation of this behavior 91 | currently possible in stable Rust. 92 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "supertrait-macros" 3 | version = "0.2.1" 4 | edition = "2024" 5 | authors = ["sam0x17"] 6 | license = "MIT" 7 | description = "Support macros for supertrait" 8 | repository = "https://github.com/sam0x17/supertrait" 9 | documentation = "https://docs.rs/crate/supertrait/latest" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | syn = { version = "2", features = ["full", "visit", "visit-mut"] } 16 | quote = "1" 17 | proc-macro2 = "1" 18 | proc-utils = { version = "0", optional = true } 19 | macro_magic = { version = "0", features = ["proc_support"] } 20 | rand = "0" 21 | 22 | [features] 23 | default = [] 24 | debug = ["dep:proc-utils"] 25 | -------------------------------------------------------------------------------- /macros/src/generic_visitor.rs: -------------------------------------------------------------------------------- 1 | use quote::ToTokens; 2 | use std::{ 3 | collections::HashSet, 4 | fmt::Debug, 5 | hash::{Hash, Hasher}, 6 | }; 7 | use syn::{ 8 | AngleBracketedGenericArguments, Expr, GenericParam, Generics, Lifetime, PathArguments, 9 | PredicateType, ReturnType, Signature, TraitBound, Type, TypeBareFn, TypeImplTrait, 10 | TypeParamBound, TypeParen, TypePath, TypeReference, TypeTraitObject, parse2, 11 | visit::{self, Visit}, 12 | }; 13 | 14 | #[derive(Clone)] 15 | pub enum GenericUsage { 16 | Type(TypePath), 17 | Lifetime(Lifetime), 18 | Const(Expr), 19 | } 20 | 21 | impl From for GenericUsage { 22 | fn from(value: GenericParam) -> Self { 23 | (&value).into() 24 | } 25 | } 26 | 27 | impl From<&GenericParam> for &GenericUsage { 28 | fn from(value: &GenericParam) -> Self { 29 | value.into() 30 | } 31 | } 32 | 33 | impl From<&GenericParam> for GenericUsage { 34 | fn from(value: &GenericParam) -> Self { 35 | match value { 36 | GenericParam::Type(type_param) => GenericUsage::from_type(&type_param.ident), 37 | GenericParam::Lifetime(lifetime_def) => { 38 | GenericUsage::from_lifetime(&lifetime_def.lifetime) 39 | } 40 | GenericParam::Const(const_param) => GenericUsage::from_const(&const_param.ident), 41 | } 42 | } 43 | } 44 | 45 | impl Debug for GenericUsage { 46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 47 | match self { 48 | Self::Type(typ) => f.debug_tuple("Type").field(&typ.to_token_stream()).finish(), 49 | Self::Lifetime(life) => f 50 | .debug_tuple("Lifetime") 51 | .field(&life.to_token_stream()) 52 | .finish(), 53 | Self::Const(constant) => f 54 | .debug_tuple("Const") 55 | .field(&constant.to_token_stream()) 56 | .finish(), 57 | } 58 | } 59 | } 60 | 61 | impl Hash for GenericUsage { 62 | fn hash(&self, state: &mut H) { 63 | let discriminant = core::mem::discriminant(self); 64 | match self { 65 | GenericUsage::Type(typ) => { 66 | (discriminant, typ.to_token_stream().to_string()).hash(state) 67 | } 68 | GenericUsage::Lifetime(lifetime) => { 69 | (discriminant, lifetime.to_token_stream().to_string()).hash(state) 70 | } 71 | GenericUsage::Const(constant) => { 72 | (discriminant, constant.to_token_stream().to_string()).hash(state) 73 | } 74 | } 75 | } 76 | } 77 | 78 | impl PartialEq for GenericUsage { 79 | fn eq(&self, other: &Self) -> bool { 80 | match (self, other) { 81 | (Self::Type(l), Self::Type(r)) => { 82 | l.to_token_stream().to_string() == r.to_token_stream().to_string() 83 | } 84 | (Self::Lifetime(l), Self::Lifetime(r)) => { 85 | l.to_token_stream().to_string() == r.to_token_stream().to_string() 86 | } 87 | (Self::Const(l), Self::Const(r)) => { 88 | l.to_token_stream().to_string() == r.to_token_stream().to_string() 89 | } 90 | _ => false, 91 | } 92 | } 93 | } 94 | 95 | impl Eq for GenericUsage {} 96 | 97 | impl GenericUsage { 98 | pub fn from_type(input: impl ToTokens) -> Self { 99 | GenericUsage::Type(parse2::(input.to_token_stream()).unwrap()) 100 | } 101 | 102 | pub fn from_const(input: impl ToTokens) -> Self { 103 | GenericUsage::Const(parse2::(input.to_token_stream()).unwrap()) 104 | } 105 | 106 | pub fn from_lifetime(input: impl ToTokens) -> Self { 107 | GenericUsage::Lifetime(parse2::(input.to_token_stream()).unwrap()) 108 | } 109 | } 110 | 111 | pub struct FindGenericParam { 112 | pub subjects: HashSet, 113 | pub usages: HashSet, 114 | } 115 | 116 | impl FindGenericParam { 117 | pub fn new(input_generics: &Generics) -> Self { 118 | let mut subjects: HashSet = HashSet::new(); 119 | for generic_param in &input_generics.params { 120 | subjects.insert(generic_param.into()); 121 | } 122 | 123 | // NOTE: there is no need for us to also traverse where clause as new generics can't be 124 | // _introduced_ there 125 | 126 | FindGenericParam { 127 | subjects, 128 | usages: HashSet::new(), 129 | } 130 | } 131 | 132 | pub fn add_type_usage(&mut self, usage: impl ToTokens) { 133 | let usage = GenericUsage::from_type(usage); 134 | if self.subjects.contains(&usage) { 135 | self.usages.insert(usage); 136 | } 137 | } 138 | 139 | pub fn add_const_usage(&mut self, usage: impl ToTokens) { 140 | let usage = GenericUsage::from_const(usage); 141 | if self.subjects.contains(&usage) { 142 | self.usages.insert(usage); 143 | } 144 | } 145 | 146 | pub fn add_lifetime_usage(&mut self, usage: impl ToTokens) { 147 | let usage = GenericUsage::from_lifetime(usage); 148 | if self.subjects.contains(&usage) { 149 | self.usages.insert(usage); 150 | } 151 | } 152 | } 153 | 154 | impl<'ast> Visit<'ast> for FindGenericParam { 155 | // Covers usage like: Vec 156 | fn visit_type_path(&mut self, node: &'ast TypePath) { 157 | for segment in &node.path.segments { 158 | if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) = 159 | &segment.arguments 160 | { 161 | for arg in args { 162 | if let syn::GenericArgument::Type(Type::Path(type_path)) = arg { 163 | self.add_type_usage(type_path); 164 | } 165 | } 166 | } 167 | } 168 | 169 | self.add_type_usage(node); 170 | 171 | visit::visit_type_path(self, node); 172 | } 173 | 174 | // Covers usage like: fn foo(arg: T) -> T {} 175 | fn visit_signature(&mut self, node: &'ast Signature) { 176 | for input in &node.inputs { 177 | if let syn::FnArg::Typed(pat) = input { 178 | if let Type::Path(type_path) = &*pat.ty { 179 | self.add_type_usage(type_path); 180 | } 181 | } 182 | } 183 | 184 | visit::visit_signature(self, node); 185 | } 186 | 187 | // Covers usage like: where T: SomeTrait 188 | fn visit_predicate_type(&mut self, node: &'ast PredicateType) { 189 | if let Type::Path(type_path) = &node.bounded_ty { 190 | self.add_type_usage(type_path); 191 | } 192 | 193 | visit::visit_predicate_type(self, node); 194 | } 195 | 196 | // Covers usage like: T: SomeTrait 197 | fn visit_type_param_bound(&mut self, node: &'ast TypeParamBound) { 198 | if let TypeParamBound::Trait(TraitBound { path, .. }) = node { 199 | if let Some(ident) = path.get_ident() { 200 | let usage = GenericUsage::from_type(ident); 201 | if self.subjects.contains(&usage) { 202 | self.usages.insert(usage); 203 | } 204 | } 205 | } 206 | 207 | visit::visit_type_param_bound(self, node); 208 | } 209 | 210 | // Covers usage like: type AssocType: SomeTrait; 211 | fn visit_type_param(&mut self, node: &'ast syn::TypeParam) { 212 | for bound in &node.bounds { 213 | if let syn::TypeParamBound::Trait(trait_bound) = bound { 214 | if let Some(ident) = trait_bound.path.get_ident() { 215 | self.add_type_usage(ident); 216 | } 217 | } 218 | } 219 | 220 | visit::visit_type_param(self, node); 221 | } 222 | 223 | // Covers usage like: 'a 224 | fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { 225 | self.add_lifetime_usage(lifetime); 226 | visit::visit_lifetime(self, lifetime); 227 | } 228 | 229 | // Covers usage like: const GENERIC: Type = value; 230 | fn visit_expr(&mut self, expr: &'ast Expr) { 231 | if let Expr::Path(expr_path) = expr { 232 | if let Some(ident) = expr_path.path.get_ident() { 233 | self.add_const_usage(ident); 234 | } 235 | } 236 | visit::visit_expr(self, expr); 237 | } 238 | 239 | // Covers usage like: dyn SomeTrait 240 | fn visit_type_trait_object(&mut self, node: &'ast TypeTraitObject) { 241 | for bound in &node.bounds { 242 | if let TypeParamBound::Trait(TraitBound { path, .. }) = bound { 243 | if let Some(ident) = path.get_ident() { 244 | self.add_type_usage(ident); 245 | } 246 | } 247 | } 248 | 249 | visit::visit_type_trait_object(self, node); 250 | } 251 | 252 | // Covers usage like: fn(T) 253 | fn visit_type_bare_fn(&mut self, node: &'ast TypeBareFn) { 254 | for arg in &node.inputs { 255 | if let Type::Path(type_path) = &arg.ty { 256 | self.add_type_usage(type_path); 257 | } 258 | } 259 | 260 | if let ReturnType::Type(_, typ) = &node.output { 261 | self.add_type_usage(typ); 262 | } 263 | 264 | visit::visit_type_bare_fn(self, node); 265 | } 266 | 267 | // Covers usage like: (T) 268 | fn visit_type_paren(&mut self, node: &'ast TypeParen) { 269 | if let Type::Path(type_path) = &*node.elem { 270 | self.add_type_usage(type_path); 271 | } 272 | 273 | visit::visit_type_paren(self, node); 274 | } 275 | 276 | // Covers usage like: impl SomeTrait 277 | fn visit_type_impl_trait(&mut self, node: &'ast TypeImplTrait) { 278 | for bound in &node.bounds { 279 | if let TypeParamBound::Trait(TraitBound { path, .. }) = bound { 280 | if let Some(ident) = path.get_ident() { 281 | self.add_type_usage(ident); 282 | } 283 | } 284 | } 285 | 286 | visit::visit_type_impl_trait(self, node); 287 | } 288 | 289 | // Covers usage like: &T and &'a T 290 | fn visit_type_reference(&mut self, node: &'ast TypeReference) { 291 | if let Some(ref lifetime) = node.lifetime { 292 | self.add_lifetime_usage(lifetime); 293 | } 294 | 295 | if let Type::Path(type_path) = &*node.elem { 296 | self.add_type_usage(type_path); 297 | } 298 | 299 | visit::visit_type_reference(self, node); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | #![warn(missing_docs)] 3 | 4 | //! Contains support macros for [supertrait](https://crates.io/crates/supertrait). 5 | 6 | use macro_magic::import_tokens_attr; 7 | use proc_macro::TokenStream; 8 | use proc_macro2::{TokenStream as TokenStream2, TokenTree}; 9 | use quote::{ToTokens, format_ident, quote}; 10 | use rand::{distr::StandardUniform, prelude::Distribution}; 11 | use std::{ 12 | cell::RefCell, 13 | collections::{HashMap, HashSet}, 14 | fmt::Debug, 15 | sync::atomic::AtomicU64, 16 | }; 17 | use syn::{ 18 | Error, GenericParam, Generics, Ident, ImplItem, ImplItemFn, Item, ItemFn, ItemImpl, ItemMod, 19 | ItemTrait, Path, Result, Signature, TraitItem, TraitItemFn, TraitItemType, TypePath, 20 | Visibility, WherePredicate, 21 | parse::{Nothing, Parse, ParseStream}, 22 | parse_macro_input, parse_quote, parse_str, parse2, 23 | punctuated::Punctuated, 24 | spanned::Spanned, 25 | visit::Visit, 26 | visit_mut::VisitMut, 27 | }; 28 | 29 | #[cfg(feature = "debug")] 30 | use proc_utils::PrettyPrint; 31 | 32 | mod generic_visitor; 33 | use generic_visitor::*; 34 | 35 | static IMPL_COUNT: AtomicU64 = AtomicU64::new(0); 36 | thread_local! { 37 | static SUPERTRAIT_PATH: RefCell = RefCell::new(String::from("::supertrait")); 38 | } 39 | 40 | fn get_supertrait_path() -> Path { 41 | SUPERTRAIT_PATH.with(|p| parse_str(p.borrow().clone().as_str()).unwrap()) 42 | } 43 | 44 | fn random() -> T 45 | where 46 | StandardUniform: Distribution, 47 | { 48 | rand::random() 49 | } 50 | 51 | /// Allows you to override the module path where supertrait's macros will look for necessary 52 | /// re-exports (such as `macro_magic`). 53 | /// 54 | /// The default is `::supertrait`. 55 | /// 56 | /// Generally speaking you shouldn't need to use this directly, but in some scenarios (like in 57 | /// the `lib.rs` of the main crate, this is necessary). 58 | #[proc_macro] 59 | pub fn set_supertrait_path(tokens: TokenStream) -> TokenStream { 60 | let path = parse_macro_input!(tokens as Path); 61 | SUPERTRAIT_PATH.with(|p| p.replace(path.to_token_stream().to_string())); 62 | quote!().into() 63 | } 64 | 65 | struct SuperTraitDef { 66 | pub orig_trait: ItemTrait, 67 | pub const_fns: Vec, 68 | pub types_with_defaults: Vec, 69 | pub other_items: Vec, 70 | } 71 | 72 | impl Parse for SuperTraitDef { 73 | fn parse(input: ParseStream) -> Result { 74 | let orig_trait = input.parse::()?; 75 | let mut const_fns: Vec = Vec::new(); 76 | let mut types_with_defaults: Vec = Vec::new(); 77 | let mut other_items: Vec = Vec::new(); 78 | for trait_item in &orig_trait.items { 79 | match trait_item { 80 | TraitItem::Fn(trait_item_fn) => match trait_item_fn.sig.constness { 81 | Some(_) => const_fns.push(trait_item_fn.clone()), 82 | None => other_items.push(trait_item.clone()), 83 | }, 84 | TraitItem::Type(typ) => match typ.default { 85 | Some(_) => types_with_defaults.push(typ.clone()), 86 | None => other_items.push(trait_item.clone()), 87 | }, 88 | other_item => other_items.push(other_item.clone()), 89 | } 90 | } 91 | Ok(SuperTraitDef { 92 | orig_trait, 93 | const_fns, 94 | types_with_defaults, 95 | other_items, 96 | }) 97 | } 98 | } 99 | 100 | struct FilteredGenerics { 101 | /// Represents the `#` in `impl<*> Something for MyStruct<#>`. Both variants share the same 102 | /// where clause 103 | use_generics: Generics, 104 | /// Represents the `*` in `impl<*> Something for MyStruct<#>`. Both variants share the same 105 | /// where clause 106 | impl_generics: Generics, 107 | has_defaults: HashSet, 108 | } 109 | 110 | impl FilteredGenerics { 111 | fn strip_default_generics(&mut self) { 112 | // find generic params in impl_generics that have defaults 113 | let has_defaults = self 114 | .impl_generics 115 | .params 116 | .iter() 117 | .filter_map(|g| match g { 118 | GenericParam::Lifetime(_) => None, 119 | GenericParam::Type(typ) => match typ.default { 120 | Some(_) => Some(typ.force_get_ident()), 121 | None => None, 122 | }, 123 | GenericParam::Const(constant) => match constant.default { 124 | Some(_) => Some(constant.force_get_ident()), // might be wrong 125 | None => None, 126 | }, 127 | }) 128 | .collect::>(); 129 | // strip these from impl_generics 130 | self.impl_generics.params = self 131 | .impl_generics 132 | .params 133 | .iter() 134 | .filter(|g| !has_defaults.contains(&g.force_get_ident())) 135 | .cloned() 136 | .collect(); 137 | // also strip them from use_generics 138 | self.use_generics.params = self 139 | .use_generics 140 | .params 141 | .iter() 142 | .filter(|g| !has_defaults.contains(&g.force_get_ident())) 143 | .cloned() 144 | .collect(); 145 | self.has_defaults = has_defaults; 146 | } 147 | } 148 | 149 | fn filter_generics(generics: &Generics, whitelist: &HashSet) -> FilteredGenerics { 150 | let filtered_generic_params = generics 151 | .params 152 | .iter() 153 | .cloned() 154 | .filter(|g| whitelist.contains(&g.into())); 155 | 156 | // generate a version of the where clause containing only whitelisted usages 157 | let filtered_where_clause = match &generics.where_clause { 158 | Some(where_clause) => { 159 | let mut where_clause = where_clause.clone(); 160 | let predicates_filtered = where_clause.predicates.iter().filter(|p| match *p { 161 | WherePredicate::Lifetime(lifetime) => { 162 | whitelist.contains(&GenericUsage::from_lifetime(&lifetime.lifetime)) 163 | } 164 | WherePredicate::Type(typ) => { 165 | whitelist.contains(&GenericUsage::from_type(&typ.bounded_ty)) 166 | } 167 | _ => unimplemented!(), 168 | }); 169 | where_clause.predicates = parse_quote!(#(#predicates_filtered),*); 170 | Some(where_clause) 171 | } 172 | None => None, 173 | }; 174 | 175 | let use_generic_params = filtered_generic_params.clone().map(|g| match g { 176 | GenericParam::Lifetime(lifetime) => lifetime.lifetime.to_token_stream(), 177 | GenericParam::Type(typ) => typ.ident.to_token_stream(), 178 | GenericParam::Const(constant) => constant.ident.to_token_stream(), 179 | }); 180 | 181 | let use_generics = Generics { 182 | lt_token: parse_quote!(<), 183 | params: parse_quote!(#(#use_generic_params),*), 184 | gt_token: parse_quote!(>), 185 | where_clause: filtered_where_clause.clone(), 186 | }; 187 | 188 | let impl_generics = Generics { 189 | lt_token: parse_quote!(<), 190 | params: parse_quote!(#(#filtered_generic_params),*), 191 | gt_token: parse_quote!(>), 192 | where_clause: filtered_where_clause, 193 | }; 194 | 195 | FilteredGenerics { 196 | use_generics, 197 | impl_generics, 198 | has_defaults: HashSet::new(), 199 | } 200 | } 201 | 202 | struct DefaultRemover; 203 | 204 | impl VisitMut for DefaultRemover { 205 | fn visit_generics_mut(&mut self, generics: &mut Generics) { 206 | for param in &mut generics.params { 207 | if let syn::GenericParam::Type(type_param) = param { 208 | type_param.default = None; 209 | } 210 | } 211 | } 212 | } 213 | 214 | trait PunctuatedExtension: Sized { 215 | fn push_front(&mut self, value: T); 216 | } 217 | 218 | impl PunctuatedExtension 219 | for Punctuated 220 | { 221 | fn push_front(&mut self, value: T) { 222 | let mut new_punctuated = Punctuated::new(); 223 | new_punctuated.push(value); 224 | new_punctuated.extend(self.iter().cloned()); 225 | *self = new_punctuated; 226 | } 227 | } 228 | 229 | /// Attach this attribute to a trait definition to transform it into a supertrait, able to make 230 | /// use of _default associated types_ and const fn trait items (the latter with some 231 | /// limitations). 232 | /// 233 | /// The following example demonstrates some of the major features and edge cases of supertraits: 234 | /// 235 | /// ```ignore 236 | /// #[supertrait] 237 | /// pub trait Fizz: Copy + Sized { 238 | /// type Foo = Option; 239 | /// type Bar; 240 | /// 241 | /// const fn double_value(val: T) -> (T, T) { 242 | /// (val, val) 243 | /// } 244 | /// 245 | /// const fn triple_value(val: T) -> (T, T, T); 246 | /// 247 | /// fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) { 248 | /// (*self, *self, plus) 249 | /// } 250 | /// 251 | /// const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 252 | /// } 253 | /// ``` 254 | /// 255 | /// ### Default Associated Types 256 | /// 257 | /// Supertrait supports default associated types in a supertrait definition. These associated 258 | /// types can also be used in other trait items via `Self::SomeType` and this will work 259 | /// properly anywhere in the trait or trait impl. 260 | /// 261 | /// Implementers are free to override default types specified on the trait by simply impling 262 | /// that trait item. 263 | /// 264 | /// In practice, default associated types in supertrait behave exactly the way you would expect 265 | /// them to behave when they finally reach stable Rust. 266 | /// 267 | /// Generics are fully supported by this feature, and any types you mention in your default 268 | /// associated type will automatically be in scope when you 269 | /// [`#[impl_supertrait]`](`macro@impl_supertrait`) the trait. 270 | /// 271 | /// 272 | /// ### Const Fn Trait Items 273 | /// Supertrait also supports const fn trait items. These items are masked by auto-generated 274 | /// non-const versions of the const fns that are added to enforce trait bounds. 275 | /// 276 | /// Currently there is no way in stable Rust to add a const fn to a trait. To accomplish 277 | /// something like this, supertrait does two things: 278 | /// 279 | /// - First, in the expansion of [`#[impl_supertrait]`](`macro@impl_supertrait`), the const fns 280 | /// are automatically implemented as _inherents_ on the implementing type. This creates major 281 | /// limitations and makes it impossible to do things like blanket impls if they involve const 282 | /// fns. 283 | /// - Second, non-const copies that mask each const fn trait item are created and injected into 284 | /// the resulting supertrait. This allow us to ensure all trait bounds are respected by 285 | /// implementers. 286 | /// 287 | /// Thus all bounds and trait requirements are enforced on the implementing type, however const 288 | /// fns in particular are implemented as inherents, i.e. `impl MyStruct { const fn something() 289 | /// {...} }`. 290 | /// 291 | /// This technique has a few limitations. Because of naming collisions on inherent impls, you 292 | /// can't impl the same (const-fn-containing) supertrait on the same type multiple times with 293 | /// different generics, like you can with famous conventional traits like `From`. 294 | /// 295 | /// For more information, see the docs for [`#[impl_supertrait]`](`macro@impl_supertrait`). 296 | /// 297 | /// ### Expansion 298 | /// 299 | /// Supertrait relies heavily on the token teleportation capabilities provided by 300 | /// [macro_magic](https://crates.io/crates/macro_magic). As a result, special care has to be 301 | /// taken to ensure any in-scope types mentioned in the teleported areas of a supertrait are 302 | /// accessible anywhere the supertrait is implemented. 303 | /// 304 | /// To accomplish this, supertrait uses the "module wormhole" technique, whereby the actual 305 | /// trait item (i.e. `MyTrait`) is represented as a module containing `use super::*;`, which in turn "teleports" all local 306 | /// imports from the trait definition site to any context in which the trait is implemented. 307 | /// Under the hood, the actual generated trait lives in `MyTrait::Trait`, along with a trait 308 | /// and struct pair called `DefaultTypes` and `Defaults` (the latter, containing an impl 309 | /// specifying all of the default types with their proper generics). That said, inside impls 310 | /// for a supertrait called `MyTrait`, you can refer to the trait like `MyTrait` instead of 311 | /// `MyTrait::Trait` as well as referring to the associated types like `Self::Whatever` 312 | /// directly. 313 | /// 314 | /// The following is the intermediate expansion for the `#[supertrait]` trait definition shown 315 | /// above, decorated with comments explaining what the various parts do: 316 | /// 317 | /// ```ignore 318 | /// #[allow(non_snake_case)] 319 | /// pub mod Fizz { 320 | /// // "wormhole technique", this allows us to capture the trait definition import scope 321 | /// // and re-use it at any trait impl sites. 322 | /// use super::*; 323 | /// 324 | /// /// Contains default associated types for this SuperTrait 325 | /// pub struct Defaults; 326 | /// 327 | /// /// A subset of the original [`Trait`] containing just the default associated types 328 | /// /// _without_ their defaults. This is automatically implemented on [`Defaults`], 329 | /// /// which contains the actual default type values. 330 | /// pub trait DefaultTypes { 331 | /// type __Self; 332 | /// type Foo; 333 | /// } 334 | /// 335 | /// // note that the `__Self` keyword is used internally in place of `Self`, which is 336 | /// // replaced back with `Self` at the trait impl site 337 | /// impl DefaultTypes for Defaults { 338 | /// // default associated type values are stored here 339 | /// type Foo = Option; 340 | /// type __Self = (); 341 | /// } 342 | /// 343 | /// // This trait is auto-generated and added as a bound on `Trait` to ensure that 344 | /// // supertraits can only be implemented via the `#[impl_supertrait]` macro. 345 | /// #[doc(hidden)] 346 | /// pub trait SupertraitSealed2484139876 {} 347 | /// 348 | /// // This is the actual internal trait that gets generated, including the 349 | /// // auto-generated sealing bound 350 | /// pub trait Trait: Copy + Sized + SupertraitSealed2484139876 { 351 | /// type Bar; 352 | /// 353 | /// fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) { 354 | /// (*self, *self, plus) 355 | /// } 356 | /// 357 | /// type Foo; 358 | /// 359 | /// fn double_value(val: T) -> (T, T) { 360 | /// (val, val) 361 | /// } 362 | /// 363 | /// fn triple_value(val: T) -> (T, T, T); 364 | /// 365 | /// fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 366 | /// } 367 | /// 368 | /// // The tokens of this module are all exported via `macro_magic` so that they can be 369 | /// // accessed directly by `#[impl_supertrait]`. This contains all the information 370 | /// // needed to complete the trait impl expansion. 371 | /// #[::supertrait::__private::macro_magic::export_tokens_no_emit(Fizz_exported_tokens)] 372 | /// mod exported_tokens { 373 | /// // tokens for const fns are stored here 374 | /// trait ConstFns { 375 | /// const fn double_value(val: T) -> (T, T) { 376 | /// (val, val) 377 | /// } 378 | /// const fn triple_value(val: T) -> (T, T, T); 379 | /// const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 380 | /// } 381 | /// 382 | /// // tokens various versions of the trait generics are stored in these fns 383 | /// fn trait_impl_generics() {} 384 | /// fn trait_use_generics() {} 385 | /// fn default_impl_generics() {} 386 | /// fn default_use_generics() {} 387 | /// 388 | /// // tokens for default associated type values are stored here 389 | /// mod default_items { 390 | /// type Foo = Option; 391 | /// } 392 | /// 393 | /// // This const is included solely so `#[impl_supertrait]` can access its `Ident` 394 | /// // to unseal and successfully implement the underlying supertrait on some type. 395 | /// const SupertraitSealed2484139876: () = (); 396 | /// } 397 | /// } 398 | /// ``` 399 | /// 400 | /// Note that in the macro expansion code, these items are only stored as a token tree within a 401 | /// `macro_rules` macro. Thus the syntax here does not need to compile, it just needs to parse 402 | /// correctly as a module of items from the perspective of `syn`. 403 | /// 404 | /// ### Debug Mode 405 | /// 406 | /// If you enable the `debug` feature, you can add `debug` as an ident argument to this 407 | /// attribute macro and its expansion will be pretty-printed to the terminal at build time. 408 | /// This is extremely useful for debugging `supertrait` internals and for providing detailed 409 | /// information when reporting bugs. 410 | #[proc_macro_attribute] 411 | pub fn supertrait(attr: TokenStream, tokens: TokenStream) -> TokenStream { 412 | let mut attr = attr; 413 | let debug = debug_feature(&mut attr); 414 | match supertrait_internal(attr, tokens, debug) { 415 | Ok(tokens) => tokens.into(), 416 | Err(err) => err.into_compile_error().into(), 417 | } 418 | } 419 | 420 | fn debug_feature(attr: &mut TokenStream) -> bool { 421 | if let Ok(ident) = syn::parse::(attr.clone()) { 422 | if ident == "debug" { 423 | *attr = TokenStream::new(); 424 | if cfg!(feature = "debug") { 425 | true 426 | } else { 427 | println!( 428 | "warning: the 'debug' feature must be enabled for debug to work on supertrait attributes." 429 | ); 430 | false 431 | } 432 | } else { 433 | false 434 | } 435 | } else { 436 | false 437 | } 438 | } 439 | 440 | fn supertrait_internal( 441 | attr: impl Into, 442 | tokens: impl Into, 443 | #[allow(unused)] debug: bool, 444 | ) -> Result { 445 | parse2::(attr.into())?; 446 | let def = parse2::(tokens.into())?; 447 | let export_tokens_ident = format_ident!("{}_exported_tokens", def.orig_trait.ident); 448 | let mut modified_trait = def.orig_trait; 449 | modified_trait.items = def.other_items; 450 | let ident = modified_trait.ident.clone(); 451 | let attrs = modified_trait.attrs.clone(); 452 | let mut defaults = def.types_with_defaults; 453 | let unfilled_defaults = defaults 454 | .iter() 455 | .cloned() 456 | .map(|mut typ| { 457 | typ.default = None; 458 | typ 459 | }) 460 | .collect::>(); 461 | let mut visitor = FindGenericParam::new(&modified_trait.generics); 462 | let mut replace_self = ReplaceSelfType { 463 | replace_type: parse_quote!(__Self), 464 | }; 465 | for trait_item_type in &mut defaults { 466 | visitor.visit_trait_item_type(trait_item_type); 467 | replace_self.visit_trait_item_type_mut(trait_item_type); 468 | } 469 | 470 | let mut default_generics = filter_generics(&modified_trait.generics, &visitor.usages); 471 | default_generics 472 | .impl_generics 473 | .params 474 | .push_front(parse_quote!(__Self)); 475 | default_generics 476 | .use_generics 477 | .params 478 | .push_front(parse_quote!(__Self)); 479 | 480 | let default_impl_generics = default_generics.impl_generics; 481 | let default_use_generics = default_generics.use_generics; 482 | 483 | modified_trait.ident = parse_quote!(Trait); 484 | 485 | let trait_use_generic_params = modified_trait.generics.params.iter().map(|g| match g { 486 | GenericParam::Lifetime(lifetime) => lifetime.lifetime.to_token_stream(), 487 | GenericParam::Type(typ) => typ.ident.to_token_stream(), 488 | GenericParam::Const(constant) => constant.ident.to_token_stream(), 489 | }); 490 | 491 | let trait_impl_generics = modified_trait.generics.clone(); 492 | let trait_use_generics = Generics { 493 | lt_token: parse_quote!(<), 494 | params: parse_quote!(#(#trait_use_generic_params),*), 495 | gt_token: parse_quote!(>), 496 | where_clause: modified_trait.generics.where_clause.clone(), 497 | }; 498 | 499 | let const_fns = def.const_fns; 500 | let mut trait_impl_generics_fn: ItemFn = parse_quote! { fn trait_impl_generics() {} }; 501 | let mut trait_use_generics_fn: ItemFn = parse_quote! { fn trait_use_generics() {} }; 502 | let mut default_impl_generics_fn: ItemFn = parse_quote! { fn default_impl_generics() {} }; 503 | let mut default_use_generics_fn: ItemFn = parse_quote! { fn default_use_generics() {} }; 504 | trait_impl_generics_fn.sig.generics = trait_impl_generics.clone(); 505 | trait_use_generics_fn.sig.generics = trait_use_generics; 506 | default_impl_generics_fn.sig.generics = default_impl_generics.clone(); 507 | default_use_generics_fn.sig.generics = default_use_generics.clone(); 508 | 509 | modified_trait 510 | .items 511 | .extend(unfilled_defaults.iter().map(|item| parse_quote!(#item))); 512 | 513 | let converted_const_fns = const_fns.iter().map(|const_fn| { 514 | let mut item = const_fn.clone(); 515 | item.sig.constness = None; 516 | let item: TraitItem = parse_quote!(#item); 517 | item 518 | }); 519 | modified_trait.items.extend(converted_const_fns); 520 | 521 | let supertrait_path = get_supertrait_path(); 522 | 523 | // seal trait with random ident 524 | let random_value: u32 = random(); 525 | let sealed_ident = format_ident!("SupertraitSealed{random_value}"); 526 | let sealed_trait: ItemTrait = parse_quote!(pub trait #sealed_ident {}); 527 | modified_trait.supertraits.push(parse_quote!(#sealed_ident)); 528 | 529 | let mut default_remover = DefaultRemover {}; 530 | let mut default_impl_generics_no_defaults = default_impl_generics.clone(); 531 | default_remover.visit_generics_mut(&mut default_impl_generics_no_defaults); 532 | 533 | modified_trait.vis = parse_quote!(pub); 534 | 535 | for def in defaults.iter_mut() { 536 | def.bounds.clear() 537 | } 538 | 539 | let output = quote! { 540 | #(#attrs)* 541 | #[allow(non_snake_case)] 542 | pub mod #ident { 543 | use super::*; 544 | 545 | /// Contains default associated types for this SuperTrait 546 | pub struct Defaults; 547 | 548 | /// A subset of the original [`Trait`] containing just the default associated types 549 | /// _without_ their defaults. This is automatically implemented on [`Defaults`], 550 | /// which contains the actual default type values. 551 | pub trait DefaultTypes #default_impl_generics { 552 | /// Used internally to represent the `Self` type. 553 | #[doc(hidden)] 554 | type __Self; 555 | #(#unfilled_defaults)* 556 | } 557 | 558 | impl #default_impl_generics_no_defaults DefaultTypes #default_use_generics for Defaults { 559 | #(#defaults)* 560 | #[doc(hidden)] 561 | type __Self = (); 562 | } 563 | 564 | /// A compile-time generated random trait used to seal supertraits so they can only 565 | /// be implemented using `impl_supertrait`. 566 | #[doc(hidden)] 567 | #sealed_trait 568 | 569 | /// The internal trait that is a part of this supertrait. 570 | /// 571 | #(#attrs)* 572 | #modified_trait 573 | 574 | #[#supertrait_path::__private::macro_magic::export_tokens_no_emit(#export_tokens_ident)] 575 | mod exported_tokens { 576 | trait ConstFns { 577 | #(#const_fns)* 578 | } 579 | 580 | #trait_impl_generics_fn 581 | #trait_use_generics_fn 582 | #default_impl_generics_fn 583 | #default_use_generics_fn 584 | 585 | mod default_items { 586 | #(#defaults)* 587 | } 588 | 589 | const #sealed_ident: () = (); 590 | } 591 | } 592 | }; 593 | #[cfg(feature = "debug")] 594 | if debug { 595 | output.pretty_print(); 596 | } 597 | Ok(output) 598 | } 599 | 600 | #[doc(hidden)] 601 | #[import_tokens_attr(format!( 602 | "{}::__private::macro_magic", 603 | get_supertrait_path().to_token_stream().to_string() 604 | ))] 605 | #[proc_macro_attribute] 606 | pub fn __impl_supertrait(attr: TokenStream, tokens: TokenStream) -> TokenStream { 607 | match impl_supertrait_internal(attr, tokens) { 608 | Ok(tokens) => tokens.into(), 609 | Err(err) => err.into_compile_error().into(), 610 | } 611 | } 612 | 613 | /// Must be attached to any impl statement involving a supertrait. 614 | /// 615 | /// This is the impl analogue of [`#[supertrait]`](`macro@supertrait`) that you should use 616 | /// whenever you impl a supertrait. In fact, a sealing technique is used to prevent anyone from 617 | /// implementing a supertrait manually without the use of `#[impl_supertrait]`, For details on 618 | /// this sealing technique, see the expansion details for 619 | /// [`#[supertrait]`](`macro@supertrait`). 620 | /// 621 | /// Consider the following supertrait definition (from the docs for 622 | /// [`#[supertrait]`](`macro@supertrait`)): 623 | /// 624 | /// ```ignore 625 | /// use supertrait::*; 626 | /// 627 | /// #[supertrait] 628 | /// pub trait Fizz: Copy + Sized { 629 | /// type Foo = Option; 630 | /// type Bar; 631 | /// 632 | /// const fn double_value(val: T) -> (T, T) { 633 | /// (val, val) 634 | /// } 635 | /// 636 | /// const fn triple_value(val: T) -> (T, T, T); 637 | /// 638 | /// fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) { 639 | /// (*self, *self, plus) 640 | /// } 641 | /// 642 | /// const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 643 | /// } 644 | /// ``` 645 | /// 646 | /// The following code uses `#[impl_supertrait]` to implement `Fizz` for the struct `Buzz` 647 | /// and makes use of the implementation in various ways: 648 | /// 649 | /// ```ignore 650 | /// #[derive(Copy, Clone, PartialEq, Eq, Debug)] 651 | /// struct Buzz; 652 | /// 653 | /// #[impl_supertrait] 654 | /// impl Fizz for Buzz { 655 | /// type Bar = usize; 656 | /// 657 | /// const fn triple_value(val: T) -> (T, T, T) { 658 | /// (val, val, val) 659 | /// } 660 | /// 661 | /// const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T) { 662 | /// (b, Some(a), a) 663 | /// } 664 | /// } 665 | /// 666 | /// #[test] 667 | /// const fn test_buzz_const() { 668 | /// assert!(Buzz::triple_value(3).0 == 3); 669 | /// let buzz = Buzz {}; 670 | /// match buzz.interleave('h', false).1 { 671 | /// Some(c) => assert!(c == 'h'), 672 | /// None => unreachable!(), 673 | /// } 674 | /// } 675 | /// 676 | /// #[test] 677 | /// fn test_buzz_default_associated_types() { 678 | /// let buzz = Buzz {}; 679 | /// assert_eq!(buzz.double_self_plus(Some(3)), (buzz, buzz, Some(3))) 680 | /// } 681 | ///``` 682 | /// 683 | /// Note that the `Self::SomeType` syntax can be used to refer to associated types _anywhere_ 684 | /// within a `#[impl_supertrait]` impl block. 685 | /// 686 | /// ### Expansion 687 | /// 688 | /// Default associated types that are not overridden are redirected to point to their counter 689 | /// parts in `MyTrait::Defaults`, with any accompanying generics. Thus the proper paths of any 690 | /// types mentioned in default associated types is preserved thanks to the `use super::*` in 691 | /// the "wormhole" `MyTrait` module. 692 | /// 693 | /// Const fns are implemented as _inherents_ (i.e., directly implemented on the target type) 694 | /// because Rust does not yet support const fns as trait items in stable. This adds a few 695 | /// limitations, mainly around naming collisions, however all bounds and generics on const fns 696 | /// are preserved, and failing to implement them will likewise result in a compile error, so 697 | /// this is probably as close as we can get to something that emulates const fns as trait items 698 | /// in a somewhat usable way in stable Rust. 699 | /// 700 | /// Here is the expansion for the above `#[impl_supertrait]`: 701 | /// 702 | /// ```ignore 703 | /// impl Fizz::Trait for Buzz { 704 | /// type Bar = usize; 705 | /// 706 | /// // a value for `Foo` was not specified by the implementer, so the macro expansion 707 | /// // substitutes the value for `Foo` contained in `Fizz::Defaults`, preserving 708 | /// // whatever module-local types that may be mentioned in the default associated 709 | /// // type. 710 | /// type Foo = >::Foo; 711 | /// fn interleave(&self, a: T, b: I) -> (I, >::Foo, T) { 712 | /// (b, Some(a), a) 713 | /// } 714 | /// fn triple_value(val: T) -> (T, T, T) { 715 | /// (val, val, val) 716 | /// } 717 | /// fn double_value(val: T) -> (T, T) { 718 | /// (val, val) 719 | /// } 720 | /// } 721 | /// 722 | /// // This line unseals `Fizz`. Without this line, the sealing bounds on `Fizz` 723 | /// // will create a compile error. 724 | /// impl Fizz::SupertraitSealed3587271628 for Buzz {} 725 | /// 726 | /// // This impl block contains the (inherent) const fn impls of `Fizz` on `Buzz`. 727 | /// impl Buzz { 728 | /// pub const fn interleave( 729 | /// &self, 730 | /// a: T, 731 | /// b: I, 732 | /// ) -> (I, >::Foo, T) { 733 | /// (b, Some(a), a) 734 | /// } 735 | /// pub const fn triple_value(val: T) -> (T, T, T) { 736 | /// (val, val, val) 737 | /// } 738 | /// pub const fn double_value(val: T) -> (T, T) { 739 | /// (val, val) 740 | /// } 741 | /// } 742 | /// 743 | /// // This use statement is automatically inserted by the macro expansion to ensure 744 | /// // the underlying trait is actually brought into scope, since because of the 745 | /// // "wormhole module" pattern, it is actually within the `Fizz` module. 746 | /// #[allow(unused)] 747 | /// use Fizz::Trait as BuzzFizzTraitImpl_4; 748 | /// ``` 749 | /// 750 | /// See the documentation for [`#[supertrait]`](`macro@supertrait`) for more information. 751 | /// 752 | /// ### Debug Mode 753 | /// 754 | /// If you enable the `debug` feature, you can add `debug` as an ident argument to this 755 | /// attribute macro and its expansion will be pretty-printed to the terminal at build time. 756 | /// This is extremely useful for debugging `supertrait` internals and for providing detailed 757 | /// information when reporting bugs. 758 | #[proc_macro_attribute] 759 | pub fn impl_supertrait(attr: TokenStream, tokens: TokenStream) -> TokenStream { 760 | let mut attr = attr; 761 | let debug = debug_feature(&mut attr); 762 | parse_macro_input!(attr as Nothing); 763 | let item_impl = parse_macro_input!(tokens as ItemImpl); 764 | let trait_being_impled = match item_impl.trait_.clone() { 765 | Some((_, path, _)) => path, 766 | None => return Error::new( 767 | item_impl.span(), 768 | "Supertrait impls must have a trait being implemented. Inherent impls are not supported." 769 | ).into_compile_error().into(), 770 | }.strip_trailing_generics(); 771 | let supertrait_path = get_supertrait_path(); 772 | let export_tokens_ident = format_ident!( 773 | "{}_exported_tokens", 774 | trait_being_impled.segments.last().unwrap().ident 775 | ); 776 | let debug_tokens = if debug { 777 | quote!(#[debug_mode]) 778 | } else { 779 | quote!() 780 | }; 781 | let output = quote! { 782 | #[#supertrait_path::__impl_supertrait(#trait_being_impled::#export_tokens_ident)] 783 | #debug_tokens 784 | #item_impl 785 | }; 786 | output.into() 787 | } 788 | 789 | struct ImportedTokens { 790 | const_fns: Vec, 791 | trait_impl_generics: Generics, 792 | #[allow(unused)] 793 | trait_use_generics: Generics, 794 | #[allow(unused)] 795 | default_impl_generics: Generics, 796 | default_use_generics: Generics, 797 | default_items: Vec, 798 | sealed_ident: Ident, 799 | } 800 | 801 | impl TryFrom for ImportedTokens { 802 | type Error = Error; 803 | 804 | fn try_from(item_mod: ItemMod) -> std::result::Result { 805 | if item_mod.ident != "exported_tokens" { 806 | return Err(Error::new( 807 | item_mod.ident.span(), 808 | "expected `exported_tokens`.", 809 | )); 810 | } 811 | let item_mod_span = item_mod.span(); 812 | let Some((_, main_body)) = item_mod.content else { 813 | return Err(Error::new( 814 | item_mod_span, 815 | "`exported_tokens` module must have a defined body.", 816 | )); 817 | }; 818 | let Some(Item::Trait(ItemTrait { 819 | ident: const_fns_ident, 820 | items: const_fns, 821 | .. 822 | })) = main_body.get(0) 823 | else { 824 | return Err(Error::new( 825 | item_mod_span, 826 | "the first item in `exported_tokens` should be a trait called `ConstFns`.", 827 | )); 828 | }; 829 | if const_fns_ident != "ConstFns" { 830 | return Err(Error::new(const_fns_ident.span(), "expected `ConstFns`.")); 831 | } 832 | 833 | let const_fns: Vec = const_fns 834 | .into_iter() 835 | .map(|item| match item { 836 | TraitItem::Fn(trait_item_fn) => Ok(trait_item_fn.clone()), 837 | _ => return Err(Error::new(item.span(), "expected `fn`")), // can't do this 838 | }) 839 | .collect::>()?; 840 | 841 | let Some(Item::Fn(ItemFn { 842 | sig: 843 | Signature { 844 | ident: trait_impl_generics_ident, 845 | generics: trait_impl_generics, 846 | .. 847 | }, 848 | .. 849 | })) = main_body.get(1) 850 | else { 851 | return Err(Error::new( 852 | item_mod_span, 853 | "the second item in `exported_tokens` should be an fn called `trait_impl_generics`.", 854 | )); 855 | }; 856 | if trait_impl_generics_ident != "trait_impl_generics" { 857 | return Err(Error::new( 858 | trait_impl_generics_ident.span(), 859 | "expected `trait_impl_generics`.", 860 | )); 861 | } 862 | 863 | let Some(Item::Fn(ItemFn { 864 | sig: 865 | Signature { 866 | ident: trait_use_generics_ident, 867 | generics: trait_use_generics, 868 | .. 869 | }, 870 | .. 871 | })) = main_body.get(2) 872 | else { 873 | return Err(Error::new( 874 | item_mod_span, 875 | "the third item in `exported_tokens` should be an fn called `trait_use_generics`.", 876 | )); 877 | }; 878 | if trait_use_generics_ident != "trait_use_generics" { 879 | return Err(Error::new( 880 | trait_use_generics_ident.span(), 881 | "expected `trait_use_generics`.", 882 | )); 883 | } 884 | 885 | let Some(Item::Fn(ItemFn { 886 | sig: 887 | Signature { 888 | ident: default_impl_generics_ident, 889 | generics: default_impl_generics, 890 | .. 891 | }, 892 | .. 893 | })) = main_body.get(3) 894 | else { 895 | return Err(Error::new( 896 | item_mod_span, 897 | "the fourth item in `exported_tokens` should be an fn called `default_impl_generics`.", 898 | )); 899 | }; 900 | if default_impl_generics_ident != "default_impl_generics" { 901 | return Err(Error::new( 902 | default_impl_generics_ident.span(), 903 | "expected `default_impl_generics`.", 904 | )); 905 | } 906 | 907 | let Some(Item::Fn(ItemFn { 908 | sig: 909 | Signature { 910 | ident: default_use_generics_ident, 911 | generics: default_use_generics, 912 | .. 913 | }, 914 | .. 915 | })) = main_body.get(4) 916 | else { 917 | return Err(Error::new( 918 | item_mod_span, 919 | "the fifth item in `exported_tokens` should be an fn called `default_use_generics`.", 920 | )); 921 | }; 922 | if default_use_generics_ident != "default_use_generics" { 923 | return Err(Error::new( 924 | default_use_generics_ident.span(), 925 | "expected `default_use_generics`.", 926 | )); 927 | } 928 | 929 | let Some(Item::Mod(default_items_mod)) = main_body.get(5) else { 930 | return Err(Error::new( 931 | item_mod_span, 932 | "the sixth item in `exported_tokens` should be a module called `default_items_mod`.", 933 | )); 934 | }; 935 | if default_items_mod.ident != "default_items" { 936 | return Err(Error::new( 937 | default_items_mod.ident.span(), 938 | "expected `default_items`.", 939 | )); 940 | } 941 | let Some((_, default_items)) = default_items_mod.content.clone() else { 942 | return Err(Error::new( 943 | default_items_mod.ident.span(), 944 | "`default_items` item must be an inline module.", 945 | )); 946 | }; 947 | let default_items: Vec = default_items 948 | .iter() 949 | .map(|item| parse_quote!(#item)) 950 | .collect(); 951 | 952 | let Some(Item::Const(sealed_const)) = main_body.get(6) else { 953 | return Err(Error::new( 954 | item_mod_span, 955 | "the seventh item in `exported_tokens` should be a const specifying the sealed ident.", 956 | )); 957 | }; 958 | let sealed_ident = sealed_const.ident.clone(); 959 | 960 | Ok(ImportedTokens { 961 | const_fns, 962 | trait_impl_generics: trait_impl_generics.clone(), 963 | trait_use_generics: trait_use_generics.clone(), 964 | default_impl_generics: default_impl_generics.clone(), 965 | default_use_generics: default_use_generics.clone(), 966 | default_items: default_items, 967 | sealed_ident, 968 | }) 969 | } 970 | } 971 | 972 | trait GetIdent { 973 | fn get_ident(&self) -> Option; 974 | } 975 | 976 | impl GetIdent for TraitItem { 977 | fn get_ident(&self) -> Option { 978 | use TraitItem::*; 979 | match self { 980 | Const(item_const) => Some(item_const.ident.clone()), 981 | Fn(item_fn) => Some(item_fn.sig.ident.clone()), 982 | Type(item_type) => Some(item_type.ident.clone()), 983 | _ => None, 984 | } 985 | } 986 | } 987 | 988 | impl GetIdent for ImplItem { 989 | fn get_ident(&self) -> Option { 990 | use ImplItem::*; 991 | match self { 992 | Const(item_const) => Some(item_const.ident.clone()), 993 | Fn(item_fn) => Some(item_fn.sig.ident.clone()), 994 | Type(item_type) => Some(item_type.ident.clone()), 995 | _ => None, 996 | } 997 | } 998 | } 999 | 1000 | trait FlattenGroups { 1001 | fn flatten_groups(&self) -> TokenStream2; 1002 | } 1003 | 1004 | impl FlattenGroups for TokenStream2 { 1005 | fn flatten_groups(&self) -> TokenStream2 { 1006 | let mut iter = self.clone().into_iter(); 1007 | let mut final_tokens = TokenStream2::new(); 1008 | while let Some(token) = iter.next() { 1009 | if let TokenTree::Group(group) = &token { 1010 | let flattened = group.stream().flatten_groups(); 1011 | final_tokens.extend(quote!(<#flattened>)); 1012 | continue; 1013 | } 1014 | final_tokens.extend([token]); 1015 | } 1016 | final_tokens 1017 | } 1018 | } 1019 | 1020 | trait ForceGetIdent: ToTokens { 1021 | fn force_get_ident(&self) -> Ident { 1022 | let mut iter = self.to_token_stream().flatten_groups().into_iter(); 1023 | let mut final_tokens = TokenStream2::new(); 1024 | while let Some(token) = iter.next() { 1025 | let mut tmp = final_tokens.clone(); 1026 | tmp.extend([token.clone()]); 1027 | if parse2::(tmp).is_ok() { 1028 | final_tokens.extend([token]); 1029 | } 1030 | } 1031 | parse_quote!(#final_tokens) 1032 | } 1033 | } 1034 | 1035 | trait StripTrailingGenerics { 1036 | fn strip_trailing_generics(&self) -> Self; 1037 | } 1038 | 1039 | impl StripTrailingGenerics for Path { 1040 | fn strip_trailing_generics(&self) -> Self { 1041 | let mut tmp = self.clone(); 1042 | let Some(last) = tmp.segments.last_mut() else { 1043 | unreachable!() 1044 | }; 1045 | let ident = last.ident.clone(); 1046 | *last = parse_quote!(#ident); 1047 | tmp 1048 | } 1049 | } 1050 | 1051 | impl ForceGetIdent for T {} 1052 | 1053 | fn merge_generics(a: &Generics, b: &Generics) -> Generics { 1054 | let mut params = b.params.clone(); 1055 | params.extend(a.params.clone()); 1056 | let where_clause = match &a.where_clause { 1057 | Some(a_where) => match &b.where_clause { 1058 | Some(b_where) => { 1059 | let mut combined_where = b_where.clone(); 1060 | combined_where.predicates.extend(a_where.predicates.clone()); 1061 | Some(combined_where) 1062 | } 1063 | None => a.where_clause.clone(), 1064 | }, 1065 | None => b.where_clause.clone(), 1066 | }; 1067 | Generics { 1068 | lt_token: b.lt_token.clone(), 1069 | params, 1070 | gt_token: b.gt_token.clone(), 1071 | where_clause: where_clause, 1072 | } 1073 | } 1074 | 1075 | struct ReplaceSelfAssociatedType { 1076 | replace_prefix: TokenStream2, 1077 | } 1078 | 1079 | impl VisitMut for ReplaceSelfAssociatedType { 1080 | fn visit_type_path_mut(&mut self, type_path: &mut TypePath) { 1081 | if type_path.path.segments.len() < 2 { 1082 | return; 1083 | } 1084 | let first_seg = type_path.path.segments.first().unwrap(); 1085 | if first_seg.ident != "Self" { 1086 | return; 1087 | } 1088 | let segments = type_path.path.segments.iter().skip(1); 1089 | let replace = self.replace_prefix.clone(); 1090 | *type_path = parse_quote!(#replace::#(#segments)::*) 1091 | } 1092 | } 1093 | 1094 | struct ReplaceType { 1095 | search_type: RemappedGeneric, 1096 | replace_type: GenericParam, 1097 | } 1098 | 1099 | impl VisitMut for ReplaceType { 1100 | fn visit_ident_mut(&mut self, ident: &mut Ident) { 1101 | let search_ident = self.search_type.ident(); 1102 | if ident != search_ident { 1103 | return; 1104 | } 1105 | *ident = self.replace_type.force_get_ident(); 1106 | } 1107 | } 1108 | 1109 | struct ReplaceSelfType { 1110 | replace_type: Ident, 1111 | } 1112 | 1113 | impl VisitMut for ReplaceSelfType { 1114 | fn visit_ident_mut(&mut self, ident: &mut Ident) { 1115 | if ident != "Self" { 1116 | return; 1117 | } 1118 | *ident = self.replace_type.clone(); 1119 | } 1120 | } 1121 | 1122 | #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] 1123 | enum RemappedGeneric { 1124 | Lifetime(Ident), 1125 | Type(Ident), 1126 | Const(Ident), 1127 | } 1128 | 1129 | impl RemappedGeneric { 1130 | fn ident(&self) -> &Ident { 1131 | match self { 1132 | RemappedGeneric::Lifetime(ident) => ident, 1133 | RemappedGeneric::Type(ident) => ident, 1134 | RemappedGeneric::Const(ident) => ident, 1135 | } 1136 | } 1137 | } 1138 | 1139 | fn impl_supertrait_internal( 1140 | foreign_tokens: impl Into, 1141 | item_tokens: impl Into, 1142 | ) -> Result { 1143 | let mut item_impl = parse2::(item_tokens.into())?; 1144 | #[cfg(feature = "debug")] 1145 | let mut debug = false; 1146 | #[cfg(feature = "debug")] 1147 | for (i, attr) in item_impl.attrs.iter().enumerate() { 1148 | let Some(ident) = attr.path().get_ident() else { 1149 | continue; 1150 | }; 1151 | if ident == "debug_mode" { 1152 | debug = true; 1153 | item_impl.attrs.remove(i); 1154 | break; 1155 | } 1156 | } 1157 | let Some((_, trait_path, _)) = &mut item_impl.trait_ else { 1158 | return Err(Error::new( 1159 | item_impl.span(), 1160 | "#[impl_supertrait] can only be attached to non-inherent impls involving a trait \ 1161 | that has `#[supertrait]` attached to it.", 1162 | )); 1163 | }; 1164 | let impl_target = item_impl.self_ty.clone(); 1165 | 1166 | let ImportedTokens { 1167 | const_fns, 1168 | trait_impl_generics, 1169 | trait_use_generics: _, 1170 | default_impl_generics, 1171 | default_use_generics, 1172 | default_items, 1173 | sealed_ident, 1174 | } = ImportedTokens::try_from(parse2::(foreign_tokens.into())?)?; 1175 | 1176 | let mut remapped_type_params: HashMap = HashMap::new(); 1177 | for (i, param) in trait_impl_generics.params.iter().enumerate() { 1178 | let remapped = match param { 1179 | GenericParam::Lifetime(lifetime) => { 1180 | RemappedGeneric::Lifetime(lifetime.lifetime.ident.clone()) 1181 | } 1182 | GenericParam::Type(typ) => RemappedGeneric::Type(typ.ident.clone()), 1183 | GenericParam::Const(constant) => RemappedGeneric::Const(constant.ident.clone()), 1184 | }; 1185 | let last_seg = trait_path.segments.last().unwrap(); 1186 | let args: Vec = match last_seg.arguments.clone() { 1187 | syn::PathArguments::None => continue, 1188 | syn::PathArguments::AngleBracketed(args) => { 1189 | args.args.into_iter().map(|a| a.to_token_stream()).collect() 1190 | } 1191 | syn::PathArguments::Parenthesized(args) => args 1192 | .inputs 1193 | .into_iter() 1194 | .map(|a| a.to_token_stream()) 1195 | .collect(), 1196 | }; 1197 | 1198 | if i >= args.len() { 1199 | continue; 1200 | } 1201 | let target = args[i].clone(); 1202 | let target: GenericParam = parse_quote!(#target); 1203 | remapped_type_params.insert(remapped, target); 1204 | } 1205 | 1206 | // replace Self type with type we are implementing on 1207 | remapped_type_params.insert( 1208 | RemappedGeneric::Type(parse_quote!(__Self)), 1209 | parse_quote!(#impl_target), 1210 | ); 1211 | 1212 | let trait_mod = trait_path.clone().strip_trailing_generics(); 1213 | let trait_mod_ident = trait_mod.segments.last().unwrap().ident.clone(); 1214 | trait_path.segments.insert( 1215 | trait_path.segments.len() - 1, 1216 | parse_quote!(#trait_mod_ident), 1217 | ); 1218 | trait_path.segments.last_mut().unwrap().ident = parse_quote!(Trait); 1219 | 1220 | // strip default generics from default_use_generics 1221 | let mut filtered_tmp = FilteredGenerics { 1222 | impl_generics: default_impl_generics, 1223 | use_generics: default_use_generics, 1224 | has_defaults: HashSet::new(), 1225 | }; 1226 | filtered_tmp.strip_default_generics(); 1227 | let default_use_generics = filtered_tmp.use_generics; 1228 | 1229 | let mut final_items: HashMap = HashMap::new(); 1230 | for item in default_items { 1231 | let item_ident = item.get_ident().unwrap(); 1232 | let mut item: ImplItem = parse_quote!(#item); 1233 | use ImplItem::*; 1234 | match &mut item { 1235 | Const(item_const) => { 1236 | item_const.expr = parse_quote!(<#trait_mod::Defaults as #trait_mod::DefaultTypes #default_use_generics>::#item_ident) 1237 | } 1238 | Type(item_type) => { 1239 | item_type.ty = parse_quote!(<#trait_mod::Defaults as #trait_mod::DefaultTypes #default_use_generics>::#item_ident) 1240 | } 1241 | _ => unimplemented!("this item has no notion of defaults"), 1242 | } 1243 | for search in remapped_type_params.keys() { 1244 | let replace = &remapped_type_params[search]; 1245 | let mut visitor = ReplaceType { 1246 | search_type: search.clone(), 1247 | replace_type: replace.clone(), 1248 | }; 1249 | visitor.visit_impl_item_mut(&mut item); 1250 | } 1251 | if item_ident == "__Self" { 1252 | item = parse_quote!(#impl_target); 1253 | } 1254 | final_items.insert(item_ident, item); 1255 | } 1256 | let mut final_verbatim_items: Vec = Vec::new(); 1257 | for item in &item_impl.items { 1258 | let Some(item_ident) = item.get_ident() else { 1259 | final_verbatim_items.push(item.clone()); 1260 | continue; 1261 | }; 1262 | final_items.insert(item_ident, item.clone()); 1263 | } 1264 | 1265 | let mut final_items = final_items.values().cloned().collect::>(); 1266 | final_items.extend(final_verbatim_items); 1267 | item_impl.items = final_items; 1268 | 1269 | let mut impl_const_fn_idents: HashSet = HashSet::new(); 1270 | let mut impl_const_fns: Vec; 1271 | (impl_const_fns, item_impl.items) = item_impl.items.into_iter().partition(|item| { 1272 | let ImplItem::Fn(impl_item_fn) = item else { 1273 | return false; 1274 | }; 1275 | if impl_item_fn.sig.constness.is_none() { 1276 | return false; 1277 | }; 1278 | impl_const_fn_idents.insert(impl_item_fn.sig.ident.clone()); 1279 | true 1280 | }); 1281 | 1282 | for item in &const_fns { 1283 | if !impl_const_fn_idents.contains(&item.sig.ident) { 1284 | if item.default.is_none() { 1285 | return Err(Error::new( 1286 | item_impl.span(), 1287 | format!("missing impl for `{}`.", item.sig.ident), 1288 | )); 1289 | } 1290 | impl_const_fns.push(parse_quote!(#item)); 1291 | } 1292 | } 1293 | for const_fn in impl_const_fns.iter_mut() { 1294 | let mut last_seg = trait_path.segments.last().unwrap().clone(); 1295 | last_seg.ident = parse_quote!(Trait); 1296 | let mut visitor = ReplaceSelfAssociatedType { 1297 | replace_prefix: quote!(<#impl_target as #trait_mod::#last_seg>), 1298 | }; 1299 | visitor.visit_impl_item_mut(const_fn); 1300 | let ImplItem::Fn(const_fn) = const_fn else { 1301 | unreachable!() 1302 | }; 1303 | const_fn.vis = parse_quote!(pub); 1304 | } 1305 | 1306 | let impl_index = IMPL_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst); 1307 | let trait_import_name: Ident = format_ident!( 1308 | "{}{}TraitImpl_{}", 1309 | impl_target.clone().force_get_ident(), 1310 | item_impl.trait_.clone().unwrap().1.force_get_ident(), 1311 | impl_index, 1312 | ); 1313 | 1314 | let converted_const_fns = impl_const_fns.iter().map(|const_fn| { 1315 | let mut const_fn: ImplItemFn = parse_quote!(#const_fn); 1316 | const_fn.sig.constness = None; 1317 | const_fn.vis = Visibility::Inherited; 1318 | let item: ImplItem = parse_quote!(#const_fn); 1319 | item 1320 | }); 1321 | 1322 | let impl_const_fns = impl_const_fns.iter().map(|const_fn| { 1323 | let mut const_fn_visitor = FindGenericParam::new(&trait_impl_generics); 1324 | const_fn_visitor.visit_impl_item(const_fn); 1325 | let const_fn_generics = 1326 | filter_generics(&trait_impl_generics, &const_fn_visitor.usages).impl_generics; 1327 | let mut const_fn: ImplItemFn = parse_quote!(#const_fn); 1328 | const_fn.sig.generics = merge_generics(&const_fn_generics, &const_fn.sig.generics); 1329 | const_fn.sig.generics.params = const_fn 1330 | .sig 1331 | .generics 1332 | .params 1333 | .iter() 1334 | .cloned() 1335 | .map(|g| match g { 1336 | GenericParam::Lifetime(lifetime) => GenericParam::Lifetime(lifetime), 1337 | GenericParam::Type(typ) => { 1338 | let mut typ = typ.clone(); 1339 | typ.default = None; 1340 | GenericParam::Type(typ) 1341 | } 1342 | GenericParam::Const(constant) => { 1343 | let mut constant = constant.clone(); 1344 | constant.default = None; 1345 | GenericParam::Const(constant) 1346 | } 1347 | }) 1348 | .collect(); 1349 | const_fn 1350 | }); 1351 | 1352 | item_impl.items.extend(converted_const_fns); 1353 | let mut impl_visitor = FindGenericParam::new(&item_impl.generics); 1354 | impl_visitor.visit_item_impl(&item_impl); 1355 | let mut filtered_generics = filter_generics(&item_impl.generics, &impl_visitor.usages); 1356 | filtered_generics.strip_default_generics(); 1357 | item_impl.generics = filtered_generics.impl_generics; 1358 | 1359 | let inherent_impl = if impl_const_fns.len() > 0 { 1360 | Some(quote! { 1361 | // const fn implementations 1362 | impl #impl_target { 1363 | #(#impl_const_fns)* 1364 | } 1365 | }) 1366 | } else { 1367 | None 1368 | }; 1369 | 1370 | let output = quote! { 1371 | #item_impl 1372 | 1373 | impl #trait_mod::#sealed_ident for #impl_target {} 1374 | 1375 | #inherent_impl 1376 | 1377 | #[doc(hidden)] 1378 | #[allow(unused)] 1379 | use #trait_mod::Trait as #trait_import_name; 1380 | }; 1381 | #[cfg(feature = "debug")] 1382 | if debug { 1383 | output.pretty_print(); 1384 | } 1385 | Ok(output) 1386 | } 1387 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cargo test --workspace || exit 1 3 | cd macros || exit 1 4 | cargo publish || exit 1 5 | cd .. || exit 1 6 | cargo publish || exit 1 7 | echo "done" 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Supertrait 🦹 2 | //! 3 | //! [![Crates.io](https://img.shields.io/crates/v/supertrait)](https://crates.io/crates/supertrait) 4 | //! [![docs.rs](https://img.shields.io/docsrs/supertrait?label=docs)](https://docs.rs/supertrait/latest/supertrait/) 5 | //! [![Build Status](https://img.shields.io/github/actions/workflow/status/sam0x17/supertrait/ci.yaml)](https://github.com/sam0x17/supertrait/actions/workflows/ci.yaml?query=branch%3Amain) 6 | //! [![MIT License](https://img.shields.io/github/license/sam0x17/supertrait)](https://github.com/sam0x17/supertrait/blob/main/LICENSE) 7 | //! 8 | //! Supertrait is a revolutionary crate that enables _default associated types_ and _const fn trait 9 | //! items_ in stable Rust as of July 2023. Supertrait accomplishes this through a variety of 10 | //! macro-related techniques including the use of 11 | //! [macro_magic](https://crates.io/crates/macro_magic) as well as the "module wormhole" 12 | //! technique demonstrated in the docs for [`#[supertrait]`](`macro@supertrait`) and 13 | //! [`#[impl_supertrait]`](`macro@impl_supertrait`). 14 | //! 15 | //! Here is an end-to-end example: 16 | //! 17 | //! ``` 18 | //! use supertrait::*; 19 | //! 20 | //! #[supertrait] 21 | //! pub trait Fizz: Copy + Sized { 22 | //! type Foo = Option; 23 | //! type Bar; 24 | //! 25 | //! const fn double_value(val: T) -> (T, T) { 26 | //! (val, val) 27 | //! } 28 | //! 29 | //! const fn triple_value(val: T) -> (T, T, T); 30 | //! 31 | //! fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) { 32 | //! (*self, *self, plus) 33 | //! } 34 | //! 35 | //! const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 36 | //! } 37 | //! 38 | //! #[derive(Copy, Clone, PartialEq, Eq, Debug)] 39 | //! struct Buzz; 40 | //! 41 | //! #[impl_supertrait] 42 | //! impl Fizz for Buzz { 43 | //! type Bar = usize; 44 | //! 45 | //! const fn triple_value(val: T) -> (T, T, T) { 46 | //! (val, val, val) 47 | //! } 48 | //! 49 | //! const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T) { 50 | //! (b, Some(a), a) 51 | //! } 52 | //! } 53 | //! 54 | //! assert!(Buzz::triple_value(3).0 == 3); 55 | //! let buzz = Buzz {}; 56 | //! match buzz.interleave('h', false).1 { 57 | //! Some(c) => assert!(c == 'h'), 58 | //! None => unreachable!(), 59 | //! } 60 | //! let buzz = Buzz {}; 61 | //! assert_eq!(buzz.double_self_plus(Some(3)), (buzz, buzz, Some(3))); 62 | //! ``` 63 | //! 64 | //! Supertraits are also sealed such that a trait created via [`supertrait`] can only be impled 65 | //! if `#[impl_supertrait]` is attached to the impl statement. 66 | //! 67 | //! Default associated types are implemented in a way that should be nearly identical with how 68 | //! default associated types will function when they are eventually added to stable rust. 69 | //! 70 | //! Const fn trait items are implemented as _inherents_ on the underlying type, however their 71 | //! presence is enforced by [`impl_supertrait`] and their type bounds are enforced by the 72 | //! requirement for shadow non-const implementations of each const fn trait item that are 73 | //! filled in by the expansion of [`impl_supertrait`]. These two mechanisms along with the 74 | //! trait sealing technique mentioned above collectively ensure that const fn trait items 75 | //! presence and correctness is enforced just as strongly as that of regular trait items. 76 | //! 77 | //! Using inherents as the vehicle for implementing const fn trait items has a few drawbacks due to 78 | //! the naming collisions that can occur with existing inherent items as well as the inability to 79 | //! blanket impl supertraits containing const fns (because it is impossible in stable Rust to 80 | //! blanket impl anything other than a real trait). 81 | //! 82 | //! That said, inherents are a convenient fallback when you find yourself reaching for const fn 83 | //! items in traits, and supertrait contains the most convenient implementation of this behavior 84 | //! currently possible in stable Rust. 85 | 86 | #![no_std] 87 | #![warn(missing_docs)] 88 | 89 | pub use supertrait_macros::*; 90 | pub mod traits; 91 | 92 | /// Contains hidden re-exports of [`macro_magic`] and other required types. 93 | #[doc(hidden)] 94 | pub mod __private { 95 | pub use macro_magic; 96 | } 97 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | //! Contains various commonly-needed supertraits and supporting types. 2 | 3 | use crate::*; 4 | 5 | /// Implements [`CustomTypeId`] for the specified type and sets its type id to the specified 6 | /// literal u64 value., e.g. `impl_custom_type_id!(bool, 10681234549081409806);`. 7 | /// 8 | /// Specifying that two distinct types have the same `TYPE_ID` can lead to UB, so it is up to 9 | /// the implementer to ensure these remain globally unique. 10 | /// 11 | /// Used by [`ConstInto`]. 12 | #[macro_export] 13 | macro_rules! impl_custom_type_id { 14 | ($ty:ty, $val:literal) => { 15 | impl $crate::traits::CustomTypeId for $ty { 16 | const TYPE_ID: u64 = $val; 17 | } 18 | }; 19 | } 20 | 21 | /// A trait containing a default assocaited type `TYPE_ID` which is supposed to contain a 22 | /// globally unique `u64` value for all types that implement [`CustomTypeId`]. 23 | pub trait CustomTypeId { 24 | /// An associated const from [`CustomTypeId`] that identifies this type with a globally 25 | /// unique [`u64`]. Allowing the same two types to have the same `TYPE_ID` is UB. 26 | const TYPE_ID: u64; 27 | } 28 | 29 | impl_custom_type_id!(bool, 10681234549081409806); 30 | impl_custom_type_id!(char, 16675001002995532570); 31 | impl_custom_type_id!(usize, 13672001936412354477); 32 | impl_custom_type_id!(u8, 16098405448592660488); 33 | impl_custom_type_id!(u16, 11966005474853499082); 34 | impl_custom_type_id!(u32, 14174446889875098038); 35 | impl_custom_type_id!(u64, 15791760840720437152); 36 | impl_custom_type_id!(u128, 12185672395767536601); 37 | impl_custom_type_id!(isize, 15162561123529181013); 38 | impl_custom_type_id!(i8, 13119477278634758343); 39 | impl_custom_type_id!(i16, 13226206073516065561); 40 | impl_custom_type_id!(i32, 10624334564060841241); 41 | impl_custom_type_id!(i64, 14258752827364393809); 42 | impl_custom_type_id!(i128, 10725666691061222156); 43 | impl_custom_type_id!(str, 15226379227753928641); 44 | impl_custom_type_id!(&str, 10629156722039909512); 45 | 46 | set_supertrait_path!(crate); 47 | 48 | /// This supertrait can be used as a somewhat less useful substitute for being able to make use 49 | /// of `From` / `Into` in const scenarios. 50 | /// 51 | /// Because supertrait uses inherent impls as the backend for const fn trait items that are 52 | /// impled, you cannot implement `ConstInto` more than once for the same type. 53 | /// 54 | /// Thus the best way to support multiple types is to write a match statement based on 55 | /// `TYPE_ID` and use unsafe pointer dereferencing to handle each of the cases, such as the 56 | /// following: 57 | /// 58 | /// ``` 59 | /// use supertrait::*; 60 | /// use supertrait::traits::*; 61 | /// 62 | /// pub struct MyStruct { 63 | /// bool: bool, 64 | /// i32: i32, 65 | /// char: char, 66 | /// } 67 | /// 68 | /// #[impl_supertrait] 69 | /// impl ConstInto for MyStruct { 70 | /// const fn const_into(&self) -> &T { 71 | /// match T::TYPE_ID { 72 | /// bool::TYPE_ID => unsafe { &*((&self.bool as *const bool) as *const T) }, 73 | /// i32::TYPE_ID => unsafe { &*((&self.i32 as *const i32) as *const T) }, 74 | /// char::TYPE_ID => unsafe { &*((&self.char as *const char) as *const T) }, 75 | /// _ => panic!("unsupported type"), 76 | /// } 77 | /// } 78 | /// } 79 | /// ``` 80 | #[supertrait] 81 | pub trait ConstInto { 82 | /// Converts `self` into the specified type. Panics if the specified type is not supported. 83 | const fn const_into(&self) -> &T; 84 | } 85 | 86 | /// This supertrait allows you to write const-compatible implementations of [`Clone`]. 87 | /// 88 | /// A bound is included ensuring that you cannot implement this for types that don't already 89 | /// implement [`Clone`]. 90 | /// 91 | /// Types that implement [`Copy`] are trivial to support here, since they can be dereferenced 92 | /// to create a copy that can be used as the return result for [`ConstClone`]. 93 | #[supertrait] 94 | pub trait ConstClone { 95 | /// Clones self. Usable in const contexts. 96 | const fn const_clone(&self) -> Self 97 | where 98 | Self: Clone; 99 | } 100 | 101 | /// This supertrait allows you to write const-compatible implementations of [`PartialEq`]. 102 | /// 103 | /// While this cannot be used to replace `==` in const contexts, sometimes it is useful to have 104 | /// analogues of built-in major traits in a const context. 105 | /// 106 | /// THe default associated type `Other` defaults to `Self` but can be overridden to determine 107 | /// equality between `self` and other types. 108 | #[supertrait] 109 | pub trait ConstPartialEq { 110 | /// The type of the item we are comparing `self` with. 111 | type Other = Self; 112 | 113 | /// Returns `true` if `self` is equal to `other`. Otherwise returns `false`. 114 | /// 115 | /// Usable in const contexts. 116 | const fn const_eq(&self, other: &Self::Other) -> bool; 117 | 118 | /// Returns `false` if `self` is equal to `other`. Otherwise returns `true`. 119 | /// 120 | /// Usable in const contexts. Has a default implementation based on the negation of 121 | /// `const_eq`. 122 | const fn const_ne(&self, other: &Self::Other) -> bool { 123 | !self.const_eq(other) 124 | } 125 | } 126 | 127 | set_supertrait_path!(::supertrait); 128 | -------------------------------------------------------------------------------- /tests/generated.rs: -------------------------------------------------------------------------------- 1 | //! These tests were generated by Chat GPT 4 2 | 3 | use supertrait::*; 4 | 5 | #[supertrait(debug)] 6 | pub trait DefaultedType { 7 | type Output: Default = i32; 8 | fn default() -> Self::Output; 9 | } 10 | 11 | #[supertrait] 12 | pub trait ConstFn { 13 | const SIZE: usize; 14 | fn const_size() -> usize; 15 | } 16 | 17 | #[supertrait] 18 | pub trait SuperTrait: DefaultedType::Trait + ConstFn::Trait {} 19 | 20 | pub struct TypeWithDefault; 21 | 22 | #[impl_supertrait] 23 | impl DefaultedType for TypeWithDefault { 24 | fn default() -> Self::Output { 25 | 0 26 | } 27 | } 28 | 29 | pub struct TypeWithConst; 30 | 31 | #[impl_supertrait] 32 | impl ConstFn for TypeWithConst { 33 | const SIZE: usize = 10; 34 | fn const_size() -> usize { 35 | Self::SIZE 36 | } 37 | } 38 | 39 | pub struct SuperType; 40 | 41 | #[impl_supertrait] 42 | impl DefaultedType for SuperType { 43 | fn default() -> Self::Output { 44 | 0 45 | } 46 | } 47 | 48 | #[impl_supertrait] 49 | impl ConstFn for SuperType { 50 | const SIZE: usize = 10; 51 | fn const_size() -> usize { 52 | Self::SIZE 53 | } 54 | } 55 | 56 | #[impl_supertrait] 57 | impl SuperTrait for SuperType {} 58 | 59 | #[test] 60 | fn test_defaulted_type_generic() { 61 | assert_eq!(::default(), 0); 62 | } 63 | 64 | #[test] 65 | fn test_const_fn_generic() { 66 | assert_eq!(TypeWithConst::const_size(), 10); 67 | } 68 | 69 | #[test] 70 | fn test_super_trait_generic() { 71 | assert_eq!(SuperType::default(), 0); 72 | assert_eq!(SuperType::const_size(), 10); 73 | } 74 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use supertrait::traits::*; 4 | use supertrait::*; 5 | 6 | #[supertrait] 7 | pub trait MyTrait { 8 | fn some_inherent_method(&self) -> u32; 9 | type Something = u32; // default associated type 10 | const fn yet_another_thing() -> bool; // const fn 11 | const fn something_else(&self) -> usize; // const fn self 12 | type SomethingOverridden = usize; // default associated type that gets overridden 13 | const SOME_CONSTANT: u8 = 7; 14 | } 15 | 16 | struct SomeStruct; 17 | 18 | #[impl_supertrait] 19 | impl MyTrait for SomeStruct { 20 | fn some_inherent_method(&self) -> u32 { 21 | 44 22 | } 23 | 24 | type SomethingOverridden = bool; 25 | 26 | const fn yet_another_thing() -> bool { 27 | false 28 | } 29 | 30 | const fn something_else(&self) -> usize { 31 | 178 32 | } 33 | } 34 | 35 | #[test] 36 | fn test_default_associated_types() { 37 | let x = SomeStruct; 38 | assert_eq!(x.some_inherent_method(), 44); 39 | let _y: ::Something = 7; 40 | let _z: ::SomethingOverridden = false; 41 | assert_eq!(::SOME_CONSTANT, 7); 42 | } 43 | 44 | #[test] 45 | const fn test_const_fn_trait_items() { 46 | SomeStruct::yet_another_thing(); 47 | SomeStruct {}.something_else(); 48 | } 49 | 50 | pub struct MyStruct { 51 | bool: bool, 52 | i32: i32, 53 | char: char, 54 | } 55 | 56 | #[impl_supertrait] 57 | impl ConstInto for MyStruct { 58 | const fn const_into(&self) -> &T { 59 | match T::TYPE_ID { 60 | bool::TYPE_ID => unsafe { &*((&self.bool as *const bool) as *const T) }, 61 | i32::TYPE_ID => unsafe { &*((&self.i32 as *const i32) as *const T) }, 62 | char::TYPE_ID => unsafe { &*((&self.char as *const char) as *const T) }, 63 | _ => panic!("unsupported type"), 64 | } 65 | } 66 | } 67 | 68 | #[test] 69 | const fn test_const_into() { 70 | let s = MyStruct { 71 | bool: true, 72 | i32: -67, 73 | char: 'h', 74 | }; 75 | assert!(*s.const_into::()); 76 | assert!(*s.const_into::() == -67); 77 | assert!(*s.const_into::() == 'h'); 78 | } 79 | 80 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] 81 | struct WrappedU8(u8); 82 | 83 | impl From for WrappedU8 { 84 | fn from(value: u8) -> Self { 85 | WrappedU8(value) 86 | } 87 | } 88 | 89 | impl From for u8 { 90 | fn from(value: WrappedU8) -> Self { 91 | value.0 92 | } 93 | } 94 | 95 | #[supertrait] 96 | pub trait RIntoWithDATs<'i, 'o, O: PartialEq + 'o> 97 | where 98 | Self: Into, 99 | O: Into, 100 | { 101 | type InputRef = Self; 102 | type OutputRef = &'o O; 103 | type InputOption = Option; 104 | type OutputOption = Option; 105 | type Unspecified; 106 | } 107 | 108 | #[impl_supertrait] 109 | impl<'o> RIntoWithDATs<'_, 'o, WrappedU8> for u8 { 110 | type Unspecified = (usize, usize); 111 | type OutputRef = &'o bool; 112 | } 113 | 114 | #[test] 115 | fn test_generics_in_default_associated_types() { 116 | let _a: >::Unspecified = (1, 2); 117 | let _b: >::OutputRef = &false; 118 | let _c: >::OutputOption = None; 119 | let _b: >::InputRef = 3u8; 120 | } 121 | 122 | #[impl_supertrait] 123 | impl ConstClone for WrappedU8 { 124 | const fn const_clone(&self) -> Self 125 | where 126 | Self: Clone, 127 | { 128 | *self 129 | } 130 | } 131 | 132 | #[test] 133 | const fn test_const_clone() { 134 | let a = WrappedU8(3); 135 | let _b: WrappedU8 = a.const_clone(); 136 | assert!(_b.0 == 3); 137 | } 138 | 139 | #[impl_supertrait] 140 | impl ConstPartialEq for WrappedU8 { 141 | const fn const_eq(&self, other: &Self) -> bool { 142 | self.0 == other.0 143 | } 144 | } 145 | 146 | #[test] 147 | const fn test_const_partial_eq() { 148 | let a = WrappedU8(1); 149 | let b = WrappedU8(2); 150 | let c = WrappedU8(1); 151 | assert!(a.const_ne(&b)); 152 | assert!(a.const_eq(&c)); 153 | } 154 | 155 | // intentionally incorrect implementation 156 | #[impl_supertrait] 157 | impl ConstPartialEq for MyStruct { 158 | const fn const_eq(&self, _other: &Self) -> bool { 159 | false 160 | } 161 | 162 | const fn const_ne(&self, _other: &Self) -> bool { 163 | false 164 | } 165 | } 166 | 167 | #[test] 168 | const fn test_incorrect_supertrait() { 169 | let a = MyStruct { 170 | bool: true, 171 | i32: 3, 172 | char: 'a', 173 | }; 174 | let b = MyStruct { 175 | bool: true, 176 | i32: 3, 177 | char: 'a', 178 | }; 179 | assert!(!a.const_eq(&b)); 180 | assert!(!a.const_ne(&b)); 181 | } 182 | 183 | #[supertrait] 184 | pub trait ConstTraitWithGenerics { 185 | type Something: Copy; 186 | type SomethingWithDefault = u8; 187 | 188 | const fn something_using_i() -> I; 189 | const fn something_using_t(val: T) -> T; 190 | const fn something_using_something() -> Self::Something; 191 | fn something() -> Self::Something; 192 | const fn something_using_something_with_default( 193 | val: Self::SomethingWithDefault, 194 | ) -> Self::SomethingWithDefault; 195 | } 196 | 197 | #[impl_supertrait] 198 | impl ConstTraitWithGenerics for MyStruct { 199 | type Something = u64; 200 | 201 | const fn something_using_i() -> i32 { 202 | 44 203 | } 204 | 205 | const fn something_using_t(val: T) -> T { 206 | val 207 | } 208 | 209 | const fn something_using_something() -> Self::Something { 210 | 47 211 | } 212 | 213 | fn something() -> Self::Something { 214 | 33 215 | } 216 | 217 | const fn something_using_something_with_default( 218 | val: Self::SomethingWithDefault, 219 | ) -> Self::SomethingWithDefault { 220 | val 221 | } 222 | } 223 | 224 | #[test] 225 | const fn test_const_trait_generics() { 226 | assert!(MyStruct::something_using_something::() == 47u64); 227 | assert!(MyStruct::something_using_i() == 44i32); 228 | assert!(MyStruct::something_using_t::(-33) == -33); 229 | assert!(MyStruct::something_using_something_with_default::(23u8) == 23u8); 230 | } 231 | 232 | #[supertrait] 233 | pub trait Fizz: Copy + Sized { 234 | type Foo = Option; 235 | type Bar; 236 | 237 | const fn double_value(val: T) -> (T, T) { 238 | (val, val) 239 | } 240 | 241 | const fn triple_value(val: T) -> (T, T, T); 242 | 243 | fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) { 244 | (*self, *self, plus) 245 | } 246 | 247 | const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T); 248 | } 249 | 250 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 251 | struct Buzz; 252 | 253 | #[impl_supertrait] 254 | impl Fizz for Buzz { 255 | type Bar = usize; 256 | 257 | const fn triple_value(val: T) -> (T, T, T) { 258 | (val, val, val) 259 | } 260 | 261 | const fn interleave(&self, a: T, b: I) -> (I, Self::Foo, T) { 262 | (b, Some(a), a) 263 | } 264 | } 265 | 266 | #[test] 267 | const fn test_buzz_const() { 268 | assert!(Buzz::triple_value(3).0 == 3); 269 | let buzz = Buzz {}; 270 | match buzz.interleave('h', false).1 { 271 | Some(c) => assert!(c == 'h'), 272 | None => unreachable!(), 273 | } 274 | } 275 | 276 | #[test] 277 | fn test_buzz_default_associated_types() { 278 | let buzz = Buzz {}; 279 | assert_eq!(buzz.double_self_plus(Some(3)), (buzz, buzz, Some(3))) 280 | } 281 | 282 | pub mod some_mod { 283 | pub mod some_sub_mod { 284 | pub mod sub_sub { 285 | pub trait Something {} 286 | } 287 | pub struct Wiz; 288 | #[supertrait::supertrait] 289 | pub trait TraitCapturingLocalType: sub_sub::Something { 290 | const fn pass_something(val: T) -> T; 291 | fn some_method(val: T) -> T; 292 | type SomeType = Wiz; 293 | } 294 | } 295 | } 296 | 297 | #[derive(PartialEq, Eq, Debug)] 298 | struct Fred; 299 | 300 | impl some_mod::some_sub_mod::sub_sub::Something for Fred {} 301 | 302 | #[impl_supertrait] 303 | impl some_mod::some_sub_mod::TraitCapturingLocalType for Fred { 304 | const fn pass_something(val: T) -> T { 305 | val 306 | } 307 | 308 | fn some_method(val: T) -> T { 309 | val 310 | } 311 | } 312 | 313 | #[test] 314 | fn test_wormhole_module_teleportation() { 315 | const A: Fred = Fred::pass_something(Fred {}); 316 | let b: Fred = Fred::some_method(Fred {}); 317 | assert_eq!(A, b); 318 | } 319 | 320 | #[supertrait] 321 | pub trait TraitWithDefaultGenerics { 322 | type Something = T; 323 | 324 | const fn something_using_t(val: T) -> T; 325 | } 326 | 327 | struct TestingTraitWithDefaultGenerics; 328 | 329 | #[impl_supertrait] 330 | impl TraitWithDefaultGenerics for TestingTraitWithDefaultGenerics { 331 | const fn something_using_t(val: bool) -> bool { 332 | val 333 | } 334 | } 335 | 336 | #[test] 337 | const fn test_default_generics_a() { 338 | assert!(TestingTraitWithDefaultGenerics::something_using_t(true)); 339 | } 340 | 341 | #[supertrait] 342 | pub trait DefaultGenericsOverridden { 343 | type Something = T; 344 | 345 | const fn something_using_t(val: T) -> T; 346 | } 347 | 348 | struct TestingDefaultGenericsOverridden; 349 | 350 | #[impl_supertrait] 351 | impl DefaultGenericsOverridden for TestingDefaultGenericsOverridden { 352 | type Something = u8; 353 | 354 | const fn something_using_t(val: T) -> T { 355 | val 356 | } 357 | } 358 | 359 | #[test] 360 | const fn test_default_generics_b() { 361 | assert!(TestingDefaultGenericsOverridden::something_using_t(-89i64) == -89i64); 362 | } 363 | 364 | #[supertrait] 365 | pub trait DefaultGenericsOverriddenSpecific { 366 | type Something = T; 367 | 368 | const fn something_using_t(val: T) -> T; 369 | } 370 | 371 | struct TestingDefaultGenericsOverriddenSpecific; 372 | 373 | #[impl_supertrait] 374 | impl DefaultGenericsOverriddenSpecific for TestingDefaultGenericsOverriddenSpecific { 375 | type Something = u8; 376 | 377 | const fn something_using_t(val: i32) -> i32 { 378 | val 379 | } 380 | } 381 | 382 | #[test] 383 | const fn test_default_generics_c() { 384 | assert!(TestingDefaultGenericsOverriddenSpecific::something_using_t(-33) == -33); 385 | let _a: >::Something = 5u8; 386 | } 387 | --------------------------------------------------------------------------------