syn icon indicating copy to clipboard operation
syn copied to clipboard

Should `~const` be a variant of `syn::TraitBoundModifier` among with `None` and `Maybe(Token![?])`?

Open JohnScience opened this issue 3 years ago • 1 comments

In combination,

#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]

Nightly features (https://github.com/rust-lang/rust/issues/67792) allow constantly implement traits.

According to clarfonthey

The variance is mostly in terms of what implementations of traits are okay -- so, const Trait essentially would mean that impl const is required, whereas ~const Trait means that a non-const impl will just make the function non-const, instead of preventing usage altogether.

Yes, we don't have the ability to have T: const Trait bounds, but the idea is that the const should be covariant, which is why the ~const is there. This can be compared to a simple T: Trait bound, which will not affect the constness of the function, e.g. T: Copy.

Mostly, the matter is making sure that things are consistent with const being an opt-in thing, while also not confusing the syntax with the contravariant ?Sized.

Currently, ~const is sort of supported at syn::TraitBound level yet treated as a part of syn::Path.

Excerpt from src/generics.rs:

#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
    impl Parse for TraitBound {
        fn parse(input: ParseStream) -> Result<Self> {
            #[cfg(feature = "full")]
            let tilde_const = if input.peek(Token![~]) && input.peek2(Token![const]) {
                let tilde_token = input.parse::<Token![~]>()?;
                let const_token = input.parse::<Token![const]>()?;
                Some((tilde_token, const_token))
            } else {
                None
            };

            let modifier: TraitBoundModifier = input.parse()?;
            let lifetimes: Option<BoundLifetimes> = input.parse()?;

            let mut path: Path = input.parse()?;
            if path.segments.last().unwrap().arguments.is_empty()
                && (input.peek(token::Paren) || input.peek(Token![::]) && input.peek3(token::Paren))
            {
                input.parse::<Option<Token![::]>>()?;
                let args: ParenthesizedGenericArguments = input.parse()?;
                let parenthesized = PathArguments::Parenthesized(args);
                path.segments.last_mut().unwrap().arguments = parenthesized;
            }

            #[cfg(feature = "full")]
            {
                if let Some((tilde_token, const_token)) = tilde_const {
                    path.segments.insert(
                        0,
                        PathSegment {
                            ident: Ident::new("const", const_token.span),
                            arguments: PathArguments::None,
                        },
                    );
                    let (_const, punct) = path.segments.pairs_mut().next().unwrap().into_tuple();
                    *punct.unwrap() = Token![::](tilde_token.span);
                }
            }

            Ok(TraitBound {
                paren_token: None,
                modifier,
                lifetimes,
                path,
            })
        }
    }

I believe it makes more sense to parse ~const as syn::TraitBoundModifier.

JohnScience avatar Feb 12 '22 23:02 JohnScience

I've implemented https://github.com/JohnScience/unconst_trait_impl based on some source code of syn. It's a bit messy yet you can track the differences between the source code of syn and mine. It's been made in a hurry yet it seems to work.

JohnScience avatar Feb 17 '22 06:02 JohnScience

~const is explicitly provisional syntax and is not backed by any RFC approved by the lang team. See https://github.com/rust-lang/rust/issues/67792: "This RFC has not yet been accepted. It is being implemented on a provisional basis to evaluate the potential fallout."

I don't believe it belongs in syn's syntax tree.

dtolnay avatar Mar 17 '23 20:03 dtolnay