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

Rollup of 2 pull requests #119243

Closed
wants to merge 6 commits into from
Closed
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
12 changes: 10 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,8 +754,7 @@ pub enum PatKind {
Ident(BindingAnnotation, Ident, Option<P<Pat>>),

/// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
/// The `bool` is `true` in the presence of a `..`.
Struct(Option<P<QSelf>>, Path, ThinVec<PatField>, /* recovered */ bool),
Struct(Option<P<QSelf>>, Path, ThinVec<PatField>, PatFieldsRest),

/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
TupleStruct(Option<P<QSelf>>, Path, ThinVec<P<Pat>>),
Expand Down Expand Up @@ -812,6 +811,15 @@ pub enum PatKind {
MacCall(P<MacCall>),
}

/// Whether the `..` is present in a struct fields pattern.
#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
pub enum PatFieldsRest {
/// `module::StructName { field, ..}`
Rest,
/// `module::StructName { field }`
None,
}

/// The kind of borrow in an `AddrOf` expression,
/// e.g., `&place` or `&raw const place`.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: self.lower_span(f.span),
}
}));
break hir::PatKind::Struct(qpath, fs, *etc);
break hir::PatKind::Struct(qpath, fs, *etc == ast::PatFieldsRest::Rest);
}
PatKind::Tuple(pats) => {
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,7 @@ impl<'a> State<'a> {
}
self.nbsp();
self.word("{");
let empty = fields.is_empty() && !etc;
let empty = fields.is_empty() && *etc == ast::PatFieldsRest::None;
if !empty {
self.space();
}
Expand All @@ -1445,7 +1445,7 @@ impl<'a> State<'a> {
},
|f| f.pat.span,
);
if *etc {
if *etc == ast::PatFieldsRest::Rest {
if !fields.is_empty() {
self.word_space(",");
}
Expand Down
187 changes: 40 additions & 147 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_infer::traits::ObligationCause;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
use rustc_middle::ty::{TraitRef, TypeVisitableExt};
use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
use rustc_middle::ty::{Instance, InstanceDef, TypeVisitableExt};
use rustc_mir_dataflow::Analysis;
use rustc_span::{sym, Span, Symbol};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};

use std::mem;
Expand Down Expand Up @@ -756,143 +754,43 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
infcx.err_ctxt().report_fulfillment_errors(errors);
}

let mut is_trait = false;
// Attempting to call a trait method?
// FIXME(effects) do we need this?
if let Some(trait_id) = tcx.trait_of_item(callee) {
if tcx.trait_of_item(callee).is_some() {
trace!("attempting to call a trait method");
if !self.tcx.features().const_trait_impl {
// trait method calls are only permitted when `effects` is enabled.
// we don't error, since that is handled by typeck. We try to resolve
// the trait into the concrete method, and uses that for const stability
// checks.
// FIXME(effects) we might consider moving const stability checks to typeck as well.
if tcx.features().effects {
is_trait = true;

if let Ok(Some(instance)) =
Instance::resolve(tcx, param_env, callee, fn_args)
&& let InstanceDef::Item(def) = instance.def
{
// Resolve a trait method call to its concrete implementation, which may be in a
// `const` trait impl. This is only used for the const stability check below, since
// we want to look at the concrete impl's stability.
fn_args = instance.args;
callee = def;
}
} else {
self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: Some(sym::const_trait_impl),
feature: Some(if tcx.features().const_trait_impl {
sym::effects
} else {
sym::const_trait_impl
}),
});
return;
}

let trait_ref = TraitRef::from_method(tcx, trait_id, fn_args);
let obligation =
Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);

let implsrc = {
let infcx = tcx.infer_ctxt().build();
let mut selcx = SelectionContext::new(&infcx);
selcx.select(&obligation)
};

match implsrc {
Ok(Some(ImplSource::Param(_))) if tcx.features().effects => {
debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env
);
return;
}
// Closure: Fn{Once|Mut}
Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
if trait_ref.self_ty().is_closure()
&& tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
{
let ty::Closure(closure_def_id, fn_args) = *trait_ref.self_ty().kind()
else {
unreachable!()
};
if !tcx.is_const_fn_raw(closure_def_id) {
self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
});

return;
}
}
Ok(Some(ImplSource::UserDefined(data))) => {
let callee_name = tcx.item_name(callee);

if let hir::Constness::NotConst = tcx.constness(data.impl_def_id) {
self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
});
return;
}

if let Some(&did) = tcx
.associated_item_def_ids(data.impl_def_id)
.iter()
.find(|did| tcx.item_name(**did) == callee_name)
{
// using internal args is ok here, since this is only
// used for the `resolve` call below
fn_args = GenericArgs::identity_for_item(tcx, did);
callee = did;
}
}
_ if !tcx.is_const_fn_raw(callee) => {
// At this point, it is only legal when the caller is in a trait
// marked with #[const_trait], and the callee is in the same trait.
let mut nonconst_call_permission = false;
if let Some(callee_trait) = tcx.trait_of_item(callee)
&& tcx.has_attr(callee_trait, sym::const_trait)
&& Some(callee_trait) == tcx.trait_of_item(caller.to_def_id())
// Can only call methods when it's `<Self as TheTrait>::f`.
&& tcx.types.self_param == fn_args.type_at(0)
{
nonconst_call_permission = true;
}

if !nonconst_call_permission {
let obligation = Obligation::new(
tcx,
ObligationCause::dummy_with_span(*fn_span),
param_env,
trait_ref,
);

// improve diagnostics by showing what failed. Our requirements are stricter this time
// as we are going to error again anyways.
let infcx = tcx.infer_ctxt().build();
if let Err(e) = implsrc {
infcx.err_ctxt().report_selection_error(
obligation.clone(),
&obligation,
&e,
);
}

self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
});
return;
}
}
_ => {}
}

// Resolve a trait method call to its concrete implementation, which may be in a
// `const` trait impl.
let instance = Instance::resolve(tcx, param_env, callee, fn_args);
debug!("Resolving ({:?}) -> {:?}", callee, instance);
if let Ok(Some(func)) = instance {
if let InstanceDef::Item(def) = func.def {
callee = def;
}
}
}

// At this point, we are calling a function, `callee`, whose `DefId` is known...
Expand Down Expand Up @@ -925,21 +823,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return;
}

if !tcx.is_const_fn_raw(callee) {
if !tcx.is_const_default_method(callee) {
// To get to here we must have already found a const impl for the
// trait, but for it to still be non-const can be that the impl is
// using default method bodies.
self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
});
return;
}
if !tcx.is_const_fn_raw(callee) && !is_trait {
self.check_op(ops::FnCallNonConst {
caller,
callee,
args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
});
return;
}

// If the `const fn` we are trying to call is not const-stable, ensure that we have
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ impl<'a> ExtCtxt<'a> {
path: ast::Path,
field_pats: ThinVec<ast::PatField>,
) -> P<ast::Pat> {
self.pat(span, PatKind::Struct(None, path, field_pats, false))
self.pat(span, PatKind::Struct(None, path, field_pats, ast::PatFieldsRest::None))
}
pub fn pat_tuple(&self, span: Span, pats: ThinVec<P<ast::Pat>>) -> P<ast::Pat> {
self.pat(span, PatKind::Tuple(pats))
Expand Down
17 changes: 9 additions & 8 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
assert!(self_ty.is_none());
}

let arg_count = check_generic_arg_count(
let mut arg_count = check_generic_arg_count(
tcx,
span,
def_id,
Expand Down Expand Up @@ -560,6 +560,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
inferred_params: vec![],
infer_args,
};
if let ty::BoundConstness::ConstIfConst = constness
&& generics.has_self
&& !tcx.has_attr(def_id, sym::const_trait)
{
let e = tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span });
arg_count.correct =
Err(GenericArgCountMismatch { reported: Some(e), invalid_args: vec![] });
}
let args = create_args_for_parent_generic_args(
tcx,
def_id,
Expand All @@ -570,13 +578,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&mut args_ctx,
);

if let ty::BoundConstness::ConstIfConst = constness
&& generics.has_self
&& !tcx.has_attr(def_id, sym::const_trait)
{
tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span });
}

(args, arg_count)
}

Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::{
self as ast, AttrVec, BindingAnnotation, ByRef, Expr, ExprKind, MacCall, Mutability, Pat,
PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
Expand Down Expand Up @@ -891,7 +891,8 @@ impl<'a> Parser<'a> {
e.span_label(path.span, "while parsing the fields for this pattern");
e.emit();
self.recover_stmt();
(ThinVec::new(), true)
// When recovering, pretend we had `Foo { .. }`, to avoid cascading errors.
(ThinVec::new(), PatFieldsRest::Rest)
});
self.bump();
Ok(PatKind::Struct(qself, path, fields, etc))
Expand Down Expand Up @@ -965,9 +966,9 @@ impl<'a> Parser<'a> {
}

/// Parses the fields of a struct-like pattern.
fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, bool)> {
fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)> {
let mut fields = ThinVec::new();
let mut etc = false;
let mut etc = PatFieldsRest::None;
let mut ate_comma = true;
let mut delayed_err: Option<DiagnosticBuilder<'a>> = None;
let mut first_etc_and_maybe_comma_span = None;
Expand Down Expand Up @@ -1001,7 +1002,7 @@ impl<'a> Parser<'a> {
|| self.check_noexpect(&token::DotDotDot)
|| self.check_keyword(kw::Underscore)
{
etc = true;
etc = PatFieldsRest::Rest;
let mut etc_sp = self.token.span;
if first_etc_and_maybe_comma_span.is_none() {
if let Some(comma_tok) = self
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ fn extend_with_struct_pat(
qself1: &Option<P<ast::QSelf>>,
path1: &ast::Path,
fps1: &mut [ast::PatField],
rest1: bool,
rest1: ast::PatFieldsRest,
start: usize,
alternatives: &mut ThinVec<P<Pat>>,
) -> bool {
Expand Down
12 changes: 9 additions & 3 deletions src/tools/rustfmt/src/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,15 @@ impl Rewrite for Pat {
None,
None,
),
PatKind::Struct(ref qself, ref path, ref fields, ellipsis) => {
rewrite_struct_pat(qself, path, fields, ellipsis, self.span, context, shape)
}
PatKind::Struct(ref qself, ref path, ref fields, rest) => rewrite_struct_pat(
qself,
path,
fields,
rest == ast::PatFieldsRest::Rest,
self.span,
context,
shape,
),
PatKind::MacCall(ref mac) => {
rewrite_macro(mac, None, context, shape, MacroPosition::Pat)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/auxiliary/closure-in-foreign-crate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![crate_type = "lib"]
#![feature(const_closures, const_trait_impl)]
#![feature(const_closures, const_trait_impl, effects)]
#![allow(incomplete_features)]

pub const fn test() {
Expand Down
Loading
Loading