Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling methods on generic parameters of const fns #2632

Closed
wants to merge 37 commits into from

Conversation

oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Feb 5, 2019

TLDR: Allow

const fn add<T: Add>(a: T, b: T) -> T::Output {
    a + b
}

and

pub struct Foo<T: Trait>(T);
impl<T: ?const Trait> Foo<T> {
    fn new(t: T) -> Self {
        // not calling methods on `t`, so we opt out of requiring
        // `<T as Trait>` to have const methods via `?const`
        Self(t)
    }
}

cc @Centril @varkor @RalfJung @eddyb

This RFC has gone through an extensive pre-RFC phase in rust-lang/const-eval#8

Rendered

Triage: pFCP comment: #2632 (comment)

@varkor
Copy link
Member

varkor commented Feb 5, 2019

One alternative syntax design that is not mentioned here is the question of const impl versus impl const.

There are strong arguments that const impl is the more consistent syntax, as it is consistent both with the existing practice of prefixing modifiers to impl (default impl, unsafe impl, etc.) and prefixing const to keywords to indicate const variants (e.g. const fn [and the hypothetical const trait]).

Conversely, as far as I'm aware, impl const is not consistent with any existing syntax in the language or this RFC.


(I don't like to start the discussion with syntax bikeshedding, but as mentioned in the original post, this RFC has already ungone significant design discussion and I'm satisfied it's close to the optimal conservative design.)

@Centril Centril added T-lang Relevant to the language team, which will review and decide on the RFC. A-syntax Syntax related proposals & ideas A-typesystem Type system related proposals & ideas A-const-eval Proposals relating to compile time evaluation (CTFE). labels Feb 5, 2019
@rpjohnst
Copy link

rpjohnst commented Feb 5, 2019

How do we want -> impl Trait and arg: dyn Trait to interact with const fn? If you must specify -> impl const Trait, does it also make sense to require arg: dyn const Trait? Both cases feel more existential-y and thus less tied to the function's type parameters, so I kind of lean that way for consistency.

@varkor
Copy link
Member

varkor commented Feb 5, 2019

I imagine it makes most sense to use the same (syntactic) rules for impl Trait and dyn Trait as for parameter bounds: that is, in const fn, an impl Trait actually means impl const Trait/const impl Trait (and similarly for dyn) and impl ?const Trait/?const impl Trait allows opting-out of constness.

@mark-i-m

This comment has been minimized.

Co-Authored-By: oli-obk <github35764891676564198441@oli-obk.de>
@ExpHP
Copy link

ExpHP commented Feb 6, 2019

I'm not sure why const Drop must be recursive on fields. In my mental model, the body of drop doesn't have calls to the fields' drop impls inserted, but rather, it's the role of drop glue to piece them all together:

{
    let local = get_thing();

    // Implicitly inserted
    // (each one is individually omitted if that type
    //  does not explicitly implement Drop)
    Drop::drop(&mut local);
    Drop::drop(&mut local.field_1);
    Drop::drop(&mut local.field_2);
}

Requiring const Drop on fields increases churn when library A cannot write const Drop yet due to a non-const Drop in library B. (so to use A in a const context, changes now need to be made to both library B and A in that order, instead of just B).


(one could argue that allowing impl const Drop in library A when a field is non-const Drop could lead to misleading documentation; but another thread of discussion on this PR seems to be arriving at the conclusion that we additionally require an auto-trait like ConstDrop, which partially mitigates this concern)

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 10, 2019

I don’t think this has been mentioned before, but putting const before impl would be more consistent with other modifiers like unsafe and pub if we decided to do that.

It has been mentioned before. This was even the original syntax. Reasoning for the change can be found at rust-lang/const-eval#8 (comment)

cc @scottmcm for consistency (not in semantics, but user expectations) I have slowly come back around to const impl Trait for Type being the best syntax.

@ExpHP
Copy link

ExpHP commented Feb 10, 2019

Ultimately the location of const is really nothing but the color of the bikeshed. impl const may be different from the other things, but it is not that hard to remember this "exception" when you have things like T: ?const Trait.

Conceptually, the keyword is attached to the trait. I.e. it's not an impl const of a Trait, but rather an impl of a const Trait.

@Centril Centril self-assigned this Feb 14, 2019
@scottmcm
Copy link
Member

Conversely, as far as I'm aware, impl const is not consistent with any existing syntax in the language or this RFC.

I think that's looking at it from the wrong perspective.

It's not that it's (impl const) Trait, but that it's impl (const Trait), the same as you can impl (dyn Trait). And that's consistent with something like struct Foo<T: const Default>(T); or similar.

@mark-i-m
Copy link
Member

But as far as I can tell, the Constness is not a property of the Trait, but of the impl, which is not the same for dyn, right?

@Centril
Copy link
Contributor

Centril commented Feb 14, 2019

It's not that it's (impl const) Trait, but that it's impl (const Trait), the same as you can impl (dyn Trait). And that's consistent with something like struct Foo<T: const Default>(T); or similar.

I agree with this in particular when viewed in light of effect systems. Moreover, impl const Trait also has composability benefits, e.g. arg: impl Add + ?const Clone + const Debug vs. arg: const impl Add + Cone + Debug are probably not the same semantically.

But as far as I can tell, the Constness is not a property of the Trait, but of the impl, which is not the same for dyn, right?

Traits can be seen as logical requirements and implementations are proofs that those requirements are satisfied. In that light, e.g. T: const Foo can be seen as a constraint that T satisfies const Foo and impl const Foo for MyT is a witness for that.

@Centril Centril added the A-effects Effects related proposals & ideas label Feb 16, 2019
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
text/0000-const-generic-const-fn-bounds.md Outdated Show resolved Hide resolved
Co-Authored-By: oli-obk <github35764891676564198441@oli-obk.de>
@oli-obk
Copy link
Contributor Author

oli-obk commented Mar 4, 2021

May be you can refer me to better place to ask, but would it be appropriate to consider a case where author of the trait wants to enforce that any reasonable trait implementation would be a const function? So there would be no ambiguity of implementing const trait for type and just implementing a trait for type, and it may be a good starting point that may satisfy 80% of use cases

That sounds like something that belongs in the "future extensions" section. It sounds like something that may be desirable for guarantees similar to unsafe trait Foo declarations.

@RalfJung
Copy link
Member

A problem came up with the original plan, described in rust-lang/rust#83452 and on Zulip: under some conditions, we currently accept const fn on stable that have trait bounds in scope; if we say that all trait bounds on const fn are implicitly const Trait (as proposed in this RFC), that would be a breaking change for callers of these functions.

@nikomatsakis
Copy link
Contributor

Should we try to revert that support, @RalfJung? I think it was accidental.

@RalfJung
Copy link
Member

@nikomatsakis That might be good if we want to keep our options open -- but @oli-obk has the overview here, I'm just reporting what came up elsewhere. I don't currently have the bandwidth to work on this.

I personally am leaning towards a different design than the one proposed in the RFC, one where there is no implicit "callable in const context" -- that would be more consistent with &dyn Trait and fn() which can already be created in const. That design doesn't mind this accident. But this is a discussion for the RFC.

@jhpratt
Copy link
Member

jhpratt commented Jun 12, 2021

What's the status of the RFC? FCP was proposed over two years ago, and is currently blocked by @withoutboats. I will note that boats is an alumnus of the lang team. While I am not dismissing their concerns outright, they have had zero public activity on GitHub in over six months. At this point, is it worth cancelling and restarting the checkboxes? This would permit the current lang team to register the same concerns if they still exist.

cc lang team: @joshtriplett @nikomatsakis @cramertj @pnkfelix @scottmcm

@burdges
Copy link

burdges commented Jun 13, 2021

I'd assume experimentation going on based upon #2632 (comment) ? If I read correctly, there was a rather extensive discussion around boats' third issue, which prompted a desire to experiment.

@jhpratt
Copy link
Member

jhpratt commented Jun 13, 2021

Yeah, the main reason I asked is because said experimentation had taken place and been on nightly for a while now.

@oli-obk
Copy link
Contributor Author

oli-obk commented Jun 13, 2021

There are currently still a few things that make the experimental feature useless in practice. We don't have the implementation capacity right now to work on it, but I do plan on more things happening here this year.

@nikomatsakis
Copy link
Contributor

I talked to @oli-obk today and we agreed that we will close this RFC for now. The content is likely still desired but we want to do more experimentation and will re-open when the design seems closer to settled.

@rfcbot rfcbot removed proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Jul 6, 2021
@pubfnbar
Copy link

pubfnbar commented Aug 3, 2021

IIUC, this RFC would make trait-associated constants unusable in const contexts if the trait bound is not declared const (possibly implicitly). I think this would be a design mistake - an unnecessary reduction of usability. Conceptually, associated constants should be usable in const contexts always, irrespective of the constness of the trait implementation or the trait bound (setting aside for now the implementation difficulties of proving the equality of different constant expressions). Here's an example that wouldn't compile given the current design:

trait Foo {
    fn foo(value: u8) -> u8;
}

trait Bar {
    const BAR: u8;

    fn bar(value: u8) -> u8;
}

const fn baz<T>() -> u8
where T: Foo + ?const Bar {
    T::foo(T::BAR) // error: `<T as Bar>` used in a const context
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Proposals relating to compile time evaluation (CTFE). A-effects Effects related proposals & ideas A-syntax Syntax related proposals & ideas A-typesystem Type system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.