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

Don't synthesize host effect params for trait associated functions marked const #119505

Merged
merged 4 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1254,11 +1254,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
coroutine_kind: Option<CoroutineKind>,
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header);
// Don't pass along the user-provided constness of trait associated functions; we don't want to
// synthesize a host effect param for them. We reject `const` on them during AST validation.
let constness = if kind == FnDeclKind::Inherent { sig.header.constness } else { Const::No };
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
});
let (generics, decl) = self.lower_generics(generics, constness, id, &itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
});
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}

Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,21 @@ ast_passes_tilde_const_disallowed = `~const` is not allowed here
.item = this item cannot have `~const` trait bounds

ast_passes_trait_fn_const =
functions in traits cannot be declared const
.label = functions in traits cannot be const
functions in {$in_impl ->
[true] trait impls
*[false] traits
} cannot be declared const
.label = functions in {$in_impl ->
[true] trait impls
*[false] traits
} cannot be const
.const_context_label = this declares all associated functions implicitly const
.remove_const_sugg = remove the `const`{$requires_multiple_changes ->
[true] {" ..."}
*[false] {""}
}
.make_impl_const_sugg = ... and declare the impl to be const instead
.make_trait_const_sugg = ... and declare the trait to be a `#[const_trait]` instead

ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted

Expand Down
130 changes: 96 additions & 34 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,29 @@ enum DisallowTildeConstContext<'a> {
Item,
}

enum TraitOrTraitImpl<'a> {
Trait { span: Span, constness: Option<Span> },
TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: &'a TraitRef },
}

impl<'a> TraitOrTraitImpl<'a> {
fn constness(&self) -> Option<Span> {
match self {
Self::Trait { constness: Some(span), .. }
| Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
_ => None,
}
}
}

struct AstValidator<'a> {
session: &'a Session,
features: &'a Features,

/// The span of the `extern` in an `extern { ... }` block, if any.
extern_mod: Option<&'a Item>,

/// Are we inside a trait impl?
in_trait_impl: bool,

/// Are we inside a const trait defn or impl?
in_const_trait_or_impl: bool,
outer_trait_or_trait_impl: Option<TraitOrTraitImpl<'a>>,

has_proc_macro_decls: bool,

Expand All @@ -78,24 +89,28 @@ struct AstValidator<'a> {
impl<'a> AstValidator<'a> {
fn with_in_trait_impl(
&mut self,
is_in: bool,
constness: Option<Const>,
trait_: Option<(Const, ImplPolarity, &'a TraitRef)>,
f: impl FnOnce(&mut Self),
) {
let old = mem::replace(&mut self.in_trait_impl, is_in);
let old_const = mem::replace(
&mut self.in_const_trait_or_impl,
matches!(constness, Some(Const::Yes(_))),
let old = mem::replace(
&mut self.outer_trait_or_trait_impl,
trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl {
constness,
polarity,
trait_ref,
}),
);
f(self);
self.in_trait_impl = old;
self.in_const_trait_or_impl = old_const;
self.outer_trait_or_trait_impl = old;
}

fn with_in_trait(&mut self, is_const: bool, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.in_const_trait_or_impl, is_const);
fn with_in_trait(&mut self, span: Span, constness: Option<Span>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(
&mut self.outer_trait_or_trait_impl,
Some(TraitOrTraitImpl::Trait { span, constness }),
);
f(self);
self.in_const_trait_or_impl = old;
self.outer_trait_or_trait_impl = old;
}

fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
Expand Down Expand Up @@ -291,10 +306,48 @@ impl<'a> AstValidator<'a> {
}
}

fn check_trait_fn_not_const(&self, constness: Const) {
if let Const::Yes(span) = constness {
self.dcx().emit_err(errors::TraitFnConst { span });
}
fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl<'a>) {
let Const::Yes(span) = constness else {
return;
};

let make_impl_const_sugg = if self.features.const_trait_impl
&& let TraitOrTraitImpl::TraitImpl {
constness: Const::No,
polarity: ImplPolarity::Positive,
trait_ref,
} = parent
{
Some(trait_ref.path.span.shrink_to_lo())
} else {
None
};

let make_trait_const_sugg = if self.features.const_trait_impl
&& let TraitOrTraitImpl::Trait { span, constness: None } = parent
{
Some(span.shrink_to_lo())
} else {
None
};

let parent_constness = parent.constness();
self.dcx().emit_err(errors::TraitFnConst {
span,
in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
const_context_label: parent_constness,
remove_const_sugg: (
self.session.source_map().span_extend_while(span, |c| c == ' ').unwrap_or(span),
match parent_constness {
Some(_) => rustc_errors::Applicability::MachineApplicable,
None => rustc_errors::Applicability::MaybeIncorrect,
},
),
requires_multiple_changes: make_impl_const_sugg.is_some()
|| make_trait_const_sugg.is_some(),
make_impl_const_sugg,
make_trait_const_sugg,
});
}

fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) {
Expand Down Expand Up @@ -817,7 +870,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self_ty,
items,
}) => {
self.with_in_trait_impl(true, Some(*constness), |this| {
self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
this.visibility_not_permitted(
&item.vis,
errors::VisibilityNotPermittedNote::TraitImpl,
Expand Down Expand Up @@ -963,8 +1016,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
let is_const_trait = attr::contains_name(&item.attrs, sym::const_trait);
self.with_in_trait(is_const_trait, |this| {
let is_const_trait =
attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
self.with_in_trait(item.span, is_const_trait, |this| {
if *is_auto == IsAuto::Yes {
// Auto traits cannot have generics, super traits nor contain items.
this.deny_generic_params(generics, item.ident.span);
Expand All @@ -977,8 +1031,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// context for the supertraits.
this.visit_vis(&item.vis);
this.visit_ident(item.ident);
let disallowed =
(!is_const_trait).then(|| DisallowTildeConstContext::Trait(item.span));
let disallowed = is_const_trait
.is_none()
.then(|| DisallowTildeConstContext::Trait(item.span));
this.with_tilde_const(disallowed, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
Expand Down Expand Up @@ -1342,7 +1397,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

let tilde_const_allowed =
matches!(fk.header(), Some(FnHeader { constness: ast::Const::Yes(_), .. }))
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)) if self.in_const_trait_or_impl);
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)))
&& self
.outer_trait_or_trait_impl
.as_ref()
.and_then(TraitOrTraitImpl::constness)
.is_some();

let disallowed = (!tilde_const_allowed).then(|| DisallowTildeConstContext::Fn(fk));
self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
Expand All @@ -1353,7 +1413,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_nomangle_item_asciionly(item.ident, item.span);
}

if ctxt == AssocCtxt::Trait || !self.in_trait_impl {
if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() {
self.check_defaultness(item.span, item.kind.defaultness());
}

Expand Down Expand Up @@ -1401,10 +1461,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
);
}

if ctxt == AssocCtxt::Trait || self.in_trait_impl {
if let Some(parent) = &self.outer_trait_or_trait_impl {
self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl);
if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
self.check_trait_fn_not_const(sig.header.constness);
self.check_trait_fn_not_const(sig.header.constness, parent);
}
}

Expand All @@ -1414,7 +1474,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

match &item.kind {
AssocItemKind::Fn(box Fn { sig, generics, body, .. })
if self.in_const_trait_or_impl
if self
.outer_trait_or_trait_impl
.as_ref()
.and_then(TraitOrTraitImpl::constness)
.is_some()
|| ctxt == AssocCtxt::Trait
|| matches!(sig.header.constness, Const::Yes(_)) =>
{
Expand All @@ -1430,8 +1494,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
);
self.visit_fn(kind, item.span, item.id);
}
_ => self
.with_in_trait_impl(false, None, |this| visit::walk_assoc_item(this, item, ctxt)),
_ => self.with_in_trait_impl(None, |this| visit::walk_assoc_item(this, item, ctxt)),
}
}
}
Expand Down Expand Up @@ -1547,8 +1610,7 @@ pub fn check_crate(
session,
features,
extern_mod: None,
in_trait_impl: false,
in_const_trait_or_impl: false,
outer_trait_or_trait_impl: None,
has_proc_macro_decls: false,
outer_impl_trait: None,
disallow_tilde_const: Some(DisallowTildeConstContext::Item),
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Errors emitted by ast_passes.

use rustc_ast::ParamKindOrd;
use rustc_errors::AddToDiagnostic;
use rustc_errors::{AddToDiagnostic, Applicability};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};

Expand Down Expand Up @@ -49,6 +49,24 @@ pub struct TraitFnConst {
#[primary_span]
#[label]
pub span: Span,
pub in_impl: bool,
#[label(ast_passes_const_context_label)]
pub const_context_label: Option<Span>,
#[suggestion(ast_passes_remove_const_sugg, code = "")]
pub remove_const_sugg: (Span, Applicability),
pub requires_multiple_changes: bool,
#[suggestion(
ast_passes_make_impl_const_sugg,
code = "const ",
applicability = "maybe-incorrect"
)]
pub make_impl_const_sugg: Option<Span>,
#[suggestion(
ast_passes_make_trait_const_sugg,
code = "#[const_trait]\n",
applicability = "maybe-incorrect"
)]
pub make_trait_const_sugg: Option<Span>,
}

#[derive(Diagnostic)]
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0379.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Erroneous code example:
trait Foo {
const fn bar() -> u32; // error!
}
impl Foo for () {
const fn bar() -> u32 { 0 } // error!
}
```

Trait methods cannot be declared `const` by design. For more information, see
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {

if is_host_effect {
if let Some(idx) = host_effect_index {
bug!("parent also has host effect param? index: {idx}, def: {def_id:?}");
tcx.dcx().span_delayed_bug(
param.span,
format!("parent also has host effect param? index: {idx}, def: {def_id:?}"),
);
}

host_effect_index = Some(index as usize);
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/const-fn-mismatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ trait Foo {

impl Foo for u32 {
const fn f() -> u32 {
//~^ ERROR functions in traits cannot be declared const
//~^ ERROR functions in trait impls cannot be declared const
22
}
}
Expand Down
7 changes: 5 additions & 2 deletions tests/ui/consts/const-fn-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
error[E0379]: functions in traits cannot be declared const
error[E0379]: functions in trait impls cannot be declared const
--> $DIR/const-fn-mismatch.rs:11:5
|
LL | const fn f() -> u32 {
| ^^^^^ functions in traits cannot be const
| ^^^^^-
| |
| functions in trait impls cannot be const
| help: remove the `const`

error: aborting due to 1 previous error

Expand Down
10 changes: 8 additions & 2 deletions tests/ui/consts/const-fn-not-in-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ error[E0379]: functions in traits cannot be declared const
--> $DIR/const-fn-not-in-trait.rs:5:5
|
LL | const fn f() -> u32;
| ^^^^^ functions in traits cannot be const
| ^^^^^-
| |
| functions in traits cannot be const
| help: remove the `const`

error[E0379]: functions in traits cannot be declared const
--> $DIR/const-fn-not-in-trait.rs:7:5
|
LL | const fn g() -> u32 {
| ^^^^^ functions in traits cannot be const
| ^^^^^-
| |
| functions in traits cannot be const
| help: remove the `const`

error: aborting due to 2 previous errors

Expand Down
5 changes: 4 additions & 1 deletion tests/ui/consts/issue-54954.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ error[E0379]: functions in traits cannot be declared const
--> $DIR/issue-54954.rs:5:5
|
LL | const fn const_val<T: Sized>() -> usize {
| ^^^^^ functions in traits cannot be const
| ^^^^^-
| |
| functions in traits cannot be const
| help: remove the `const`

error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
--> $DIR/issue-54954.rs:1:24
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/feature-gates/feature-gate-min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ trait Foo {
}

impl Foo for u32 {
const fn foo() -> u32 { 0 } //~ ERROR functions in traits cannot be declared const
const fn foo() -> u32 { 0 } //~ ERROR functions in trait impls cannot be declared const
}

trait Bar {}
Expand Down
Loading
Loading