Merge from rustc

This commit is contained in:
The Miri Conjob Bot 2024-02-18 05:00:49 +00:00
commit 3ef558ec15
174 changed files with 9862 additions and 3253 deletions

View file

@ -2541,9 +2541,6 @@ dependencies = [
[[package]]
name = "miropt-test-tools"
version = "0.1.0"
dependencies = [
"regex",
]
[[package]]
name = "native-tls"

View file

@ -12,10 +12,12 @@
#![allow(internal_features)]
#![feature(rustdoc_internals)]
#![feature(associated_type_bounds)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![cfg_attr(bootstrap, feature(min_specialization))]
#![feature(never_type)]
#![feature(negative_impls)]
#![feature(stmt_expr_attributes)]

File diff suppressed because it is too large Load diff

View file

@ -57,6 +57,7 @@ use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::{ConstArg, GenericArg, ItemLocalMap, ParamName, TraitCandidate};
use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_macros::extension;
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_session::parse::{add_feature_diagnostics, feature_err};
@ -190,16 +191,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
trait ResolverAstLoweringExt {
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>;
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>;
fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>>;
fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
}
impl ResolverAstLoweringExt for ResolverAstLowering {
#[extension(trait ResolverAstLoweringExt)]
impl ResolverAstLowering {
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> {
if let ExprKind::Path(None, path) = &expr.kind {
// Don't perform legacy const generics rewriting if the path already

View file

@ -229,7 +229,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
seen_spans.insert(move_span);
}
use_spans.var_path_only_subdiag(&mut err, desired_action);
use_spans.var_path_only_subdiag(self.dcx(), &mut err, desired_action);
if !is_loop_move {
err.span_label(
@ -291,18 +291,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if needs_note {
if let Some(local) = place.as_local() {
let span = self.body.local_decls[local].source_info.span;
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move,
ty,
place: &note_msg,
span,
});
err.subdiagnostic(
self.dcx(),
crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move,
ty,
place: &note_msg,
span,
},
);
} else {
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
is_partial_move,
ty,
place: &note_msg,
});
err.subdiagnostic(
self.dcx(),
crate::session_diagnostics::TypeNoCopy::Note {
is_partial_move,
ty,
place: &note_msg,
},
);
};
}
@ -557,7 +563,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
E0381,
"{used} binding {desc}{isnt_initialized}"
);
use_spans.var_path_only_subdiag(&mut err, desired_action);
use_spans.var_path_only_subdiag(self.dcx(), &mut err, desired_action);
if let InitializationRequiringAction::PartialAssignment
| InitializationRequiringAction::Assignment = desired_action
@ -848,9 +854,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&value_msg,
);
borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
borrow_spans.var_path_only_subdiag(
self.dcx(),
&mut err,
crate::InitializationRequiringAction::Borrow,
);
move_spans.var_subdiag(None, &mut err, None, |kind, var_span| {
move_spans.var_subdiag(self.dcx(), &mut err, None, |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
@ -895,7 +905,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_span,
&self.describe_any_place(borrow.borrowed_place.as_ref()),
);
borrow_spans.var_subdiag(None, &mut err, Some(borrow.kind), |kind, var_span| {
borrow_spans.var_subdiag(self.dcx(), &mut err, Some(borrow.kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
let place = &borrow.borrowed_place;
let desc_place = self.describe_any_place(place.as_ref());
@ -1043,7 +1053,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
"mutably borrow",
);
borrow_spans.var_subdiag(
None,
self.dcx(),
&mut err,
Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }),
|kind, var_span| {
@ -1131,22 +1141,31 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
};
if issued_spans == borrow_spans {
borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
place: desc_place,
var_span,
is_single_var: false,
},
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
borrow_spans.var_subdiag(
self.dcx(),
&mut err,
Some(gen_borrow_kind),
|kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
place: desc_place,
var_span,
is_single_var: false,
},
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
BorrowUsePlaceClosure {
place: desc_place,
var_span,
is_single_var: false,
}
}
}
}
});
},
);
} else {
issued_spans.var_subdiag(
Some(self.dcx()),
self.dcx(),
&mut err,
Some(issued_borrow.kind),
|kind, var_span| {
@ -1165,7 +1184,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
borrow_spans.var_subdiag(
Some(self.dcx()),
self.dcx(),
&mut err,
Some(gen_borrow_kind),
|kind, var_span| {
@ -2217,7 +2236,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.span_label(borrow_span, "borrowed value does not live long enough");
err.span_label(drop_span, format!("`{name}` dropped here while still borrowed"));
borrow_spans.args_subdiag(&mut err, |args_span| {
borrow_spans.args_subdiag(self.dcx(), &mut err, |args_span| {
crate::session_diagnostics::CaptureArgLabel::Capture {
is_within: borrow_spans.for_coroutine(),
args_span,
@ -2476,7 +2495,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None,
);
borrow_spans.args_subdiag(&mut err, |args_span| {
borrow_spans.args_subdiag(self.dcx(), &mut err, |args_span| {
crate::session_diagnostics::CaptureArgLabel::Capture {
is_within: borrow_spans.for_coroutine(),
args_span,
@ -2935,7 +2954,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
"assign",
);
loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
loan_spans.var_subdiag(self.dcx(), &mut err, Some(loan.kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
@ -2953,7 +2972,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
loan_spans.var_subdiag(self.dcx(), &mut err, Some(loan.kind), |kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },

View file

@ -124,7 +124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
let did = did.expect_local();
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
diag.eager_subdiagnostic(
diag.subdiagnostic(
self.dcx(),
OnClosureNote::InvokedTwice {
place_name: &ty::place_to_string_for_capture(
@ -146,7 +146,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
let did = did.expect_local();
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
diag.eager_subdiagnostic(
diag.subdiagnostic(
self.dcx(),
OnClosureNote::MovedTwice {
place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
@ -587,11 +587,12 @@ impl UseSpans<'_> {
/// Add a span label to the arguments of the closure, if it exists.
pub(super) fn args_subdiag(
self,
dcx: &rustc_errors::DiagCtxt,
err: &mut Diagnostic,
f: impl FnOnce(Span) -> CaptureArgLabel,
) {
if let UseSpans::ClosureUse { args_span, .. } = self {
err.subdiagnostic(f(args_span));
err.subdiagnostic(dcx, f(args_span));
}
}
@ -599,6 +600,7 @@ impl UseSpans<'_> {
/// only adds label to the `path_span`
pub(super) fn var_path_only_subdiag(
self,
dcx: &rustc_errors::DiagCtxt,
err: &mut Diagnostic,
action: crate::InitializationRequiringAction,
) {
@ -607,20 +609,26 @@ impl UseSpans<'_> {
if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
match closure_kind {
hir::ClosureKind::Coroutine(_) => {
err.subdiagnostic(match action {
Borrow => BorrowInCoroutine { path_span },
MatchOn | Use => UseInCoroutine { path_span },
Assignment => AssignInCoroutine { path_span },
PartialAssignment => AssignPartInCoroutine { path_span },
});
err.subdiagnostic(
dcx,
match action {
Borrow => BorrowInCoroutine { path_span },
MatchOn | Use => UseInCoroutine { path_span },
Assignment => AssignInCoroutine { path_span },
PartialAssignment => AssignPartInCoroutine { path_span },
},
);
}
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
err.subdiagnostic(match action {
Borrow => BorrowInClosure { path_span },
MatchOn | Use => UseInClosure { path_span },
Assignment => AssignInClosure { path_span },
PartialAssignment => AssignPartInClosure { path_span },
});
err.subdiagnostic(
dcx,
match action {
Borrow => BorrowInClosure { path_span },
MatchOn | Use => UseInClosure { path_span },
Assignment => AssignInClosure { path_span },
PartialAssignment => AssignPartInClosure { path_span },
},
);
}
}
}
@ -629,32 +637,32 @@ impl UseSpans<'_> {
/// Add a subdiagnostic to the use of the captured variable, if it exists.
pub(super) fn var_subdiag(
self,
dcx: Option<&rustc_errors::DiagCtxt>,
dcx: &rustc_errors::DiagCtxt,
err: &mut Diagnostic,
kind: Option<rustc_middle::mir::BorrowKind>,
f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
) {
if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
if capture_kind_span != path_span {
err.subdiagnostic(match kind {
Some(kd) => match kd {
rustc_middle::mir::BorrowKind::Shared
| rustc_middle::mir::BorrowKind::Fake => {
CaptureVarKind::Immut { kind_span: capture_kind_span }
}
err.subdiagnostic(
dcx,
match kind {
Some(kd) => match kd {
rustc_middle::mir::BorrowKind::Shared
| rustc_middle::mir::BorrowKind::Fake => {
CaptureVarKind::Immut { kind_span: capture_kind_span }
}
rustc_middle::mir::BorrowKind::Mut { .. } => {
CaptureVarKind::Mut { kind_span: capture_kind_span }
}
rustc_middle::mir::BorrowKind::Mut { .. } => {
CaptureVarKind::Mut { kind_span: capture_kind_span }
}
},
None => CaptureVarKind::Move { kind_span: capture_kind_span },
},
None => CaptureVarKind::Move { kind_span: capture_kind_span },
});
);
};
let diag = f(closure_kind, path_span);
match dcx {
Some(hd) => err.eager_subdiagnostic(hd, diag),
None => err.subdiagnostic(diag),
};
err.subdiagnostic(dcx, diag);
}
}
@ -1025,26 +1033,33 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
CallKind::FnCall { fn_trait_id, .. }
if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
{
err.subdiagnostic(CaptureReasonLabel::Call {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::Call {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
},
);
err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span });
}
CallKind::Operator { self_arg, .. } => {
let self_arg = self_arg.unwrap();
err.subdiagnostic(CaptureReasonLabel::OperatorUse {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::OperatorUse {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
},
);
if self.fn_self_span_reported.insert(fn_span) {
err.subdiagnostic(CaptureReasonNote::LhsMoveByOperator {
span: self_arg.span,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonNote::LhsMoveByOperator { span: self_arg.span },
);
}
}
CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
@ -1061,11 +1076,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
let func = tcx.def_path_str(method_did);
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonNote::FuncTakeSelf {
func,
place_name: place_name.clone(),
span: self_arg.span,
},
);
}
let parent_did = tcx.parent(method_did);
let parent_self_ty =
@ -1079,7 +1097,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
});
if is_option_or_result && maybe_reinitialized_locations_is_empty {
err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::BorrowContent { var_span },
);
}
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
let ty = moved_place.ty(self.body, tcx).ty;
@ -1093,18 +1114,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
_ => false,
};
if suggest {
err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
ty,
span: move_span.shrink_to_lo(),
});
err.subdiagnostic(
self.dcx(),
CaptureReasonSuggest::IterateSlice {
ty,
span: move_span.shrink_to_lo(),
},
);
}
err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::ImplicitCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
},
);
// If the moved place was a `&mut` ref, then we can
// suggest to reborrow it where it was moved, so it
// will still be valid by the time we get to the usage.
@ -1128,19 +1155,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
} else {
if let Some((CallDesugaringKind::Await, _)) = desugaring {
err.subdiagnostic(CaptureReasonLabel::Await {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::Await {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
},
);
} else {
err.subdiagnostic(CaptureReasonLabel::MethodCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::MethodCall {
fn_call_span,
place_name: &place_name,
is_partial,
is_loop_message,
},
);
}
// Erase and shadow everything that could be passed to the new infcx.
let ty = moved_place.ty(self.body, tcx).ty;
@ -1155,7 +1188,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
&& self.infcx.can_eq(self.param_env, ty, self_ty)
{
err.eager_subdiagnostic(
err.subdiagnostic(
self.dcx(),
CaptureReasonSuggest::FreshReborrow {
span: move_span.shrink_to_hi(),
@ -1239,17 +1272,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
} else {
if move_span != span || is_loop_message {
err.subdiagnostic(CaptureReasonLabel::MovedHere {
move_span,
is_partial,
is_move_msg,
is_loop_message,
});
err.subdiagnostic(
self.dcx(),
CaptureReasonLabel::MovedHere {
move_span,
is_partial,
is_move_msg,
is_loop_message,
},
);
}
// If the move error occurs due to a loop, don't show
// another message for the same span
if !is_loop_message {
move_spans.var_subdiag(None, err, None, |kind, var_span| match kind {
move_spans.var_subdiag(self.dcx(), err, None, |kind, var_span| match kind {
hir::ClosureKind::Coroutine(_) => {
CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
}

View file

@ -448,12 +448,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
None => "value".to_string(),
};
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: place_ty,
place: &place_desc,
span,
});
err.subdiagnostic(
self.dcx(),
crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: place_ty,
place: &place_desc,
span,
},
);
} else {
binds_to.sort();
binds_to.dedup();
@ -475,14 +478,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
Some(desc) => format!("`{desc}`"),
None => "value".to_string(),
};
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: place_ty,
place: &place_desc,
span,
});
err.subdiagnostic(
self.dcx(),
crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: place_ty,
place: &place_desc,
span,
},
);
use_spans.args_subdiag(err, |args_span| {
use_spans.args_subdiag(self.dcx(), err, |args_span| {
crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
place: place_desc,
args_span,
@ -580,12 +586,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if binds_to.len() == 1 {
let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: bind_to.ty,
place: place_desc,
span: binding_span,
});
err.subdiagnostic(
self.dcx(),
crate::session_diagnostics::TypeNoCopy::Label {
is_partial_move: false,
ty: bind_to.ty,
place: place_desc,
span: binding_span,
},
);
}
}

View file

@ -229,7 +229,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
if suggest {
borrow_spans.var_subdiag(
None,
self.dcx(),
&mut err,
Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
|_kind, var_span| {

View file

@ -616,13 +616,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
let upvar_span = upvars_map.get(&def_hir).unwrap().span;
diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
diag.subdiagnostic(self.dcx(), VarHereDenote::Defined { span: upvar_def_span });
diag.subdiagnostic(self.dcx(), VarHereDenote::Captured { span: upvar_span });
}
}
if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span });
diag.subdiagnostic(self.dcx(), VarHereDenote::FnMutInferred { span: fr_span });
}
self.suggest_move_on_borrowing_closure(&mut diag);
@ -788,7 +788,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
},
};
diag.subdiagnostic(err_category);
diag.subdiagnostic(self.dcx(), err_category);
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
@ -979,7 +979,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
ident.span,
"calling this method introduces the `impl`'s `'static` requirement",
);
err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
err.subdiagnostic(self.dcx(), RequireStaticErr::UsedImpl { multi_span });
err.span_suggestion_verbose(
span.shrink_to_hi(),
"consider relaxing the implicit `'static` requirement",

View file

@ -2,6 +2,7 @@ use crate::location::{LocationIndex, LocationTable};
use crate::BorrowIndex;
use polonius_engine::AllFacts as PoloniusFacts;
use polonius_engine::Atom;
use rustc_macros::extension;
use rustc_middle::mir::Local;
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::move_paths::MovePathIndex;
@ -24,20 +25,10 @@ impl polonius_engine::FactTypes for RustcFacts {
pub type AllFacts = PoloniusFacts<RustcFacts>;
pub(crate) trait AllFactsExt {
#[extension(pub(crate) trait AllFactsExt)]
impl AllFacts {
/// Returns `true` if there is a need to gather `AllFacts` given the
/// current `-Z` flags.
fn enabled(tcx: TyCtxt<'_>) -> bool;
fn write_to_dir(
&self,
dir: impl AsRef<Path>,
location_table: &LocationTable,
) -> Result<(), Box<dyn Error>>;
}
impl AllFactsExt for AllFacts {
/// Return
fn enabled(tcx: TyCtxt<'_>) -> bool {
tcx.sess.opts.unstable_opts.nll_facts
|| tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled()

View file

@ -1,24 +1,16 @@
use crate::borrow_set::LocalsStateAtExit;
use rustc_hir as hir;
use rustc_macros::extension;
use rustc_middle::mir::ProjectionElem;
use rustc_middle::mir::{Body, Mutability, Place};
use rustc_middle::ty::{self, TyCtxt};
/// Extension methods for the `Place` type.
pub trait PlaceExt<'tcx> {
#[extension(pub trait PlaceExt<'tcx>)]
impl<'tcx> Place<'tcx> {
/// Returns `true` if we can safely ignore borrows of this place.
/// This is true whenever there is no action that the user can do
/// to the place `self` that would invalidate the borrow. This is true
/// for borrows of raw pointer dereferents as well as shared references.
fn ignore_borrow(
&self,
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
locals_state_at_exit: &LocalsStateAtExit,
) -> bool;
}
impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
fn ignore_borrow(
&self,
tcx: TyCtxt<'tcx>,

View file

@ -6,6 +6,7 @@ use rustc_hir::OpaqueTyOrigin;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_macros::extension;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};
@ -225,15 +226,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
pub trait InferCtxtExt<'tcx> {
fn infer_opaque_definition_from_instantiation(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
) -> Ty<'tcx>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Given the fully resolved, instantiated type for an opaque
/// type, i.e., the value of an inference variable like C1 or C2
/// (*), computes the "definition type" for an opaque type

View file

@ -22,6 +22,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::BodyOwnerKind;
use rustc_index::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_macros::extension;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt};
@ -793,27 +794,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
}
}
trait InferCtxtExt<'tcx> {
fn replace_free_regions_with_nll_infer_vars<T>(
&self,
origin: NllRegionVariableOrigin,
value: T,
) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>;
fn replace_bound_regions_with_nll_infer_vars<T>(
&self,
origin: NllRegionVariableOrigin,
all_outlive_scope: LocalDefId,
value: ty::Binder<'tcx, T>,
indices: &mut UniversalRegionIndices<'tcx>,
) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> {
#[extension(trait InferCtxtExt<'tcx>)]
impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn replace_free_regions_with_nll_infer_vars<T>(
&self,

View file

@ -233,11 +233,11 @@ impl CodegenBackend for CraneliftCodegenBackend {
ongoing_codegen: Box<dyn Any>,
sess: &Session,
_outputs: &OutputFilenames,
) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
Ok(ongoing_codegen
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
ongoing_codegen
.downcast::<driver::aot::OngoingCodegen>()
.unwrap()
.join(sess, self.config.borrow().as_ref().unwrap()))
.join(sess, self.config.borrow().as_ref().unwrap())
}
fn link(

View file

@ -122,7 +122,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetFeatureDisableOrEnabl
diag.span(span);
};
if let Some(missing_features) = self.missing_features {
diag.subdiagnostic(missing_features);
diag.subdiagnostic(dcx, missing_features);
}
diag.arg("features", self.features.join(", "));
diag

View file

@ -217,13 +217,11 @@ impl CodegenBackend for GccCodegenBackend {
Box::new(res)
}
fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
let (codegen_results, work_products) = ongoing_codegen
fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
.expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
.join(sess);
Ok((codegen_results, work_products))
.join(sess)
}
fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorGuaranteed> {

View file

@ -131,7 +131,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetFeatureDisableOrEnabl
diag.span(span);
};
if let Some(missing_features) = self.missing_features {
diag.subdiagnostic(missing_features);
diag.subdiagnostic(dcx, missing_features);
}
diag.arg("features", self.features.join(", "));
diag

View file

@ -369,7 +369,7 @@ impl CodegenBackend for LlvmCodegenBackend {
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
let (codegen_results, work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
@ -382,7 +382,7 @@ impl CodegenBackend for LlvmCodegenBackend {
});
}
Ok((codegen_results, work_products))
(codegen_results, work_products)
}
fn link(

View file

@ -102,7 +102,7 @@ pub trait CodegenBackend {
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed>;
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>);
/// This is called on the returned `CodegenResults` from `join_codegen`
fn link(

View file

@ -344,7 +344,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
visitor.visit_ty(ty);
}
fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
fn check_mut_borrow(&mut self, place: &Place<'_>, kind: hir::BorrowKind) {
match self.const_kind() {
// In a const fn all borrows are transient or point to the places given via
// references in the arguments (so we already checked them with
@ -355,10 +355,19 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
// to mutable memory.
hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
_ => {
// For indirect places, we are not creating a new permanent borrow, it's just as
// transient as the already existing one. For reborrowing references this is handled
// at the top of `visit_rvalue`, but for raw pointers we handle it here.
// Pointers/references to `static mut` and cases where the `*` is not the first
// projection also end up here.
// Locals with StorageDead do not live beyond the evaluation and can
// thus safely be borrowed without being able to be leaked to the final
// value of the constant.
if self.local_has_storage_dead(local) {
// Note: This is only sound if every local that has a `StorageDead` has a
// `StorageDead` in every control flow path leading to a `return` terminator.
// The good news is that interning will detect if any unexpected mutable
// pointer slips through.
if place.is_indirect() || self.local_has_storage_dead(place.local) {
self.check_op(ops::TransientMutBorrow(kind));
} else {
self.check_op(ops::MutBorrow(kind));
@ -390,6 +399,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
// Special-case reborrows to be more like a copy of a reference.
// FIXME: this does not actually handle all reborrows. It only detects cases where `*` is the outermost
// projection of the borrowed place, it skips deref'ing raw pointers and it skips `static`.
// All those cases are handled below with shared/mutable borrows.
// Once `const_mut_refs` is stable, we should be able to entirely remove this special case.
// (`const_refs_to_cell` is not needed, we already allow all borrows of indirect places anyway.)
match *rvalue {
Rvalue::Ref(_, kind, place) => {
if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
@ -460,7 +474,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
if !is_allowed {
self.check_mut_borrow(
place.local,
place,
if matches!(rvalue, Rvalue::Ref(..)) {
hir::BorrowKind::Ref
} else {
@ -478,7 +492,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
place.as_ref(),
);
if borrowed_place_has_mut_interior {
// If the place is indirect, this is basically a reborrow. We have a reborrow
// special case above, but for raw pointers and pointers/references to `static` and
// when the `*` is not the first projection, `place_as_reborrow` does not recognize
// them as such, so we end up here. This should probably be considered a
// `TransientCellBorrow` (we consider the equivalent mutable case a
// `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
// it is too much of a breaking change to take back.
if borrowed_place_has_mut_interior && !place.is_indirect() {
match self.const_kind() {
// In a const fn all borrows are transient or point to the places given via
// references in the arguments (so we already checked them with
@ -495,6 +516,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// final value.
// Note: This is only sound if every local that has a `StorageDead` has a
// `StorageDead` in every control flow path leading to a `return` terminator.
// The good news is that interning will detect if any unexpected mutable
// pointer slips through.
if self.local_has_storage_dead(place.local) {
self.check_op(ops::TransientCellBorrow);
} else {
@ -948,6 +971,12 @@ fn place_as_reborrow<'tcx>(
) -> Option<PlaceRef<'tcx>> {
match place.as_ref().last_projection() {
Some((place_base, ProjectionElem::Deref)) => {
// FIXME: why do statics and raw pointers get excluded here? This makes
// some code involving mutable pointers unstable, but it is unclear
// why that code is treated differently from mutable references.
// Once TransientMutBorrow and TransientCellBorrow are stable,
// this can probably be cleaned up without any behavioral changes.
// A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
// that points to the allocation for the static. Don't treat these as reborrows.
if body.local_decls[place_base.local].is_ref_to_static() {

View file

@ -131,7 +131,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
// FIXME(effects) revisit this
if !tcx.is_const_trait_impl_raw(data.impl_def_id) {
let span = tcx.def_span(data.impl_def_id);
err.subdiagnostic(errors::NonConstImplNote { span });
err.subdiagnostic(tcx.dcx(), errors::NonConstImplNote { span });
}
}
}

View file

@ -74,6 +74,13 @@ pub trait Qualif {
adt: AdtDef<'tcx>,
args: GenericArgsRef<'tcx>,
) -> bool;
/// Returns `true` if this `Qualif` behaves sructurally for pointers and references:
/// the pointer/reference qualifies if and only if the pointee qualifies.
///
/// (This is currently `false` for all our instances, but that may change in the future. Also,
/// by keeping it abstract, the handling of `Deref` in `in_place` becomes more clear.)
fn deref_structural<'tcx>(cx: &ConstCx<'_, 'tcx>) -> bool;
}
/// Constant containing interior mutability (`UnsafeCell<T>`).
@ -103,6 +110,10 @@ impl Qualif for HasMutInterior {
// It arises structurally for all other types.
adt.is_unsafe_cell()
}
fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
false
}
}
/// Constant containing an ADT that implements `Drop`.
@ -131,6 +142,10 @@ impl Qualif for NeedsDrop {
) -> bool {
adt.has_dtor(cx.tcx)
}
fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
false
}
}
/// Constant containing an ADT that implements non-const `Drop`.
@ -210,6 +225,10 @@ impl Qualif for NeedsNonConstDrop {
) -> bool {
adt.has_non_const_dtor(cx.tcx)
}
fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
false
}
}
// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.
@ -303,6 +322,11 @@ where
return false;
}
if matches!(elem, ProjectionElem::Deref) && !Q::deref_structural(cx) {
// We have to assume that this qualifies.
return true;
}
place = place_base;
}

View file

@ -263,14 +263,10 @@ pub enum SubdiagnosticMessage {
/// Translatable message which has already been translated eagerly.
///
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
/// be instantiated multiple times with different values. As translation normally happens
/// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
/// the setting of diagnostic arguments in the derived code will overwrite previous variable
/// values and only the final value will be set when translation occurs - resulting in
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
/// stores messages which have been translated eagerly.
Eager(Cow<'static, str>),
/// be instantiated multiple times with different values. These subdiagnostics' messages
/// are translated when they are added to the parent diagnostic, producing this variant of
/// `DiagnosticMessage`.
Translated(Cow<'static, str>),
/// Identifier of a Fluent message. Instances of this variant are generated by the
/// `Subdiagnostic` derive.
FluentIdentifier(FluentId),
@ -307,19 +303,15 @@ impl From<Cow<'static, str>> for SubdiagnosticMessage {
pub enum DiagnosticMessage {
/// Non-translatable diagnostic message.
Str(Cow<'static, str>),
/// Translatable message which has already been translated eagerly.
/// Translatable message which has been already translated.
///
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
/// be instantiated multiple times with different values. As translation normally happens
/// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
/// the setting of diagnostic arguments in the derived code will overwrite previous variable
/// values and only the final value will be set when translation occurs - resulting in
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
/// stores messages which have been translated eagerly.
Eager(Cow<'static, str>),
/// be instantiated multiple times with different values. These subdiagnostics' messages
/// are translated when they are added to the parent diagnostic, producing this variant of
/// `DiagnosticMessage`.
Translated(Cow<'static, str>),
/// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic
/// message.
/// message. Yet to be translated.
///
/// <https://projectfluent.org/fluent/guide/hello.html>
/// <https://projectfluent.org/fluent/guide/attributes.html>
@ -336,7 +328,7 @@ impl DiagnosticMessage {
pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
let attr = match sub {
SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s),
SubdiagnosticMessage::Eager(s) => return DiagnosticMessage::Eager(s),
SubdiagnosticMessage::Translated(s) => return DiagnosticMessage::Translated(s),
SubdiagnosticMessage::FluentIdentifier(id) => {
return DiagnosticMessage::FluentIdentifier(id, None);
}
@ -345,7 +337,7 @@ impl DiagnosticMessage {
match self {
DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
DiagnosticMessage::Eager(s) => DiagnosticMessage::Eager(s.clone()),
DiagnosticMessage::Translated(s) => DiagnosticMessage::Translated(s.clone()),
DiagnosticMessage::FluentIdentifier(id, _) => {
DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
}
@ -354,7 +346,7 @@ impl DiagnosticMessage {
pub fn as_str(&self) -> Option<&str> {
match self {
DiagnosticMessage::Eager(s) | DiagnosticMessage::Str(s) => Some(s),
DiagnosticMessage::Translated(s) | DiagnosticMessage::Str(s) => Some(s),
DiagnosticMessage::FluentIdentifier(_, _) => None,
}
}
@ -396,7 +388,7 @@ impl Into<SubdiagnosticMessage> for DiagnosticMessage {
fn into(self) -> SubdiagnosticMessage {
match self {
DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s),
DiagnosticMessage::Eager(s) => SubdiagnosticMessage::Eager(s),
DiagnosticMessage::Translated(s) => SubdiagnosticMessage::Translated(s),
DiagnosticMessage::FluentIdentifier(id, None) => {
SubdiagnosticMessage::FluentIdentifier(id)
}

View file

@ -851,18 +851,11 @@ impl Diagnostic {
self
}
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
/// [rustc_macros::Subdiagnostic]).
pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
subdiagnostic.add_to_diagnostic(self);
self
}
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
/// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
/// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
/// interpolated variables).
pub fn eager_subdiagnostic(
pub fn subdiagnostic(
&mut self,
dcx: &crate::DiagCtxt,
subdiagnostic: impl AddToDiagnostic,
@ -918,7 +911,7 @@ impl Diagnostic {
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
/// passes the user's string along).
fn subdiagnostic_message_to_diagnostic_message(
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
&self,
attr: impl Into<SubdiagnosticMessage>,
) -> DiagnosticMessage {

View file

@ -404,9 +404,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
));
forward!((subdiagnostic, with_subdiagnostic)(
subdiagnostic: impl crate::AddToDiagnostic,
));
forward!((eager_subdiagnostic, with_eager_subdiagnostic)(
dcx: &DiagCtxt,
subdiagnostic: impl crate::AddToDiagnostic,
));

View file

@ -10,6 +10,7 @@
use rustc_span::source_map::SourceMap;
use rustc_span::{FileLines, FileName, SourceFile, Span};
use crate::error::TranslateError;
use crate::snippet::{
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
};
@ -559,6 +560,18 @@ pub struct SilentEmitter {
pub fatal_note: String,
}
pub fn silent_translate<'a>(
message: &'a DiagnosticMessage,
) -> Result<Cow<'_, str>, TranslateError<'_>> {
match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Translated(msg) => Ok(Cow::Borrowed(msg)),
DiagnosticMessage::FluentIdentifier(identifier, _) => {
// Any value works here.
Ok(identifier.clone())
}
}
}
impl Translate for SilentEmitter {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
@ -567,6 +580,16 @@ impl Translate for SilentEmitter {
fn fallback_fluent_bundle(&self) -> &FluentBundle {
panic!("silent emitter attempted to translate message")
}
// Override `translate_message` for the silent emitter because eager translation of
// subdiagnostics result in a call to this.
fn translate_message<'a>(
&'a self,
message: &'a DiagnosticMessage,
_: &'a FluentArgs<'_>,
) -> Result<Cow<'_, str>, TranslateError<'_>> {
silent_translate(message)
}
}
impl Emitter for SilentEmitter {

View file

@ -643,7 +643,8 @@ impl DiagCtxt {
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> SubdiagnosticMessage {
SubdiagnosticMessage::Eager(Cow::from(self.eagerly_translate_to_string(message, args)))
let inner = self.inner.borrow();
inner.eagerly_translate(message, args)
}
/// Translate `message` eagerly with `args` to `String`.
@ -653,8 +654,7 @@ impl DiagCtxt {
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> String {
let inner = self.inner.borrow();
let args = crate::translation::to_fluent_args(args);
inner.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
inner.eagerly_translate_to_string(message, args)
}
// This is here to not allow mutation of flags;
@ -1464,6 +1464,25 @@ impl DiagCtxtInner {
.or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
}
/// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`.
pub fn eagerly_translate<'a>(
&self,
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> SubdiagnosticMessage {
SubdiagnosticMessage::Translated(Cow::from(self.eagerly_translate_to_string(message, args)))
}
/// Translate `message` eagerly with `args` to `String`.
pub fn eagerly_translate_to_string<'a>(
&self,
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> String {
let args = crate::translation::to_fluent_args(args);
self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
}
fn flush_delayed(&mut self) {
if self.delayed_bugs.is_empty() {
return;
@ -1502,15 +1521,22 @@ impl DiagCtxtInner {
}
let mut bug =
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
if backtrace || self.ice_file.is_none() { bug.decorate(self) } else { bug.inner };
// "Undelay" the delayed bugs (into plain `Bug`s).
if bug.level != DelayedBug {
// NOTE(eddyb) not panicking here because we're already producing
// an ICE, and the more information the merrier.
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
let subdiag = InvalidFlushedDelayedDiagnosticLevel {
span: bug.span.primary_span().unwrap(),
level: bug.level,
};
// FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
// just uses `DiagCtxtInner` functions.
subdiag.add_to_diagnostic_with(&mut bug, |diag, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
self.eagerly_translate(msg, args)
});
}
bug.level = Bug;
@ -1545,25 +1571,35 @@ impl DelayedDiagnostic {
DelayedDiagnostic { inner: diagnostic, note: backtrace }
}
fn decorate(mut self) -> Diagnostic {
fn decorate(mut self, dcx: &DiagCtxtInner) -> Diagnostic {
// FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
// just uses `DiagCtxtInner` functions.
let subdiag_with = |diag: &mut Diagnostic, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
dcx.eagerly_translate(msg, args)
};
match self.note.status() {
BacktraceStatus::Captured => {
let inner = &self.inner;
self.inner.subdiagnostic(DelayedAtWithNewline {
let subdiag = DelayedAtWithNewline {
span: inner.span.primary_span().unwrap_or(DUMMY_SP),
emitted_at: inner.emitted_at.clone(),
note: self.note,
});
};
subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
}
// Avoid the needless newline when no backtrace has been captured,
// the display impl should just be a single line.
_ => {
let inner = &self.inner;
self.inner.subdiagnostic(DelayedAtWithoutNewline {
let subdiag = DelayedAtWithoutNewline {
span: inner.span.primary_span().unwrap_or(DUMMY_SP),
emitted_at: inner.emitted_at.clone(),
note: self.note,
});
};
subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
}
}
@ -1709,15 +1745,15 @@ impl Level {
}
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
pub fn add_elided_lifetime_in_path_suggestion(
pub fn add_elided_lifetime_in_path_suggestion<E: EmissionGuarantee>(
source_map: &SourceMap,
diag: &mut Diagnostic,
diag: &mut DiagnosticBuilder<'_, E>,
n: usize,
path_span: Span,
incl_angl_brckt: bool,
insertion_span: Span,
) {
diag.subdiagnostic(ExpectedLifetimeParameter { span: path_span, count: n });
diag.subdiagnostic(diag.dcx, ExpectedLifetimeParameter { span: path_span, count: n });
if !source_map.is_span_accessible(insertion_span) {
// Do not try to suggest anything if generated by a proc-macro.
return;
@ -1726,11 +1762,10 @@ pub fn add_elided_lifetime_in_path_suggestion(
let suggestion =
if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
diag.subdiagnostic(IndicateAnonymousLifetime {
span: insertion_span.shrink_to_hi(),
count: n,
suggestion,
});
diag.subdiagnostic(
diag.dcx,
IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion },
);
}
pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(

View file

@ -2,7 +2,7 @@ use crate::error::{TranslateError, TranslateErrorKind};
use crate::snippet::Style;
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
pub use rustc_error_messages::FluentArgs;
use std::borrow::Cow;
use std::env;
use std::error::Report;
@ -61,7 +61,7 @@ pub trait Translate {
) -> Result<Cow<'_, str>, TranslateError<'_>> {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Translated(msg) => {
return Ok(Cow::Borrowed(msg));
}
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),

View file

@ -96,7 +96,7 @@ impl Annotatable {
}
}
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) -> V::Result {
match self {
Annotatable::Item(item) => visitor.visit_item(item),
Annotatable::TraitItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Trait),

View file

@ -14,7 +14,8 @@ use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult};
use rustc_ast::{try_visit, walk_list};
use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind};
use rustc_ast::{ForeignItemKind, HasAttrs, HasNodeId};
use rustc_ast::{Inline, ItemKind, MacStmtStyle, MetaItemKind, ModKind};
@ -143,16 +144,15 @@ macro_rules! ast_fragments {
}
}
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) -> V::Result {
match self {
AstFragment::OptExpr(Some(expr)) => visitor.visit_expr(expr),
AstFragment::OptExpr(Some(expr)) => try_visit!(visitor.visit_expr(expr)),
AstFragment::OptExpr(None) => {}
AstFragment::MethodReceiverExpr(expr) => visitor.visit_method_receiver_expr(expr),
$($(AstFragment::$Kind(ast) => visitor.$visit_ast(ast),)?)*
$($(AstFragment::$Kind(ast) => for ast_elt in &ast[..] {
visitor.$visit_ast_elt(ast_elt, $($args)*);
})?)*
AstFragment::MethodReceiverExpr(expr) => try_visit!(visitor.visit_method_receiver_expr(expr)),
$($(AstFragment::$Kind(ast) => try_visit!(visitor.$visit_ast(ast)),)?)*
$($(AstFragment::$Kind(ast) => walk_list!(visitor, $visit_ast_elt, &ast[..], $($args)*),)?)*
}
V::Result::output()
}
}

View file

@ -7,7 +7,7 @@ use crate::mbe::{
use rustc_ast::token::{self, Token, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
use rustc_errors::{Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
use rustc_parse::parser::{Parser, Recovery};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::Ident;
@ -58,7 +58,7 @@ pub(super) fn failed_to_match_macro<'cx>(
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
}
annotate_doc_comment(&mut err, sess.source_map(), span);
annotate_doc_comment(cx.sess.dcx(), &mut err, sess.source_map(), span);
if let Some(span) = remaining_matcher.span() {
err.span_note(span, format!("while trying to match {remaining_matcher}"));
@ -311,12 +311,17 @@ enum ExplainDocComment {
},
}
pub(super) fn annotate_doc_comment(err: &mut Diagnostic, sm: &SourceMap, span: Span) {
pub(super) fn annotate_doc_comment(
dcx: &DiagCtxt,
err: &mut Diagnostic,
sm: &SourceMap,
span: Span,
) {
if let Ok(src) = sm.span_to_snippet(span) {
if src.starts_with("///") || src.starts_with("/**") {
err.subdiagnostic(ExplainDocComment::Outer { span });
err.subdiagnostic(dcx, ExplainDocComment::Outer { span });
} else if src.starts_with("//!") || src.starts_with("/*!") {
err.subdiagnostic(ExplainDocComment::Inner { span });
err.subdiagnostic(dcx, ExplainDocComment::Inner { span });
}
}
}

View file

@ -454,7 +454,7 @@ pub fn compile_declarative_macro(
let sp = token.span.substitute_dummy(def.span);
let mut err = sess.dcx().struct_span_err(sp, s);
err.span_label(sp, msg);
annotate_doc_comment(&mut err, sess.source_map(), sp);
annotate_doc_comment(sess.dcx(), &mut err, sess.source_map(), sp);
err.emit();
return dummy_syn_ext();
}

File diff suppressed because it is too large Load diff

View file

@ -407,9 +407,9 @@ pub fn check_intrinsic_type(
}
sym::float_to_int_unchecked => (2, 0, vec![param(0)], param(1)),
sym::assume => (0, 0, vec![tcx.types.bool], Ty::new_unit(tcx)),
sym::likely => (0, 0, vec![tcx.types.bool], tcx.types.bool),
sym::unlikely => (0, 0, vec![tcx.types.bool], tcx.types.bool),
sym::assume => (0, 1, vec![tcx.types.bool], Ty::new_unit(tcx)),
sym::likely => (0, 1, vec![tcx.types.bool], tcx.types.bool),
sym::unlikely => (0, 1, vec![tcx.types.bool], tcx.types.bool),
sym::read_via_copy => (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
sym::write_via_move => {

View file

@ -14,6 +14,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
use rustc_macros::extension;
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_bound_vars::*;
@ -27,17 +28,8 @@ use std::fmt;
use crate::errors;
trait RegionExt {
fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg);
fn late(index: u32, param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg);
fn id(&self) -> Option<DefId>;
fn shifted(self, amount: u32) -> ResolvedArg;
}
impl RegionExt for ResolvedArg {
#[extension(trait RegionExt)]
impl ResolvedArg {
fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) {
debug!("ResolvedArg::early: def_id={:?}", param.def_id);
(param.def_id, ResolvedArg::EarlyBound(param.def_id.to_def_id()))

View file

@ -260,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let semi = expr.span.shrink_to_hi().with_hi(semi_span.hi());
let sugg = crate::errors::RemoveSemiForCoerce { expr: expr.span, ret, semi };
diag.subdiagnostic(sugg);
diag.subdiagnostic(self.dcx(), sugg);
}
/// When the previously checked expression (the scrutinee) diverges,
@ -311,7 +311,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
error: &mut bool,
) {
if let Some((if_span, msg)) = ret_reason {
err.span_label(if_span, msg.clone());
err.span_label(if_span, msg);
} else if let ExprKind::Block(block, _) = then_expr.kind
&& let Some(expr) = block.expr
{

View file

@ -993,19 +993,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
if let Some((deref_ty, _)) = derefed {
// Give a note about what the expr derefs to.
if deref_ty != self.expr_ty.peel_refs() {
err.subdiagnostic(errors::DerefImplsIsEmpty {
span: self.expr_span,
deref_ty: fcx.ty_to_string(deref_ty),
});
err.subdiagnostic(
fcx.dcx(),
errors::DerefImplsIsEmpty {
span: self.expr_span,
deref_ty: fcx.ty_to_string(deref_ty),
},
);
}
// Create a multipart suggestion: add `!` and `.is_empty()` in
// place of the cast.
err.subdiagnostic(errors::UseIsEmpty {
lo: self.expr_span.shrink_to_lo(),
hi: self.span.with_lo(self.expr_span.hi()),
expr_ty: fcx.ty_to_string(self.expr_ty),
});
err.subdiagnostic(
fcx.dcx(),
errors::UseIsEmpty {
lo: self.expr_span.shrink_to_lo(),
hi: self.span.with_lo(self.expr_span.hi()),
expr_ty: fcx.ty_to_string(self.expr_ty),
},
);
}
}
}

View file

@ -396,7 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(sp) =
tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
{
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
}
oprnd_t = Ty::new_error(tcx, err.emit());
}
@ -2048,7 +2048,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.shrink_to_hi()
.to(range_end.span);
err.subdiagnostic(TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr });
err.subdiagnostic(
self.dcx(),
TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr },
);
}
}

View file

@ -1269,7 +1269,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
// The user provided `ptr::null()`, but the function expects
// `ptr::null_mut()`.
err.subdiagnostic(SuggestPtrNullMut { span: arg.span });
err.subdiagnostic(self.dcx(), SuggestPtrNullMut { span: arg.span });
}
}

View file

@ -458,13 +458,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// but those checks need to be a bit more delicate and the benefit is diminishing.
if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
let sugg = prefix_wrap(".as_ref()");
err.subdiagnostic(errors::SuggestConvertViaMethod {
span: expr.span.shrink_to_hi(),
sugg,
expected,
found,
borrow_removal_span,
});
err.subdiagnostic(
self.dcx(),
errors::SuggestConvertViaMethod {
span: expr.span.shrink_to_hi(),
sugg,
expected,
found,
borrow_removal_span,
},
);
return true;
} else if let Some((deref_ty, _)) =
self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
@ -472,13 +475,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& error_tys_equate_as_ref
{
let sugg = prefix_wrap(".as_deref()");
err.subdiagnostic(errors::SuggestConvertViaMethod {
span: expr.span.shrink_to_hi(),
sugg,
expected,
found,
borrow_removal_span,
});
err.subdiagnostic(
self.dcx(),
errors::SuggestConvertViaMethod {
span: expr.span.shrink_to_hi(),
sugg,
expected,
found,
borrow_removal_span,
},
);
return true;
} else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
&& Some(adt.did()) == self.tcx.lang_items().string()
@ -565,7 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
end: span.shrink_to_hi(),
},
};
err.subdiagnostic(suggest_boxing);
err.subdiagnostic(self.dcx(), suggest_boxing);
true
} else {
@ -799,29 +805,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &fn_decl.output {
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
// `fn main()` must return `()`, do not suggest changing return type
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
err.subdiagnostic(self.dcx(), errors::ExpectedReturnTypeLabel::Unit { span });
return true;
}
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
if let Some(found) = found.make_suggestable(self.tcx, false) {
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
span,
found: found.to_string(),
});
err.subdiagnostic(
self.dcx(),
errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() },
);
return true;
} else if let ty::Closure(_, args) = found.kind()
// FIXME(compiler-errors): Get better at printing binders...
&& let closure = args.as_closure()
&& closure.sig().is_suggestable(self.tcx, false)
{
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
span,
found: closure.print_as_impl_trait().to_string(),
});
err.subdiagnostic(
self.dcx(),
errors::AddReturnTypeSuggestion::Add {
span,
found: closure.print_as_impl_trait().to_string(),
},
);
return true;
} else {
// FIXME: if `found` could be `impl Iterator` we should suggest that.
err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
err.subdiagnostic(
self.dcx(),
errors::AddReturnTypeSuggestion::MissingHere { span },
);
return true;
}
}
@ -843,16 +855,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!(?found);
if found.is_suggestable(self.tcx, false) {
if term.span.is_empty() {
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
span: term.span,
found: found.to_string(),
});
err.subdiagnostic(
self.dcx(),
errors::AddReturnTypeSuggestion::Add {
span: term.span,
found: found.to_string(),
},
);
return true;
} else {
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
span: term.span,
expected,
});
err.subdiagnostic(
self.dcx(),
errors::ExpectedReturnTypeLabel::Other {
span: term.span,
expected,
},
);
}
}
} else {
@ -867,10 +885,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.normalize(hir_ty.span, ty);
let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
if self.can_coerce(expected, ty) {
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
span: hir_ty.span,
expected,
});
err.subdiagnostic(
self.dcx(),
errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, expected },
);
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
return true;
}
@ -1106,7 +1124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
// `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
true
} else {
false
@ -1220,7 +1238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
return false;
};
diag.subdiagnostic(subdiag);
diag.subdiagnostic(self.dcx(), subdiag);
return true;
}
}

View file

@ -3269,19 +3269,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if impls_trait(trait_info.def_id) {
self.suggest_valid_traits(err, vec![trait_info.def_id], false);
} else {
err.subdiagnostic(CandidateTraitNote {
span: self.tcx.def_span(trait_info.def_id),
trait_name: self.tcx.def_path_str(trait_info.def_id),
item_name,
action_or_ty: if trait_missing_method {
"NONE".to_string()
} else {
param_type.map_or_else(
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
ToString::to_string,
)
err.subdiagnostic(
self.dcx(),
CandidateTraitNote {
span: self.tcx.def_span(trait_info.def_id),
trait_name: self.tcx.def_path_str(trait_info.def_id),
item_name,
action_or_ty: if trait_missing_method {
"NONE".to_string()
} else {
param_type.map_or_else(
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
ToString::to_string,
)
},
},
});
);
}
}
trait_infos => {

View file

@ -824,7 +824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the previous expression was a block expression, suggest parentheses
// (turning this into a binary subtraction operation instead.)
// for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
} else {
match actual.kind() {
Uint(_) if op == hir::UnOp::Neg => {

View file

@ -13,30 +13,10 @@ use rustc_middle::ty::{self, TyCtxt};
/// FIXME(-Znext-solver): This or public because it is shared with the
/// new trait solver implementation. We should deduplicate canonicalization.
pub trait CanonicalExt<'tcx, V> {
#[extension(pub trait CanonicalExt<'tcx, V>)]
impl<'tcx, V> Canonical<'tcx, V> {
/// Instantiate the wrapped value, replacing each canonical value
/// with the value given in `var_values`.
fn instantiate(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
where
V: TypeFoldable<TyCtxt<'tcx>>;
/// Allows one to apply a instantiation to some subset of
/// `self.value`. Invoke `projection_fn` with `self.value` to get
/// a value V that is expressed in terms of the same canonical
/// variables bound in `self` (usually this extracts from subset
/// of `self`). Apply the instantiation `var_values` to this value
/// V, replacing each of the canonical variables.
fn instantiate_projected<T>(
&self,
tcx: TyCtxt<'tcx>,
var_values: &CanonicalVarValues<'tcx>,
projection_fn: impl FnOnce(&V) -> T,
) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>;
}
impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> {
fn instantiate(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
where
V: TypeFoldable<TyCtxt<'tcx>>,
@ -44,6 +24,12 @@ impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> {
self.instantiate_projected(tcx, var_values, |value| value.clone())
}
/// Allows one to apply a instantiation to some subset of
/// `self.value`. Invoke `projection_fn` with `self.value` to get
/// a value V that is expressed in terms of the same canonical
/// variables bound in `self` (usually this extracts from subset
/// of `self`). Apply the instantiation `var_values` to this value
/// V, replacing each of the canonical variables.
fn instantiate_projected<T>(
&self,
tcx: TyCtxt<'tcx>,

View file

@ -845,7 +845,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
arm_ty,
arm_span,
) {
err.subdiagnostic(subdiag);
err.subdiagnostic(self.dcx(), subdiag);
}
if let Some(ret_sp) = opt_suggest_box_span {
// Get return type span and point to it.
@ -882,7 +882,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
else_ty,
else_span,
) {
err.subdiagnostic(subdiag);
err.subdiagnostic(self.dcx(), subdiag);
}
// don't suggest wrapping either blocks in `if .. {} else {}`
let is_empty_arm = |id| {
@ -2786,19 +2786,8 @@ pub enum FailureCode {
Error0644,
}
pub trait ObligationCauseExt<'tcx> {
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode;
fn as_failure_code_diag(
&self,
terr: TypeError<'tcx>,
span: Span,
subdiags: Vec<TypeErrorAdditionalDiags>,
) -> ObligationCauseFailureCode;
fn as_requirement_str(&self) -> &'static str;
}
impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
#[extension(pub trait ObligationCauseExt<'tcx>)]
impl<'tcx> ObligationCause<'tcx> {
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode {
use self::FailureCode::*;
use crate::traits::ObligationCauseCode::*;

View file

@ -342,7 +342,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
trait_predicates: trait_predicates.join(", "),
}
};
err.subdiagnostic(suggestion);
err.subdiagnostic(self.dcx(), suggestion);
}
pub(super) fn report_placeholder_failure(

View file

@ -84,7 +84,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)),
end_sp: return_sp.shrink_to_hi(),
};
err.subdiagnostic(sugg);
err.subdiagnostic(self.dcx(), sugg);
let mut starts = Vec::new();
let mut ends = Vec::new();
@ -93,7 +93,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
ends.push(span.shrink_to_hi());
}
let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends };
err.subdiagnostic(sugg);
err.subdiagnostic(self.dcx(), sugg);
}
pub(super) fn suggest_tuple_pattern(
@ -138,7 +138,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
span_low: cause.span.shrink_to_lo(),
span_high: cause.span.shrink_to_hi(),
};
diag.subdiagnostic(sugg);
diag.subdiagnostic(self.dcx(), sugg);
}
_ => {
// More than one matching variant.
@ -147,7 +147,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
cause_span: cause.span,
compatible_variants,
};
diag.subdiagnostic(sugg);
diag.subdiagnostic(self.dcx(), sugg);
}
}
}
@ -219,9 +219,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
},
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
// FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
span: exp_span.shrink_to_hi(),
});
diag.subdiagnostic(
self.dcx(),
ConsiderAddingAwait::FutureSugg { span: exp_span.shrink_to_hi() },
);
Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
}
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
@ -249,7 +250,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
_ => None,
};
if let Some(subdiag) = subdiag {
diag.subdiagnostic(subdiag);
diag.subdiagnostic(self.dcx(), subdiag);
}
}
@ -285,7 +286,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
} else {
return;
};
diag.subdiagnostic(suggestion);
diag.subdiagnostic(self.dcx(), suggestion);
}
}
}
@ -325,15 +326,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
(true, true) => {
diag.subdiagnostic(FnItemsAreDistinct);
diag.subdiagnostic(self.dcx(), FnItemsAreDistinct);
FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
}
(false, false) => {
diag.subdiagnostic(FnItemsAreDistinct);
diag.subdiagnostic(self.dcx(), FnItemsAreDistinct);
FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
}
};
diag.subdiagnostic(sugg);
diag.subdiagnostic(self.dcx(), sugg);
}
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
let expected_sig =
@ -342,7 +343,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
diag.subdiagnostic(FnUniqTypes);
diag.subdiagnostic(self.dcx(), FnUniqTypes);
}
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
@ -371,7 +372,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
};
diag.subdiagnostic(sug);
diag.subdiagnostic(self.dcx(), sug);
}
(ty::FnDef(did, args), ty::FnPtr(sig)) => {
let expected_sig =
@ -390,7 +391,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!("{fn_name} as {found_sig}")
};
diag.subdiagnostic(FnConsiderCasting { casting });
diag.subdiagnostic(self.dcx(), FnConsiderCasting { casting });
}
_ => {
return;
@ -822,7 +823,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let diag = self.consider_returning_binding_diag(blk, expected_ty);
match diag {
Some(diag) => {
err.subdiagnostic(diag);
err.subdiagnostic(self.dcx(), diag);
true
}
None => false,

View file

@ -626,11 +626,8 @@ pub struct InferCtxtBuilder<'tcx> {
next_trait_solver: bool,
}
pub trait TyCtxtInferExt<'tcx> {
fn infer_ctxt(self) -> InferCtxtBuilder<'tcx>;
}
impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
#[extension(pub trait TyCtxtInferExt<'tcx>)]
impl<'tcx> TyCtxt<'tcx> {
fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
InferCtxtBuilder {
tcx: self,

View file

@ -52,18 +52,8 @@ pub trait TraitEngine<'tcx>: 'tcx {
) -> Vec<PredicateObligation<'tcx>>;
}
pub trait TraitEngineExt<'tcx> {
fn register_predicate_obligations(
&mut self,
infcx: &InferCtxt<'tcx>,
obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
);
#[must_use]
fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
}
impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T {
#[extension(pub trait TraitEngineExt<'tcx>)]
impl<'tcx, T: ?Sized + TraitEngine<'tcx>> T {
fn register_predicate_obligations(
&mut self,
infcx: &InferCtxt<'tcx>,
@ -74,6 +64,7 @@ impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T {
}
}
#[must_use]
fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
let errors = self.select_where_possible(infcx);
if !errors.is_empty() {

View file

@ -259,7 +259,7 @@ pub struct Linker {
impl Linker {
pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> {
let (codegen_results, work_products) =
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)?;
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames);
sess.compile_status()?;

View file

@ -1757,7 +1757,7 @@ impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
diag.note(note.to_string());
}
if let Some(sugg) = self.suggestion {
diag.subdiagnostic(sugg);
diag.subdiagnostic(diag.dcx, sugg);
}
}

View file

@ -331,44 +331,7 @@ impl DiagnosticDeriveVariantBuilder {
}
}
(Meta::Path(_), "subdiagnostic") => {
if FieldInnerTy::from_type(&info.binding.ast().ty).will_iterate() {
let DiagnosticDeriveKind::Diagnostic = self.kind else {
// No eager translation for lints.
return Ok(quote! { diag.subdiagnostic(#binding); });
};
return Ok(quote! { diag.eager_subdiagnostic(dcx, #binding); });
} else {
return Ok(quote! { diag.subdiagnostic(#binding); });
}
}
(Meta::List(meta_list), "subdiagnostic") => {
let err = || {
span_err(
meta_list.span().unwrap(),
"`eager` is the only supported nested attribute for `subdiagnostic`",
)
.emit();
};
let Ok(p): Result<Path, _> = meta_list.parse_args() else {
err();
return Ok(quote! {});
};
if !p.is_ident("eager") {
err();
return Ok(quote! {});
}
match &self.kind {
DiagnosticDeriveKind::Diagnostic => {}
DiagnosticDeriveKind::LintDiagnostic => {
throw_invalid_attr!(attr, |diag| {
diag.help("eager subdiagnostics are not supported on lints")
})
}
};
return Ok(quote! { diag.eager_subdiagnostic(dcx, #binding); });
return Ok(quote! { diag.subdiagnostic(diag.dcx, #binding); });
}
_ => (),
}

View file

@ -0,0 +1,154 @@
use proc_macro2::Ident;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
braced, parse_macro_input, Attribute, Generics, ImplItem, Pat, PatIdent, Path, Signature,
Token, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, Type, Visibility,
};
pub(crate) fn extension(
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let ExtensionAttr { vis, trait_ } = parse_macro_input!(attr as ExtensionAttr);
let Impl { attrs, generics, self_ty, items } = parse_macro_input!(input as Impl);
let headers: Vec<_> = items
.iter()
.map(|item| match item {
ImplItem::Fn(f) => TraitItem::Fn(TraitItemFn {
attrs: scrub_attrs(&f.attrs),
sig: scrub_header(f.sig.clone()),
default: None,
semi_token: Some(Token![;](f.block.span())),
}),
ImplItem::Const(ct) => TraitItem::Const(TraitItemConst {
attrs: scrub_attrs(&ct.attrs),
const_token: ct.const_token,
ident: ct.ident.clone(),
generics: ct.generics.clone(),
colon_token: ct.colon_token,
ty: ct.ty.clone(),
default: None,
semi_token: ct.semi_token,
}),
ImplItem::Type(ty) => TraitItem::Type(TraitItemType {
attrs: scrub_attrs(&ty.attrs),
type_token: ty.type_token,
ident: ty.ident.clone(),
generics: ty.generics.clone(),
colon_token: None,
bounds: Punctuated::new(),
default: None,
semi_token: ty.semi_token,
}),
ImplItem::Macro(mac) => TraitItem::Macro(TraitItemMacro {
attrs: scrub_attrs(&mac.attrs),
mac: mac.mac.clone(),
semi_token: mac.semi_token,
}),
ImplItem::Verbatim(stream) => TraitItem::Verbatim(stream.clone()),
_ => unimplemented!(),
})
.collect();
quote! {
#(#attrs)*
#vis trait #trait_ {
#(#headers)*
}
impl #generics #trait_ for #self_ty {
#(#items)*
}
}
.into()
}
/// Only keep `#[doc]` attrs.
fn scrub_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
attrs
.into_iter()
.cloned()
.filter(|attr| {
let ident = &attr.path().segments[0].ident;
ident == "doc" || ident == "must_use"
})
.collect()
}
/// Scrub arguments so that they're valid for trait signatures.
fn scrub_header(mut sig: Signature) -> Signature {
for (idx, input) in sig.inputs.iter_mut().enumerate() {
match input {
syn::FnArg::Receiver(rcvr) => {
// `mut self` -> `self`
if rcvr.reference.is_none() {
rcvr.mutability.take();
}
}
syn::FnArg::Typed(arg) => match &mut *arg.pat {
Pat::Ident(arg) => {
// `ref mut ident @ pat` -> `ident`
arg.by_ref.take();
arg.mutability.take();
arg.subpat.take();
}
_ => {
// `pat` -> `__arg0`
arg.pat = Box::new(
PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: Ident::new(&format!("__arg{idx}"), arg.pat.span()),
subpat: None,
}
.into(),
)
}
},
}
}
sig
}
struct ExtensionAttr {
vis: Visibility,
trait_: Path,
}
impl Parse for ExtensionAttr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let vis = input.parse()?;
let _: Token![trait] = input.parse()?;
let trait_ = input.parse()?;
Ok(ExtensionAttr { vis, trait_ })
}
}
struct Impl {
attrs: Vec<Attribute>,
generics: Generics,
self_ty: Type,
items: Vec<ImplItem>,
}
impl Parse for Impl {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let _: Token![impl] = input.parse()?;
let generics = input.parse()?;
let self_ty = input.parse()?;
let content;
let _brace_token = braced!(content in input);
let mut items = Vec::new();
while !content.is_empty() {
items.push(content.parse()?);
}
Ok(Impl { attrs, generics, self_ty, items })
}
}

View file

@ -14,6 +14,7 @@ use proc_macro::TokenStream;
mod current_version;
mod diagnostics;
mod extension;
mod hash_stable;
mod lift;
mod query;
@ -40,6 +41,11 @@ pub fn symbols(input: TokenStream) -> TokenStream {
symbols::symbols(input.into()).into()
}
#[proc_macro_attribute]
pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream {
extension::extension(attr, input)
}
decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);
decl_derive!(
[HashStable_Generic, attributes(stable_hasher)] =>

View file

@ -3,6 +3,8 @@ use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
use crate::query::LocalCrate;
use crate::ty::TyCtxt;
use rustc_ast as ast;
use rustc_ast::visit::VisitorResult;
use rustc_ast::walk_list;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::svh::Svh;
@ -431,23 +433,28 @@ impl<'hir> Map<'hir> {
}
/// Walks the contents of the local crate. See also `visit_all_item_likes_in_crate`.
pub fn walk_toplevel_module(self, visitor: &mut impl Visitor<'hir>) {
pub fn walk_toplevel_module<V>(self, visitor: &mut V) -> V::Result
where
V: Visitor<'hir>,
{
let (top_mod, span, hir_id) = self.get_module(LocalModDefId::CRATE_DEF_ID);
visitor.visit_mod(top_mod, span, hir_id);
visitor.visit_mod(top_mod, span, hir_id)
}
/// Walks the attributes in a crate.
pub fn walk_attributes(self, visitor: &mut impl Visitor<'hir>) {
pub fn walk_attributes<V>(self, visitor: &mut V) -> V::Result
where
V: Visitor<'hir>,
{
let krate = self.krate();
for info in krate.owners.iter() {
if let MaybeOwner::Owner(info) = info {
for attrs in info.attrs.map.values() {
for a in *attrs {
visitor.visit_attribute(a)
}
walk_list!(visitor, visit_attribute, *attrs);
}
}
}
V::Result::output()
}
/// Visits all item-likes in the crate in some deterministic (but unspecified) order. If you
@ -460,52 +467,38 @@ impl<'hir> Map<'hir> {
/// provided by `tcx.hir_crate_items(())`.
///
/// Please see the notes in `intravisit.rs` for more information.
pub fn visit_all_item_likes_in_crate<V>(self, visitor: &mut V)
pub fn visit_all_item_likes_in_crate<V>(self, visitor: &mut V) -> V::Result
where
V: Visitor<'hir>,
{
let krate = self.tcx.hir_crate_items(());
for id in krate.items() {
visitor.visit_item(self.item(id));
}
for id in krate.trait_items() {
visitor.visit_trait_item(self.trait_item(id));
}
for id in krate.impl_items() {
visitor.visit_impl_item(self.impl_item(id));
}
for id in krate.foreign_items() {
visitor.visit_foreign_item(self.foreign_item(id));
}
walk_list!(visitor, visit_item, krate.items().map(|id| self.item(id)));
walk_list!(visitor, visit_trait_item, krate.trait_items().map(|id| self.trait_item(id)));
walk_list!(visitor, visit_impl_item, krate.impl_items().map(|id| self.impl_item(id)));
walk_list!(
visitor,
visit_foreign_item,
krate.foreign_items().map(|id| self.foreign_item(id))
);
V::Result::output()
}
/// This method is the equivalent of `visit_all_item_likes_in_crate` but restricted to
/// item-likes in a single module.
pub fn visit_item_likes_in_module<V>(self, module: LocalModDefId, visitor: &mut V)
pub fn visit_item_likes_in_module<V>(self, module: LocalModDefId, visitor: &mut V) -> V::Result
where
V: Visitor<'hir>,
{
let module = self.tcx.hir_module_items(module);
for id in module.items() {
visitor.visit_item(self.item(id));
}
for id in module.trait_items() {
visitor.visit_trait_item(self.trait_item(id));
}
for id in module.impl_items() {
visitor.visit_impl_item(self.impl_item(id));
}
for id in module.foreign_items() {
visitor.visit_foreign_item(self.foreign_item(id));
}
walk_list!(visitor, visit_item, module.items().map(|id| self.item(id)));
walk_list!(visitor, visit_trait_item, module.trait_items().map(|id| self.trait_item(id)));
walk_list!(visitor, visit_impl_item, module.impl_items().map(|id| self.impl_item(id)));
walk_list!(
visitor,
visit_foreign_item,
module.foreign_items().map(|id| self.foreign_item(id))
);
V::Result::output()
}
pub fn for_each_module(self, mut f: impl FnMut(LocalModDefId)) {

View file

@ -23,20 +23,8 @@ use std::fmt;
use std::num::NonZero;
use std::ops::Bound;
pub trait IntegerExt {
fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>;
fn from_int_ty<C: HasDataLayout>(cx: &C, ity: ty::IntTy) -> Integer;
fn from_uint_ty<C: HasDataLayout>(cx: &C, uty: ty::UintTy) -> Integer;
fn repr_discr<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
repr: &ReprOptions,
min: i128,
max: i128,
) -> (Integer, bool);
}
impl IntegerExt for Integer {
#[extension(pub trait IntegerExt)]
impl Integer {
#[inline]
fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> {
match (*self, signed) {
@ -123,12 +111,8 @@ impl IntegerExt for Integer {
}
}
pub trait PrimitiveExt {
fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
}
impl PrimitiveExt for Primitive {
#[extension(pub trait PrimitiveExt)]
impl Primitive {
#[inline]
fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self {

View file

@ -96,13 +96,8 @@ impl<'tcx> Discr<'tcx> {
}
}
pub trait IntTypeExt {
fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
fn disr_incr<'tcx>(&self, tcx: TyCtxt<'tcx>, val: Option<Discr<'tcx>>) -> Option<Discr<'tcx>>;
fn initial_discriminant<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Discr<'tcx>;
}
impl IntTypeExt for IntegerType {
#[extension(pub trait IntTypeExt)]
impl IntegerType {
fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
IntegerType::Pointer(true) => tcx.types.isize,

View file

@ -1109,7 +1109,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
if !is_empty_match && all_arms_have_guards {
err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
err.subdiagnostic(cx.tcx.dcx(), NonExhaustiveMatchAllArmsGuarded);
}
if let Some((span, sugg)) = suggestion {
err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);

View file

@ -32,11 +32,6 @@ impl<'tcx> MirLint<'tcx> for ConstPropLint {
return;
}
// will be evaluated by miri and produce its errors there
if body.source.promoted.is_some() {
return;
}
let def_id = body.source.def_id().expect_local();
let def_kind = tcx.def_kind(def_id);
let is_fn_like = def_kind.is_fn_like();

View file

@ -132,18 +132,23 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
bcb_data.basic_blocks.iter().flat_map(move |&bb| {
let data = &mir_body[bb];
let unexpand = move |expn_span| {
unexpand_into_body_span_with_visible_macro(expn_span, body_span)
// Discard any spans that fill the entire body, because they tend
// to represent compiler-inserted code, e.g. implicitly returning `()`.
.filter(|(span, _)| !span.source_equal(body_span))
};
let statement_spans = data.statements.iter().filter_map(move |statement| {
let expn_span = filtered_statement_span(statement)?;
let (span, visible_macro) =
unexpand_into_body_span_with_visible_macro(expn_span, body_span)?;
let (span, visible_macro) = unexpand(expn_span)?;
Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_like(statement)))
});
let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| {
let expn_span = filtered_terminator_span(terminator)?;
let (span, visible_macro) =
unexpand_into_body_span_with_visible_macro(expn_span, body_span)?;
let (span, visible_macro) = unexpand(expn_span)?;
Some(SpanFromMir::new(span, visible_macro, bcb, false))
});

View file

@ -270,7 +270,7 @@ impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
diag.span_label(self.yield_sp, fluent::_subdiag::label);
if let Some(reason) = self.reason {
diag.subdiagnostic(reason);
diag.subdiagnostic(diag.dcx, reason);
}
diag.span_help(self.src_sp, fluent::_subdiag::help);
diag.arg("pre", self.pre);

View file

@ -452,7 +452,6 @@ impl<'a> Parser<'a> {
let mut expected = self
.expected_tokens
.iter()
.cloned()
.filter(|token| {
// Filter out suggestions that suggest the same token which was found and deemed incorrect.
fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
@ -464,7 +463,7 @@ impl<'a> Parser<'a> {
false
}
if *token != parser::TokenType::Token(self.token.kind.clone()) {
if **token != parser::TokenType::Token(self.token.kind.clone()) {
let eq = is_ident_eq_keyword(&self.token.kind, &token);
// If the suggestion is a keyword and the found token is an ident,
// the content of which are equal to the suggestion's content,
@ -483,6 +482,7 @@ impl<'a> Parser<'a> {
}
false
})
.cloned()
.collect::<Vec<_>>();
expected.sort_by_cached_key(|x| x.to_string());
expected.dedup();
@ -2350,7 +2350,7 @@ impl<'a> Parser<'a> {
let mut err = self.dcx().struct_span_err(span, msg);
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
}
err.span_label(span, "expected expression");

View file

@ -1433,7 +1433,7 @@ impl<'a> Parser<'a> {
// If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`
// then suggest parens around the lhs.
if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
err.subdiagnostic(this.dcx(), ExprParenthesesNeeded::surrounding(*sp));
}
err
})

View file

@ -1933,7 +1933,7 @@ impl<'a> Parser<'a> {
if self.token.kind == token::Not {
if let Err(mut err) = self.unexpected::<FieldDef>() {
// Encounter the macro invocation
err.subdiagnostic(MacroExpandsToAdtField { adt_ty });
err.subdiagnostic(self.dcx(), MacroExpandsToAdtField { adt_ty });
return Err(err);
}
}
@ -2352,10 +2352,13 @@ impl<'a> Parser<'a> {
.into_iter()
.any(|s| self.prev_token.is_ident_named(s));
err.subdiagnostic(errors::FnTraitMissingParen {
span: self.prev_token.span,
machine_applicable,
});
err.subdiagnostic(
self.dcx(),
errors::FnTraitMissingParen {
span: self.prev_token.span,
machine_applicable,
},
);
}
return Err(err);
}

View file

@ -843,7 +843,7 @@ impl<'a> Parser<'a> {
let sp = self.sess.source_map().start_point(self.token.span);
if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
}
Err(err)

View file

@ -893,7 +893,7 @@ impl<'a> Parser<'a> {
ParseError {
description: "expected format parameter to occur after `:`".to_owned(),
note: None,
label: format!("expected `{}` to occur after `:`", alignment).to_owned(),
label: format!("expected `{}` to occur after `:`", alignment),
span: pos.to(pos),
secondary_label: None,
suggestion: Suggestion::None,

View file

@ -403,9 +403,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
if let Some(suggestion) = suggestion {
err.subdiagnostic(ChangeImportBindingSuggestion { span: binding_span, suggestion });
err.subdiagnostic(
self.dcx(),
ChangeImportBindingSuggestion { span: binding_span, suggestion },
);
} else {
err.subdiagnostic(ChangeImportBinding { span: binding_span });
err.subdiagnostic(self.dcx(), ChangeImportBinding { span: binding_span });
}
}
@ -1430,17 +1433,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
);
if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {
err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span });
err.subdiagnostic(self.dcx(), MaybeMissingMacroRulesName { span: ident.span });
return;
}
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });
err.subdiagnostic(self.dcx(), ExplicitUnsafeTraits { span: ident.span, ident });
return;
}
if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
err.subdiagnostic(AddedMacroUse);
err.subdiagnostic(self.dcx(), AddedMacroUse);
return;
}
@ -1450,10 +1453,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let span = self.def_span(def_id);
let source_map = self.tcx.sess.source_map();
let head_span = source_map.guess_head_span(span);
err.subdiagnostic(ConsiderAddingADerive {
span: head_span.shrink_to_lo(),
suggestion: "#[derive(Default)]\n".to_string(),
});
err.subdiagnostic(
self.dcx(),
ConsiderAddingADerive {
span: head_span.shrink_to_lo(),
suggestion: "#[derive(Default)]\n".to_string(),
},
);
}
for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(

View file

@ -1262,12 +1262,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// exclude decl_macro
if self.get_macro_by_def_id(def_id).macro_rules =>
{
err.subdiagnostic(ConsiderAddingMacroExport {
err.subdiagnostic(self.dcx(), ConsiderAddingMacroExport {
span: binding.span,
});
}
_ => {
err.subdiagnostic(ConsiderMarkingAsPub {
err.subdiagnostic(self.dcx(), ConsiderMarkingAsPub {
span: import.span,
ident,
});

View file

@ -1111,11 +1111,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
Side::Start => (segment.ident.span.between(range.span), " @ ".into()),
Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),
};
err.subdiagnostic(errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg {
span,
ident: segment.ident,
snippet,
});
err.subdiagnostic(
self.r.dcx(),
errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg {
span,
ident: segment.ident,
snippet,
},
);
}
enum Side {
@ -1207,10 +1210,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
});
if let Some(param) = param {
err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg {
span: param.shrink_to_lo(),
applicability,
});
err.subdiagnostic(
self.r.dcx(),
errors::UnexpectedResChangeTyToConstParamSugg {
span: param.shrink_to_lo(),
applicability,
},
);
}
}

View file

@ -173,21 +173,21 @@ pub fn add_feature_diagnostics_for_issue(
feature_from_cli: bool,
) {
if let Some(n) = find_feature_issue(feature, issue) {
err.subdiagnostic(FeatureDiagnosticForIssue { n });
err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n });
}
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
if sess.parse_sess.unstable_features.is_nightly_build() {
if feature_from_cli {
err.subdiagnostic(CliFeatureDiagnosticHelp { feature });
err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature });
} else {
err.subdiagnostic(FeatureDiagnosticHelp { feature });
err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature });
}
if sess.opts.unstable_opts.ui_testing {
err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
err.subdiagnostic(sess.dcx(), SuggestUpgradeCompiler::ui_testing());
} else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
err.subdiagnostic(suggestion);
err.subdiagnostic(sess.dcx(), suggestion);
}
}
}

View file

@ -17,6 +17,7 @@ pub enum Arch {
Arm64e,
Arm64_32,
I386,
I386_sim,
I686,
X86_64,
X86_64h,
@ -34,7 +35,7 @@ impl Arch {
Arm64 | Arm64_macabi | Arm64_sim => "arm64",
Arm64e => "arm64e",
Arm64_32 => "arm64_32",
I386 => "i386",
I386 | I386_sim => "i386",
I686 => "i686",
X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
X86_64h => "x86_64h",
@ -45,7 +46,7 @@ impl Arch {
Cow::Borrowed(match self {
Armv7k | Armv7s => "arm",
Arm64 | Arm64e | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64",
I386 | I686 => "x86",
I386 | I386_sim | I686 => "x86",
X86_64 | X86_64_sim | X86_64_macabi | X86_64h => "x86_64",
})
}
@ -54,9 +55,7 @@ impl Arch {
match self {
Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "",
X86_64_macabi | Arm64_macabi => "macabi",
// x86_64-apple-ios is a simulator target, even though it isn't
// declared that way in the target like the other ones...
Arm64_sim | X86_64_sim => "sim",
I386_sim | Arm64_sim | X86_64_sim => "sim",
}
}
@ -70,7 +69,7 @@ impl Arch {
// Only macOS 10.12+ is supported, which means
// all x86_64/x86 CPUs must be running at least penryn
// https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82
I386 | I686 => "penryn",
I386 | I386_sim | I686 => "penryn",
X86_64 | X86_64_sim => "penryn",
X86_64_macabi => "penryn",
// Note: `core-avx2` is slightly more advanced than `x86_64h`, see
@ -85,7 +84,7 @@ impl Arch {
fn stack_probes(self) -> StackProbeType {
match self {
Armv7k | Armv7s => StackProbeType::None,
Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h | X86_64_sim
Arm64 | Arm64e | Arm64_32 | I386 | I386_sim | I686 | X86_64 | X86_64h | X86_64_sim
| X86_64_macabi | Arm64_macabi | Arm64_sim => StackProbeType::Inline,
}
}
@ -302,8 +301,8 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
// Otherwise if cross-compiling for a different OS/SDK, remove any part
// of the linking environment that's wrong and reversed.
match arch {
Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim
| X86_64h | Arm64_sim => {
Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I386_sim | I686 | X86_64
| X86_64_sim | X86_64h | Arm64_sim => {
cvs!["MACOSX_DEPLOYMENT_TARGET"]
}
X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],

View file

@ -2,7 +2,9 @@ use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch};
use crate::spec::{Target, TargetOptions};
pub fn target() -> Target {
let arch = Arch::I386;
// i386-apple-ios is a simulator target, even though it isn't declared
// that way in the target name like the other ones...
let arch = Arch::I386_sim;
Target {
// Clang automatically chooses a more specific target based on
// IPHONEOS_DEPLOYMENT_TARGET.

View file

@ -2,6 +2,8 @@ use crate::spec::base::apple::{ios_sim_llvm_target, opts, Arch};
use crate::spec::{SanitizerSet, Target, TargetOptions};
pub fn target() -> Target {
// x86_64-apple-ios is a simulator target, even though it isn't declared
// that way in the target name like the other ones...
let arch = Arch::X86_64_sim;
let mut base = opts("ios", arch);
base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;

View file

@ -2,6 +2,8 @@ use crate::spec::base::apple::{opts, tvos_sim_llvm_target, Arch};
use crate::spec::{Target, TargetOptions};
pub fn target() -> Target {
// x86_64-apple-tvos is a simulator target, even though it isn't declared
// that way in the target name like the other ones...
let arch = Arch::X86_64_sim;
Target {
llvm_target: tvos_sim_llvm_target(arch).into(),

View file

@ -17,49 +17,8 @@ use std::fmt::Debug;
pub use rustc_infer::infer::*;
pub trait InferCtxtExt<'tcx> {
fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool;
fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool;
/// Check whether a `ty` implements given trait(trait_def_id) without side-effects.
///
/// The inputs are:
///
/// - the def-id of the trait
/// - the type parameters of the trait, including the self-type
/// - the parameter environment
///
/// Invokes `evaluate_obligation`, so in the event that evaluating
/// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent
/// (or EvaluatedToAmbigStackDependent) will be returned.
fn type_implements_trait(
&self,
trait_def_id: DefId,
params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
param_env: ty::ParamEnv<'tcx>,
) -> traits::EvaluationResult;
/// Returns `Some` if a type implements a trait shallowly, without side-effects,
/// along with any errors that would have been reported upon further obligation
/// processing.
///
/// - If this returns `Some([])`, then the trait holds modulo regions.
/// - If this returns `Some([errors..])`, then the trait has an impl for
/// the self type, but some nested obligations do not hold.
/// - If this returns `None`, no implementation that applies could be found.
///
/// FIXME(-Znext-solver): Due to the recursive nature of the new solver,
/// this will probably only ever return `Some([])` or `None`.
fn type_implements_trait_shallow(
&self,
trait_def_id: DefId,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<Vec<traits::FulfillmentError<'tcx>>>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
let ty = self.resolve_vars_if_possible(ty);
@ -81,6 +40,17 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)
}
/// Check whether a `ty` implements given trait(trait_def_id) without side-effects.
///
/// The inputs are:
///
/// - the def-id of the trait
/// - the type parameters of the trait, including the self-type
/// - the parameter environment
///
/// Invokes `evaluate_obligation`, so in the event that evaluating
/// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent
/// (or EvaluatedToAmbigStackDependent) will be returned.
#[instrument(level = "debug", skip(self, params), ret)]
fn type_implements_trait(
&self,
@ -99,6 +69,17 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
}
/// Returns `Some` if a type implements a trait shallowly, without side-effects,
/// along with any errors that would have been reported upon further obligation
/// processing.
///
/// - If this returns `Some([])`, then the trait holds modulo regions.
/// - If this returns `Some([errors..])`, then the trait has an impl for
/// the self type, but some nested obligations do not hold.
/// - If this returns `None`, no implementation that applies could be found.
///
/// FIXME(-Znext-solver): Due to the recursive nature of the new solver,
/// this will probably only ever return `Some([])` or `None`.
fn type_implements_trait_shallow(
&self,
trait_def_id: DefId,
@ -124,19 +105,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
}
}
pub trait InferCtxtBuilderExt<'tcx> {
fn enter_canonical_trait_query<K, R>(
self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
where
K: TypeFoldable<TyCtxt<'tcx>>,
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
}
impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
#[extension(pub trait InferCtxtBuilderExt<'tcx>)]
impl<'tcx> InferCtxtBuilder<'tcx> {
/// The "main method" for a canonicalized trait query. Given the
/// canonical key `canonical_key`, this method will create a new
/// inference context, instantiate the key, and run your operation

View file

@ -3,20 +3,14 @@ use rustc_infer::infer::{InferCtxt, RegionResolutionError};
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::ObligationCause;
pub trait InferCtxtRegionExt<'tcx> {
#[extension(pub trait InferCtxtRegionExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Resolve regions, using the deep normalizer to normalize any type-outlives
/// obligations in the process. This is in `rustc_trait_selection` because
/// we need to normalize.
///
/// Prefer this method over `resolve_regions_with_normalize`, unless you are
/// doing something specific for normalization.
fn resolve_regions(
&self,
outlives_env: &OutlivesEnvironment<'tcx>,
) -> Vec<RegionResolutionError<'tcx>>;
}
impl<'tcx> InferCtxtRegionExt<'tcx> for InferCtxt<'tcx> {
fn resolve_regions(
&self,
outlives_env: &OutlivesEnvironment<'tcx>,

View file

@ -131,22 +131,12 @@ pub enum GenerateProofTree {
Never,
}
pub trait InferCtxtEvalExt<'tcx> {
#[extension(pub trait InferCtxtEvalExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Evaluates a goal from **outside** of the trait solver.
///
/// Using this while inside of the solver is wrong as it uses a new
/// search graph which would break cycle detection.
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
generate_proof_tree: GenerateProofTree,
) -> (
Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
Option<inspect::GoalEvaluation<'tcx>>,
);
}
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
#[instrument(level = "debug", skip(self))]
fn evaluate_root_goal(
&self,

View file

@ -17,14 +17,8 @@ use crate::solve::inspect::ProofTreeBuilder;
use crate::traits::StructurallyNormalizeExt;
use crate::traits::TraitEngineExt;
pub trait InferCtxtSelectExt<'tcx> {
fn select_in_new_trait_solver(
&self,
obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>>;
}
impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
#[extension(pub trait InferCtxtSelectExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
fn select_in_new_trait_solver(
&self,
obligation: &PolyTraitObligation<'tcx>,

View file

@ -216,22 +216,17 @@ pub trait ProofTreeVisitor<'tcx> {
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy>;
}
pub trait ProofTreeInferCtxtExt<'tcx> {
fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
visitor: &mut V,
) -> ControlFlow<V::BreakTy>;
}
impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> {
#[extension(pub trait ProofTreeInferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
visitor: &mut V,
) -> ControlFlow<V::BreakTy> {
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
let proof_tree = proof_tree.unwrap();
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
self.probe(|_| {
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
let proof_tree = proof_tree.unwrap();
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
})
}
}

View file

@ -61,11 +61,8 @@ enum GoalEvaluationKind {
Nested { is_normalizes_to_hack: IsNormalizesToHack },
}
trait CanonicalResponseExt {
fn has_no_inference_or_external_constraints(&self) -> bool;
}
impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
#[extension(trait CanonicalResponseExt)]
impl<'tcx> Canonical<'tcx, Response<'tcx>> {
fn has_no_inference_or_external_constraints(&self) -> bool {
self.value.external_constraints.region_constraints.is_empty()
&& self.value.var_values.is_identity()

View file

@ -8,23 +8,21 @@ use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::InferOk;
use crate::regions::InferCtxtRegionExt;
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::solve::{deeply_normalize_for_diagnostics, inspect};
use crate::traits::engine::TraitEngineExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::solve::{deeply_normalize_for_diagnostics, inspect, FulfillmentCtxt};
use crate::traits::engine::TraitEngineExt as _;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::structural_normalize::StructurallyNormalizeExt;
use crate::traits::NormalizeExt;
use crate::traits::SkipLeakCheck;
use crate::traits::{
Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
SelectionContext,
Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext,
};
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::Diagnostic;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{util, TraitEngine};
use rustc_infer::traits::{util, TraitEngine, TraitEngineExt};
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
use rustc_middle::traits::specialization_graph::OverlapMode;
@ -310,29 +308,35 @@ fn equate_impl_headers<'tcx>(
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligations: &'a [PredicateObligation<'tcx>],
) -> Option<&'a PredicateObligation<'tcx>> {
) -> Option<PredicateObligation<'tcx>> {
let infcx = selcx.infcx;
obligations.iter().find(|obligation| {
let evaluation_result = if infcx.next_trait_solver() {
infcx.evaluate_obligation(obligation)
} else {
if infcx.next_trait_solver() {
let mut fulfill_cx = FulfillmentCtxt::new(infcx);
fulfill_cx.register_predicate_obligations(infcx, obligations.iter().cloned());
// We only care about the obligations that are *definitely* true errors.
// Ambiguities do not prove the disjointness of two impls.
let mut errors = fulfill_cx.select_where_possible(infcx);
errors.pop().map(|err| err.obligation)
} else {
obligations.iter().cloned().find(|obligation| {
// We use `evaluate_root_obligation` to correctly track intercrate
// ambiguity clauses. We cannot use this in the new solver.
selcx.evaluate_root_obligation(obligation)
};
let evaluation_result = selcx.evaluate_root_obligation(obligation);
match evaluation_result {
Ok(result) => !result.may_apply(),
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
// since there can be instantiations of this goal that don't overflow and result in
// success. This isn't much of a problem in the old solver, since we treat overflow
// fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
// but in the new solver, this is very important for correctness, since overflow
// *must* be treated as ambiguity for completeness.
Err(_overflow) => false,
}
})
match evaluation_result {
Ok(result) => !result.may_apply(),
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
// since there can be instantiations of this goal that don't overflow and result in
// success. This isn't much of a problem in the old solver, since we treat overflow
// fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
// but in the new solver, this is very important for correctness, since overflow
// *must* be treated as ambiguity for completeness.
Err(_overflow) => false,
}
})
}
}
/// Check if both impls can be satisfied by a common type by considering whether
@ -522,15 +526,13 @@ fn try_prove_negated_where_clause<'tcx>(
// Without this, we over-eagerly register coherence ambiguity candidates when
// impl candidates do exist.
let ref infcx = root_infcx.fork_with_intercrate(false);
let ocx = ObligationCtxt::new(infcx);
let mut fulfill_cx = FulfillmentCtxt::new(infcx);
ocx.register_obligation(Obligation::new(
infcx.tcx,
ObligationCause::dummy(),
param_env,
negative_predicate,
));
if !ocx.select_all_or_error().is_empty() {
fulfill_cx.register_predicate_obligation(
infcx,
Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, negative_predicate),
);
if !fulfill_cx.select_all_or_error(infcx).is_empty() {
return false;
}

View file

@ -27,11 +27,8 @@ use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::Variance;
use rustc_middle::ty::{self, Ty, TyCtxt};
pub trait TraitEngineExt<'tcx> {
fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
}
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
#[extension(pub trait TraitEngineExt<'tcx>)]
impl<'tcx> dyn TraitEngine<'tcx> {
fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
if infcx.next_trait_solver() {
Box::new(NextFulfillmentCtxt::new(infcx))

View file

@ -11,38 +11,8 @@ use super::ArgKind;
pub use rustc_infer::traits::error_reporting::*;
pub trait InferCtxtExt<'tcx> {
/// Given some node representing a fn-like thing in the HIR map,
/// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to
/// `report_arg_count_mismatch`.
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>;
/// Reports an error when the number of arguments needed by a
/// trait match doesn't match the number that the expression
/// provides.
fn report_arg_count_mismatch(
&self,
span: Span,
found_span: Option<Span>,
expected_args: Vec<ArgKind>,
found_args: Vec<ArgKind>,
is_closure: bool,
closure_pipe_span: Option<Span>,
) -> DiagnosticBuilder<'tcx>;
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
/// in that order, and returns the generic type corresponding to the
/// argument of that trait (corresponding to the closure arguments).
fn type_implements_fn_trait(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: ty::Binder<'tcx, Ty<'tcx>>,
polarity: ty::ImplPolarity,
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Given some node representing a fn-like thing in the HIR map,
/// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to
@ -229,6 +199,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
err
}
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
/// in that order, and returns the generic type corresponding to the
/// argument of that trait (corresponding to the closure arguments).
fn type_implements_fn_trait(
&self,
param_env: ty::ParamEnv<'tcx>,

View file

@ -23,24 +23,6 @@ use crate::errors::{
use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
pub trait TypeErrCtxtExt<'tcx> {
/*private*/
fn impl_similar_to(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> Option<(DefId, GenericArgsRef<'tcx>)>;
/*private*/
fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str>;
fn on_unimplemented_note(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> OnUnimplementedNote;
}
/// The symbols which are always allowed in a format string
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
kw::SelfUpper,
@ -56,7 +38,8 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
sym::Trait,
];
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn impl_similar_to(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,

View file

@ -106,279 +106,6 @@ impl<'tcx, 'a> CoroutineData<'tcx, 'a> {
}
}
// This trait is public to expose the diagnostics methods to clippy.
pub trait TypeErrCtxtExt<'tcx> {
fn suggest_restricting_param_bound(
&self,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
associated_item: Option<(&'static str, Ty<'tcx>)>,
body_id: LocalDefId,
);
fn suggest_dereferences(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn get_closure_name(
&self,
def_id: DefId,
err: &mut Diagnostic,
msg: Cow<'static, str>,
) -> Option<Symbol>;
fn suggest_fn_call(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn check_for_binding_assigned_block_without_tail_expression(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
fn suggest_add_clone_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn extract_callable_info(
&self,
body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
found: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>;
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
has_custom_message: bool,
) -> bool;
fn suggest_borrowing_for_object_cast(
&self,
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
self_ty: Ty<'tcx>,
object_ty: Ty<'tcx>,
);
fn suggest_remove_reference(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
fn suggest_change_mut(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
fn suggest_semicolon_removal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
fn suggest_impl_trait(
&self,
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn point_at_returns_when_relevant(
&self,
err: &mut DiagnosticBuilder<'tcx>,
obligation: &PredicateObligation<'tcx>,
);
fn report_closure_arg_mismatch(
&self,
span: Span,
found_span: Option<Span>,
found: ty::PolyTraitRef<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
found_node: Option<Node<'_>>,
param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx>;
fn note_conflicting_fn_args(
&self,
err: &mut Diagnostic,
cause: &ObligationCauseCode<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
);
fn note_conflicting_closure_bounds(
&self,
cause: &ObligationCauseCode<'tcx>,
err: &mut DiagnosticBuilder<'tcx>,
);
fn suggest_fully_qualified_path(
&self,
err: &mut Diagnostic,
item_def_id: DefId,
span: Span,
trait_ref: DefId,
);
fn maybe_note_obligation_cause_for_async_await(
&self,
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
) -> bool;
fn note_obligation_cause_for_async_await(
&self,
err: &mut Diagnostic,
interior_or_upvar_span: CoroutineInteriorOrUpvar,
is_async: bool,
outer_coroutine: Option<DefId>,
trait_pred: ty::TraitPredicate<'tcx>,
target_ty: Ty<'tcx>,
obligation: &PredicateObligation<'tcx>,
next_code: Option<&ObligationCauseCode<'tcx>>,
);
fn note_obligation_cause_code<T>(
&self,
body_id: LocalDefId,
err: &mut Diagnostic,
predicate: T,
param_env: ty::ParamEnv<'tcx>,
cause_code: &ObligationCauseCode<'tcx>,
obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
T: ToPredicate<'tcx>;
/// Suggest to await before try: future? => future.await?
fn suggest_await_before_try(
&self,
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
span: Span,
);
fn suggest_floating_point_literal(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
);
fn suggest_derive(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
fn suggest_dereferencing_index(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
fn suggest_option_method_if_applicable(
&self,
failed_pred: ty::Predicate<'tcx>,
param_env: ty::ParamEnv<'tcx>,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
);
fn note_function_argument_obligation(
&self,
body_id: LocalDefId,
err: &mut Diagnostic,
arg_hir_id: HirId,
parent_code: &ObligationCauseCode<'tcx>,
param_env: ty::ParamEnv<'tcx>,
predicate: ty::Predicate<'tcx>,
call_hir_id: HirId,
);
fn look_for_iterator_item_mistakes(
&self,
assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
typeck_results: &TypeckResults<'tcx>,
type_diffs: &[TypeError<'tcx>],
param_env: ty::ParamEnv<'tcx>,
path_segment: &hir::PathSegment<'_>,
args: &[hir::Expr<'_>],
err: &mut Diagnostic,
);
fn point_at_chain(
&self,
expr: &hir::Expr<'_>,
typeck_results: &TypeckResults<'tcx>,
type_diffs: Vec<TypeError<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
err: &mut Diagnostic,
);
fn probe_assoc_types_at_expr(
&self,
type_diffs: &[TypeError<'tcx>],
span: Span,
prev_ty: Ty<'tcx>,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
fn suggest_convert_to_slice(
&self,
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
candidate_impls: &[ImplCandidate<'tcx>],
span: Span,
);
fn explain_hrtb_projection(
&self,
diag: &mut Diagnostic,
pred: ty::PolyTraitPredicate<'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: &ObligationCause<'tcx>,
);
fn suggest_desugaring_async_fn_in_trait(
&self,
err: &mut Diagnostic,
trait_ref: ty::PolyTraitRef<'tcx>,
);
}
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
(
generics.tail_span_for_predicate_suggestion(),
@ -509,7 +236,8 @@ pub fn suggest_restriction<'tcx>(
}
}
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn suggest_restricting_param_bound(
&self,
err: &mut Diagnostic,
@ -4830,11 +4558,14 @@ fn hint_missing_borrow<'tcx>(
}
if !to_borrow.is_empty() {
err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow });
err.subdiagnostic(infcx.dcx(), errors::AdjustSignatureBorrow::Borrow { to_borrow });
}
if !remove_borrow.is_empty() {
err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow });
err.subdiagnostic(
infcx.dcx(),
errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow },
);
}
}

View file

@ -57,78 +57,8 @@ use super::{
pub use rustc_infer::traits::error_reporting::*;
pub trait TypeErrCtxtExt<'tcx> {
fn build_overflow_error<T>(
&self,
predicate: &T,
span: Span,
suggest_increasing_limit: bool,
) -> DiagnosticBuilder<'tcx>
where
T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
fn report_overflow_error<T>(
&self,
predicate: &T,
span: Span,
suggest_increasing_limit: bool,
mutate: impl FnOnce(&mut Diagnostic),
) -> !
where
T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed;
fn report_fulfillment_errors(&self, errors: Vec<FulfillmentError<'tcx>>) -> ErrorGuaranteed;
fn report_overflow_obligation<T>(
&self,
obligation: &Obligation<'tcx, T>,
suggest_increasing_limit: bool,
) -> !
where
T: ToPredicate<'tcx> + Clone;
fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
/// The `root_obligation` parameter should be the `root_obligation` field
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
/// then it should be the same as `obligation`.
fn report_selection_error(
&self,
obligation: PredicateObligation<'tcx>,
root_obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
) -> ErrorGuaranteed;
fn emit_specialized_closure_kind_error(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Option<ErrorGuaranteed>;
fn fn_arg_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
) -> Result<(), ErrorGuaranteed>;
fn try_conversion_context(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
err: &mut Diagnostic,
) -> bool;
fn report_const_param_not_wf(
&self,
ty: Ty<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> DiagnosticBuilder<'tcx>;
}
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn report_fulfillment_errors(
&self,
mut errors: Vec<FulfillmentError<'tcx>>,
@ -382,6 +312,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.emit()
}
/// The `root_obligation` parameter should be the `root_obligation` field
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
/// then it should be the same as `obligation`.
fn report_selection_error(
&self,
mut obligation: PredicateObligation<'tcx>,
@ -1393,209 +1326,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
pub(super) trait InferCtxtPrivExt<'tcx> {
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
// `error` occurring implies that `cond` occurs.
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool;
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed;
fn report_projection_error(
&self,
obligation: &PredicateObligation<'tcx>,
error: &MismatchedProjectionTypes<'tcx>,
) -> ErrorGuaranteed;
fn maybe_detailed_projection_msg(
&self,
pred: ty::ProjectionPredicate<'tcx>,
normalized_ty: ty::Term<'tcx>,
expected_ty: ty::Term<'tcx>,
) -> Option<String>;
fn fuzzy_match_tys(
&self,
a: Ty<'tcx>,
b: Ty<'tcx>,
ignoring_lifetimes: bool,
) -> Option<CandidateSimilarity>;
fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str;
fn find_similar_impl_candidates(
&self,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> Vec<ImplCandidate<'tcx>>;
fn report_similar_impl_candidates(
&self,
impl_candidates: &[ImplCandidate<'tcx>],
trait_ref: ty::PolyTraitRef<'tcx>,
body_def_id: LocalDefId,
err: &mut Diagnostic,
other: bool,
param_env: ty::ParamEnv<'tcx>,
) -> bool;
fn report_similar_impl_candidates_for_root_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
body_def_id: LocalDefId,
err: &mut Diagnostic,
);
/// Gets the parent trait chain start
fn get_parent_trait_ref(
&self,
code: &ObligationCauseCode<'tcx>,
) -> Option<(Ty<'tcx>, Option<Span>)>;
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
/// with the same path as `trait_ref`, a help message about
/// a probable version mismatch is added to `err`
fn note_version_mismatch(
&self,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool;
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
/// `trait_ref`.
///
/// For this to work, `new_self_ty` must have no escaping bound variables.
fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,
trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
) -> PredicateObligation<'tcx>;
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed;
fn predicate_can_apply(
&self,
param_env: ty::ParamEnv<'tcx>,
pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>);
fn suggest_unsized_bound_if_applicable(
&self,
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
);
fn annotate_source_of_ambiguity(
&self,
err: &mut Diagnostic,
impls: &[ambiguity::Ambiguity],
predicate: ty::Predicate<'tcx>,
);
fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>);
fn maybe_indirection_for_unsized(
&self,
err: &mut Diagnostic,
item: &'tcx Item<'tcx>,
param: &'tcx GenericParam<'tcx>,
) -> bool;
fn is_recursive_obligation(
&self,
obligated_types: &mut Vec<Ty<'tcx>>,
cause_code: &ObligationCauseCode<'tcx>,
) -> bool;
fn get_standard_error_message(
&self,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
message: Option<String>,
predicate_is_const: bool,
append_const_msg: Option<AppendConstMessage>,
post_message: String,
) -> String;
fn get_safe_transmute_error_and_reason(
&self,
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
) -> GetSafeTransmuteErrorAndReason;
fn add_tuple_trait_message(
&self,
obligation_cause_code: &ObligationCauseCode<'tcx>,
err: &mut Diagnostic,
);
fn try_to_add_help_message(
&self,
obligation: &PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
err: &mut Diagnostic,
span: Span,
is_fn_trait: bool,
suggested: bool,
unsatisfied_const: bool,
);
fn add_help_message_for_fn_trait(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
implemented_kind: ty::ClosureKind,
params: ty::Binder<'tcx, Ty<'tcx>>,
);
fn maybe_add_note_for_unsatisfied_const(
&self,
trait_predicate: &ty::PolyTraitPredicate<'tcx>,
err: &mut Diagnostic,
span: Span,
) -> UnsatisfiedConst;
fn report_closure_error(
&self,
obligation: &PredicateObligation<'tcx>,
closure_def_id: DefId,
found_kind: ty::ClosureKind,
kind: ty::ClosureKind,
trait_prefix: &'static str,
) -> DiagnosticBuilder<'tcx>;
fn report_cyclic_signature_error(
&self,
obligation: &PredicateObligation<'tcx>,
found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
terr: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx>;
fn report_opaque_type_auto_trait_leakage(
&self,
obligation: &PredicateObligation<'tcx>,
def_id: DefId,
) -> DiagnosticBuilder<'tcx>;
fn report_signature_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
span: Span,
found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
) -> Result<DiagnosticBuilder<'tcx>, ErrorGuaranteed>;
fn report_not_const_evaluatable_error(
&self,
obligation: &PredicateObligation<'tcx>,
span: Span,
) -> Result<DiagnosticBuilder<'tcx>, ErrorGuaranteed>;
}
impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
#[extension(pub(super) trait InferCtxtPrivExt<'tcx>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
// `error` occurring implies that `cond` occurs.
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
@ -2414,6 +2146,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
suggested
}
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
/// `trait_ref`.
///
/// For this to work, `new_self_ty` must have no escaping bound variables.
fn mk_trait_obligation_with_new_self_ty(
&self,
param_env: ty::ParamEnv<'tcx>,

View file

@ -11,25 +11,6 @@ pub use rustc_middle::traits::query::OutlivesBound;
pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
pub trait InferCtxtExt<'a, 'tcx> {
/// Do *NOT* call this directly.
fn implied_bounds_tys_compat(
&'a self,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
tys: &'a FxIndexSet<Ty<'tcx>>,
compat: bool,
) -> BoundsCompat<'a, 'tcx>;
/// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat`
/// with `compat` set to `true`, otherwise `false`.
fn implied_bounds_tys(
&'a self,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
tys: &'a FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx>;
}
/// Implied bounds are region relationships that we deduce
/// automatically. The idea is that (e.g.) a caller must check that a
@ -130,7 +111,9 @@ fn implied_outlives_bounds<'a, 'tcx>(
bounds
}
impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
#[extension(pub trait InferCtxtExt<'a, 'tcx>)]
impl<'a, 'tcx: 'a> InferCtxt<'tcx> {
/// Do *NOT* call this directly.
fn implied_bounds_tys_compat(
&'a self,
param_env: ParamEnv<'tcx>,
@ -142,6 +125,8 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
.flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat))
}
/// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat`
/// with `compat` set to `true`, otherwise `false`.
fn implied_bounds_tys(
&'a self,
param_env: ParamEnv<'tcx>,

View file

@ -52,12 +52,22 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>;
pub(super) struct InProgress;
pub trait NormalizeExt<'tcx> {
#[extension(pub trait NormalizeExt<'tcx>)]
impl<'tcx> At<'_, 'tcx> {
/// Normalize a value using the `AssocTypeNormalizer`.
///
/// This normalization should be used when the type contains inference variables or the
/// projection may be fallible.
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
if self.infcx.next_trait_solver() {
InferOk { value, obligations: Vec::new() }
} else {
let mut selcx = SelectionContext::new(self.infcx);
let Normalized { value, obligations } =
normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
InferOk { value, obligations }
}
}
/// Deeply normalizes `value`, replacing all aliases which can by normalized in
/// the current environment. In the new solver this errors in case normalization
@ -73,25 +83,6 @@ pub trait NormalizeExt<'tcx> {
/// existing fulfillment context in the old solver. Once we also eagerly prove goals with
/// the old solver or have removed the old solver, remove `traits::fully_normalize` and
/// rename this function to `At::fully_normalize`.
fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
self,
value: T,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> Result<T, Vec<FulfillmentError<'tcx>>>;
}
impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
if self.infcx.next_trait_solver() {
InferOk { value, obligations: Vec::new() }
} else {
let mut selcx = SelectionContext::new(self.infcx);
let Normalized { value, obligations } =
normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
InferOk { value, obligations }
}
}
fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
self,
value: T,

View file

@ -4,32 +4,8 @@ use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferCtxt;
use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext};
pub trait InferCtxtExt<'tcx> {
fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool;
fn predicate_must_hold_considering_regions(
&self,
obligation: &PredicateObligation<'tcx>,
) -> bool;
fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool;
fn evaluate_obligation(
&self,
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError>;
// Helper function that canonicalizes and runs the query. If an
// overflow results, we re-run it in the local context so we can
// report a nice error.
/*crate*/
fn evaluate_obligation_no_overflow(
&self,
obligation: &PredicateObligation<'tcx>,
) -> EvaluationResult;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Evaluates whether the predicate can be satisfied (by any means)
/// in the given `ParamEnv`.
fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool {
@ -114,9 +90,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
}
}
// Helper function that canonicalizes and runs the query. If an
// overflow results, we re-run it in the local context so we can
// report a nice error.
/// Helper function that canonicalizes and runs the query. If an
/// overflow results, we re-run it in the local context so we can
/// report a nice error.
fn evaluate_obligation_no_overflow(
&self,
obligation: &PredicateObligation<'tcx>,

View file

@ -22,20 +22,8 @@ use super::NoSolution;
pub use rustc_middle::traits::query::NormalizationResult;
pub trait QueryNormalizeExt<'tcx> {
/// Normalize a value using the `QueryNormalizer`.
///
/// This normalization should *only* be used when the projection does not
/// have possible ambiguity or may not be well-formed.
///
/// After codegen, when lifetimes do not matter, it is preferable to instead
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<TyCtxt<'tcx>>;
}
impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
#[extension(pub trait QueryNormalizeExt<'tcx>)]
impl<'cx, 'tcx> At<'cx, 'tcx> {
/// Normalize `value` in the context of the inference context,
/// yielding a resulting type, or an error if `value` cannot be
/// normalized. If you don't care about regions, you should prefer
@ -49,6 +37,12 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
/// normalizing, but for now should be used only when we actually
/// know that normalization will succeed, since error reporting
/// and other details are still "under development".
///
/// This normalization should *only* be used when the projection does not
/// have possible ambiguity or may not be well-formed.
///
/// After codegen, when lifetimes do not matter, it is preferable to instead
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<TyCtxt<'tcx>>,

View file

@ -33,20 +33,8 @@ enum Inserted<'tcx> {
ShouldRecurseOn(DefId),
}
trait ChildrenExt<'tcx> {
fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
impl_def_id: DefId,
simplified_self: Option<SimplifiedType>,
overlap_mode: OverlapMode,
) -> Result<Inserted<'tcx>, OverlapError<'tcx>>;
}
impl<'tcx> ChildrenExt<'tcx> for Children {
#[extension(trait ChildrenExt<'tcx>)]
impl<'tcx> Children {
/// Insert an impl into this set of children without comparing to any existing impls.
fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
@ -247,22 +235,8 @@ where
}
}
pub trait GraphExt<'tcx> {
/// Insert a local impl into the specialization graph. If an existing impl
/// conflicts with it (has overlap, but neither specializes the other),
/// information about the area of overlap is returned in the `Err`.
fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
impl_def_id: DefId,
overlap_mode: OverlapMode,
) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>;
/// Insert cached metadata mapping from a child impl back to its parent.
fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId);
}
impl<'tcx> GraphExt<'tcx> for Graph {
#[extension(pub trait GraphExt<'tcx>)]
impl<'tcx> Graph {
/// Insert a local impl into the specialization graph. If an existing impl
/// conflicts with it (has overlap, but neither specializes the other),
/// information about the area of overlap is returned in the `Err`.

View file

@ -5,15 +5,8 @@ use rustc_middle::ty::{self, Ty};
use crate::traits::{NormalizeExt, Obligation};
pub trait StructurallyNormalizeExt<'tcx> {
fn structurally_normalize(
&self,
ty: Ty<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>>;
}
impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
#[extension(pub trait StructurallyNormalizeExt<'tcx>)]
impl<'tcx> At<'_, 'tcx> {
fn structurally_normalize(
&self,
ty: Ty<'tcx>,

4
configure vendored
View file

@ -1,6 +1,6 @@
#!/bin/sh
script="$(dirname $0)"/src/bootstrap/configure.py
script="$(dirname "$0")"/src/bootstrap/configure.py
try() {
cmd=$1
@ -15,4 +15,4 @@ try python3 "$@"
try python2.7 "$@"
try python27 "$@"
try python2 "$@"
exec python $script "$@"
exec python "$script" "$@"

View file

@ -27,8 +27,8 @@ pub struct Drain<
drain_len: usize,
// index into the logical array, not the physical one (always lies in [0..deque.len))
idx: usize,
// number of elements after the drain range
tail_len: usize,
// number of elements remaining after dropping the drain
new_len: usize,
remaining: usize,
// Needed to make Drain covariant over T
_marker: PhantomData<&'a T>,
@ -41,12 +41,12 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
drain_len: usize,
) -> Self {
let orig_len = mem::replace(&mut deque.len, drain_start);
let tail_len = orig_len - drain_start - drain_len;
let new_len = orig_len - drain_len;
Drain {
deque: NonNull::from(deque),
drain_len,
idx: drain_start,
tail_len,
new_len,
remaining: drain_len,
_marker: PhantomData,
}
@ -79,7 +79,7 @@ impl<T: fmt::Debug, A: Allocator> fmt::Debug for Drain<'_, T, A> {
f.debug_tuple("Drain")
.field(&self.drain_len)
.field(&self.idx)
.field(&self.tail_len)
.field(&self.new_len)
.field(&self.remaining)
.finish()
}
@ -95,70 +95,9 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
fn drop(&mut self) {
struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>);
impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {
fn drop(&mut self) {
if self.0.remaining != 0 {
unsafe {
// SAFETY: We just checked that `self.remaining != 0`.
let (front, back) = self.0.as_slices();
ptr::drop_in_place(front);
ptr::drop_in_place(back);
}
}
let source_deque = unsafe { self.0.deque.as_mut() };
let drain_start = source_deque.len();
let drain_len = self.0.drain_len;
let drain_end = drain_start + drain_len;
let orig_len = self.0.tail_len + drain_end;
if T::IS_ZST {
// no need to copy around any memory if T is a ZST
source_deque.len = orig_len - drain_len;
return;
}
let head_len = drain_start;
let tail_len = self.0.tail_len;
match (head_len, tail_len) {
(0, 0) => {
source_deque.head = 0;
source_deque.len = 0;
}
(0, _) => {
source_deque.head = source_deque.to_physical_idx(drain_len);
source_deque.len = orig_len - drain_len;
}
(_, 0) => {
source_deque.len = orig_len - drain_len;
}
_ => unsafe {
if head_len <= tail_len {
source_deque.wrap_copy(
source_deque.head,
source_deque.to_physical_idx(drain_len),
head_len,
);
source_deque.head = source_deque.to_physical_idx(drain_len);
source_deque.len = orig_len - drain_len;
} else {
source_deque.wrap_copy(
source_deque.to_physical_idx(head_len + drain_len),
source_deque.to_physical_idx(head_len),
tail_len,
);
source_deque.len = orig_len - drain_len;
}
},
}
}
}
let guard = DropGuard(self);
if guard.0.remaining != 0 {
if mem::needs_drop::<T>() && guard.0.remaining != 0 {
unsafe {
// SAFETY: We just checked that `self.remaining != 0`.
let (front, back) = guard.0.as_slices();
@ -172,6 +111,125 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
}
// Dropping `guard` handles moving the remaining elements into place.
impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {
#[inline]
fn drop(&mut self) {
if mem::needs_drop::<T>() && self.0.remaining != 0 {
unsafe {
// SAFETY: We just checked that `self.remaining != 0`.
let (front, back) = self.0.as_slices();
ptr::drop_in_place(front);
ptr::drop_in_place(back);
}
}
let source_deque = unsafe { self.0.deque.as_mut() };
let drain_len = self.0.drain_len;
let new_len = self.0.new_len;
if T::IS_ZST {
// no need to copy around any memory if T is a ZST
source_deque.len = new_len;
return;
}
let head_len = source_deque.len; // #elements in front of the drain
let tail_len = new_len - head_len; // #elements behind the drain
// Next, we will fill the hole left by the drain with as few writes as possible.
// The code below handles the following control flow and reduces the amount of
// branches under the assumption that `head_len == 0 || tail_len == 0`, i.e.
// draining at the front or at the back of the dequeue is especially common.
//
// H = "head index" = `deque.head`
// h = elements in front of the drain
// d = elements in the drain
// t = elements behind the drain
//
// Note that the buffer may wrap at any point and the wrapping is handled by
// `wrap_copy` and `to_physical_idx`.
//
// Case 1: if `head_len == 0 && tail_len == 0`
// Everything was drained, reset the head index back to 0.
// H
// [ . . . . . d d d d . . . . . ]
// H
// [ . . . . . . . . . . . . . . ]
//
// Case 2: else if `tail_len == 0`
// Don't move data or the head index.
// H
// [ . . . h h h h d d d d . . . ]
// H
// [ . . . h h h h . . . . . . . ]
//
// Case 3: else if `head_len == 0`
// Don't move data, but move the head index.
// H
// [ . . . d d d d t t t t . . . ]
// H
// [ . . . . . . . t t t t . . . ]
//
// Case 4: else if `tail_len <= head_len`
// Move data, but not the head index.
// H
// [ . . h h h h d d d d t t . . ]
// H
// [ . . h h h h t t . . . . . . ]
//
// Case 5: else
// Move data and the head index.
// H
// [ . . h h d d d d t t t t . . ]
// H
// [ . . . . . . h h t t t t . . ]
// When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`),
// we don't need to copy any data. The number of elements copied would be 0.
if head_len != 0 && tail_len != 0 {
join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len);
// Marking this function as cold helps LLVM to eliminate it entirely if
// this branch is never taken.
// We use `#[cold]` instead of `#[inline(never)]`, because inlining this
// function into the general case (`.drain(n..m)`) is fine.
// See `tests/codegen/vecdeque-drain.rs` for a test.
#[cold]
fn join_head_and_tail_wrapping<T, A: Allocator>(
source_deque: &mut VecDeque<T, A>,
drain_len: usize,
head_len: usize,
tail_len: usize,
) {
// Pick whether to move the head or the tail here.
let (src, dst, len);
if head_len < tail_len {
src = source_deque.head;
dst = source_deque.to_physical_idx(drain_len);
len = head_len;
} else {
src = source_deque.to_physical_idx(head_len + drain_len);
dst = source_deque.to_physical_idx(head_len);
len = tail_len;
};
unsafe {
source_deque.wrap_copy(src, dst, len);
}
}
}
if new_len == 0 {
// Special case: If the entire dequeue was drained, reset the head back to 0,
// like `.clear()` does.
source_deque.head = 0;
} else if head_len < tail_len {
// If we moved the head above, then we need to adjust the head index here.
source_deque.head = source_deque.to_physical_idx(drain_len);
}
source_deque.len = new_len;
}
}
}
}

View file

@ -485,10 +485,10 @@ impl<T, A: Allocator> VecDeque<T, A> {
// H := head
// L := last element (`self.to_physical_idx(self.len - 1)`)
//
// H L
// [o o o o o o o . ]
// H L
// A [o o o o o o o . . . . . . . . . ]
// H L
// [o o o o o o o o ]
// H L
// A [o o o o o o o o . . . . . . . . ]
// L H
// [o o o o o o o o ]
// H L

View file

@ -11,7 +11,7 @@ use core::iter::{
TrustedRandomAccessNoCoerce,
};
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};
use core::num::NonZero;
#[cfg(not(no_global_oom_handling))]
use core::ops::Deref;
@ -200,27 +200,23 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
#[inline]
fn next(&mut self) -> Option<T> {
if T::IS_ZST {
if self.ptr.as_ptr() == self.end as *mut _ {
None
} else {
// `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by
// reducing the `end`.
self.end = self.end.wrapping_byte_sub(1);
// Make up a value of this ZST.
Some(unsafe { mem::zeroed() })
let ptr = if T::IS_ZST {
if self.ptr.as_ptr() == self.end as *mut T {
return None;
}
// `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by
// reducing the `end`.
self.end = self.end.wrapping_byte_sub(1);
self.ptr
} else {
if self.ptr == non_null!(self.end, T) {
None
} else {
let old = self.ptr;
self.ptr = unsafe { old.add(1) };
Some(unsafe { ptr::read(old.as_ptr()) })
return None;
}
}
let old = self.ptr;
self.ptr = unsafe { old.add(1) };
old
};
Some(unsafe { ptr.read() })
}
#[inline]
@ -305,7 +301,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
// Also note the implementation of `Self: TrustedRandomAccess` requires
// that `T: Copy` so reading elements from the buffer doesn't invalidate
// them for `Drop`.
unsafe { if T::IS_ZST { mem::zeroed() } else { self.ptr.add(i).read() } }
unsafe { self.ptr.add(i).read() }
}
}
@ -314,23 +310,22 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
#[inline]
fn next_back(&mut self) -> Option<T> {
if T::IS_ZST {
if self.end as *mut _ == self.ptr.as_ptr() {
None
} else {
// See above for why 'ptr.offset' isn't used
self.end = self.end.wrapping_byte_sub(1);
// Make up a value of this ZST.
Some(unsafe { mem::zeroed() })
if self.ptr.as_ptr() == self.end as *mut _ {
return None;
}
// See above for why 'ptr.offset' isn't used
self.end = self.end.wrapping_byte_sub(1);
// Note that even though this is next_back() we're reading from `self.ptr`, not
// `self.end`. We track our length using the byte offset from `self.ptr` to `self.end`,
// so the end pointer may not be suitably aligned for T.
Some(unsafe { ptr::read(self.ptr.as_ptr()) })
} else {
if non_null!(self.end, T) == self.ptr {
None
} else {
let new_end = unsafe { non_null!(self.end, T).sub(1) };
*non_null!(mut self.end, T) = new_end;
Some(unsafe { ptr::read(new_end.as_ptr()) })
if self.ptr == non_null!(self.end, T) {
return None;
}
unsafe {
self.end = self.end.sub(1);
Some(ptr::read(self.end))
}
}
}

View file

@ -937,52 +937,68 @@ extern "rust-intrinsic" {
#[rustc_nounwind]
pub fn unreachable() -> !;
/// Informs the optimizer that a condition is always true.
/// If the condition is false, the behavior is undefined.
///
/// No code is generated for this intrinsic, but the optimizer will try
/// to preserve it (and its condition) between passes, which may interfere
/// with optimization of surrounding code and reduce performance. It should
/// not be used if the invariant can be discovered by the optimizer on its
/// own, or if it does not enable any significant optimizations.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_stable(feature = "const_assume", since = "1.77.0")]
#[rustc_nounwind]
pub fn assume(b: bool);
}
/// Hints to the compiler that branch condition is likely to be true.
/// Returns the value passed to it.
///
/// Any use other than with `if` statements will probably not have an effect.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_unstable(feature = "const_likely", issue = "none")]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn likely(b: bool) -> bool;
/// Informs the optimizer that a condition is always true.
/// If the condition is false, the behavior is undefined.
///
/// No code is generated for this intrinsic, but the optimizer will try
/// to preserve it (and its condition) between passes, which may interfere
/// with optimization of surrounding code and reduce performance. It should
/// not be used if the invariant can be discovered by the optimizer on its
/// own, or if it does not enable any significant optimizations.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_stable(feature = "const_assume", since = "1.77.0")]
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
pub const unsafe fn assume(b: bool) {
if !b {
// SAFETY: the caller must guarantee the argument is never `false`
unsafe { unreachable() }
}
}
/// Hints to the compiler that branch condition is likely to be false.
/// Returns the value passed to it.
///
/// Any use other than with `if` statements will probably not have an effect.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_unstable(feature = "const_likely", issue = "none")]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
pub fn unlikely(b: bool) -> bool;
/// Hints to the compiler that branch condition is likely to be true.
/// Returns the value passed to it.
///
/// Any use other than with `if` statements will probably not have an effect.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_unstable(feature = "const_likely", issue = "none")]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
#[rustc_nounwind]
pub const fn likely(b: bool) -> bool {
b
}
/// Hints to the compiler that branch condition is likely to be false.
/// Returns the value passed to it.
///
/// Any use other than with `if` statements will probably not have an effect.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_unstable(feature = "const_likely", issue = "none")]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
#[rustc_nounwind]
pub const fn unlikely(b: bool) -> bool {
b
}
extern "rust-intrinsic" {
/// Executes a breakpoint trap, for inspection by a debugger.
///
/// This intrinsic does not have a stable counterpart.

View file

@ -3,7 +3,7 @@ use crate::iter::{
Cloned, Copied, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable, Map, TrustedFused,
TrustedLen,
};
use crate::iter::{Once, OnceWith};
use crate::iter::{Empty, Once, OnceWith};
use crate::num::NonZero;
use crate::ops::{ControlFlow, Try};
use crate::result;
@ -593,6 +593,7 @@ where
}
}
// See also the `OneShot` specialization below.
impl<I, U> Iterator for FlattenCompat<I, U>
where
I: Iterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
@ -601,7 +602,7 @@ where
type Item = U::Item;
#[inline]
fn next(&mut self) -> Option<U::Item> {
default fn next(&mut self) -> Option<U::Item> {
loop {
if let elt @ Some(_) = and_then_or_clear(&mut self.frontiter, Iterator::next) {
return elt;
@ -614,7 +615,7 @@ where
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
default fn size_hint(&self) -> (usize, Option<usize>) {
let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), U::size_hint);
let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), U::size_hint);
let lo = flo.saturating_add(blo);
@ -636,7 +637,7 @@ where
}
#[inline]
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
default fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
where
Self: Sized,
Fold: FnMut(Acc, Self::Item) -> R,
@ -653,7 +654,7 @@ where
}
#[inline]
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
default fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
@ -669,7 +670,7 @@ where
#[inline]
#[rustc_inherit_overflow_checks]
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
default fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
#[inline]
#[rustc_inherit_overflow_checks]
fn advance<U: Iterator>(n: usize, iter: &mut U) -> ControlFlow<(), usize> {
@ -686,7 +687,7 @@ where
}
#[inline]
fn count(self) -> usize {
default fn count(self) -> usize {
#[inline]
#[rustc_inherit_overflow_checks]
fn count<U: Iterator>(acc: usize, iter: U) -> usize {
@ -697,7 +698,7 @@ where
}
#[inline]
fn last(self) -> Option<Self::Item> {
default fn last(self) -> Option<Self::Item> {
#[inline]
fn last<U: Iterator>(last: Option<U::Item>, iter: U) -> Option<U::Item> {
iter.last().or(last)
@ -707,13 +708,14 @@ where
}
}
// See also the `OneShot` specialization below.
impl<I, U> DoubleEndedIterator for FlattenCompat<I, U>
where
I: DoubleEndedIterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
U: DoubleEndedIterator,
{
#[inline]
fn next_back(&mut self) -> Option<U::Item> {
default fn next_back(&mut self) -> Option<U::Item> {
loop {
if let elt @ Some(_) = and_then_or_clear(&mut self.backiter, |b| b.next_back()) {
return elt;
@ -726,7 +728,7 @@ where
}
#[inline]
fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
default fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
where
Self: Sized,
Fold: FnMut(Acc, Self::Item) -> R,
@ -743,7 +745,7 @@ where
}
#[inline]
fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
default fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
@ -759,7 +761,7 @@ where
#[inline]
#[rustc_inherit_overflow_checks]
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
default fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
#[inline]
#[rustc_inherit_overflow_checks]
fn advance<U: DoubleEndedIterator>(n: usize, iter: &mut U) -> ControlFlow<(), usize> {
@ -841,3 +843,198 @@ fn and_then_or_clear<T, U>(opt: &mut Option<T>, f: impl FnOnce(&mut T) -> Option
}
x
}
/// Specialization trait for iterator types that never return more than one item.
///
/// Note that we still have to deal with the possibility that the iterator was
/// already exhausted before it came into our control.
#[rustc_specialization_trait]
trait OneShot {}
// These all have exactly one item, if not already consumed.
impl<T> OneShot for Once<T> {}
impl<F> OneShot for OnceWith<F> {}
impl<T> OneShot for array::IntoIter<T, 1> {}
impl<T> OneShot for option::IntoIter<T> {}
impl<T> OneShot for option::Iter<'_, T> {}
impl<T> OneShot for option::IterMut<'_, T> {}
impl<T> OneShot for result::IntoIter<T> {}
impl<T> OneShot for result::Iter<'_, T> {}
impl<T> OneShot for result::IterMut<'_, T> {}
// These are always empty, which is fine to optimize too.
impl<T> OneShot for Empty<T> {}
impl<T> OneShot for array::IntoIter<T, 0> {}
// These adaptors never increase the number of items.
// (There are more possible, but for now this matches BoundedSize above.)
impl<I: OneShot> OneShot for Cloned<I> {}
impl<I: OneShot> OneShot for Copied<I> {}
impl<I: OneShot, P> OneShot for Filter<I, P> {}
impl<I: OneShot, P> OneShot for FilterMap<I, P> {}
impl<I: OneShot, F> OneShot for Map<I, F> {}
// Blanket impls pass this property through as well
// (but we can't do `Box<I>` unless we expose this trait to alloc)
impl<I: OneShot> OneShot for &mut I {}
#[inline]
fn into_item<I>(inner: I) -> Option<I::Item>
where
I: IntoIterator<IntoIter: OneShot>,
{
inner.into_iter().next()
}
#[inline]
fn flatten_one<I: IntoIterator<IntoIter: OneShot>, Acc>(
mut fold: impl FnMut(Acc, I::Item) -> Acc,
) -> impl FnMut(Acc, I) -> Acc {
move |acc, inner| match inner.into_iter().next() {
Some(item) => fold(acc, item),
None => acc,
}
}
#[inline]
fn try_flatten_one<I: IntoIterator<IntoIter: OneShot>, Acc, R: Try<Output = Acc>>(
mut fold: impl FnMut(Acc, I::Item) -> R,
) -> impl FnMut(Acc, I) -> R {
move |acc, inner| match inner.into_iter().next() {
Some(item) => fold(acc, item),
None => try { acc },
}
}
#[inline]
fn advance_by_one<I>(n: NonZero<usize>, inner: I) -> Option<NonZero<usize>>
where
I: IntoIterator<IntoIter: OneShot>,
{
match inner.into_iter().next() {
Some(_) => NonZero::new(n.get() - 1),
None => Some(n),
}
}
// Specialization: When the inner iterator `U` never returns more than one item, the `frontiter` and
// `backiter` states are a waste, because they'll always have already consumed their item. So in
// this impl, we completely ignore them and just focus on `self.iter`, and we only call the inner
// `U::next()` one time.
//
// It's mostly fine if we accidentally mix this with the more generic impls, e.g. by forgetting to
// specialize one of the methods. If the other impl did set the front or back, we wouldn't see it
// here, but it would be empty anyway; and if the other impl looked for a front or back that we
// didn't bother setting, it would just see `None` (or a previous empty) and move on.
//
// An exception to that is `advance_by(0)` and `advance_back_by(0)`, where the generic impls may set
// `frontiter` or `backiter` without consuming the item, so we **must** override those.
impl<I, U> Iterator for FlattenCompat<I, U>
where
I: Iterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
U: Iterator + OneShot,
{
#[inline]
fn next(&mut self) -> Option<U::Item> {
while let Some(inner) = self.iter.next() {
if let item @ Some(_) = inner.into_iter().next() {
return item;
}
}
None
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, upper) = self.iter.size_hint();
match <I::Item as ConstSizeIntoIterator>::size() {
Some(0) => (0, Some(0)),
Some(1) => (lower, upper),
_ => (0, upper),
}
}
#[inline]
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
where
Self: Sized,
Fold: FnMut(Acc, Self::Item) -> R,
R: Try<Output = Acc>,
{
self.iter.try_fold(init, try_flatten_one(fold))
}
#[inline]
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
self.iter.fold(init, flatten_one(fold))
}
#[inline]
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
if let Some(n) = NonZero::new(n) {
self.iter.try_fold(n, advance_by_one).map_or(Ok(()), Err)
} else {
// Just advance the outer iterator
self.iter.advance_by(0)
}
}
#[inline]
fn count(self) -> usize {
self.iter.filter_map(into_item).count()
}
#[inline]
fn last(self) -> Option<Self::Item> {
self.iter.filter_map(into_item).last()
}
}
// Note: We don't actually care about `U: DoubleEndedIterator`, since forward and backward are the
// same for a one-shot iterator, but we have to keep that to match the default specialization.
impl<I, U> DoubleEndedIterator for FlattenCompat<I, U>
where
I: DoubleEndedIterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
U: DoubleEndedIterator + OneShot,
{
#[inline]
fn next_back(&mut self) -> Option<U::Item> {
while let Some(inner) = self.iter.next_back() {
if let item @ Some(_) = inner.into_iter().next() {
return item;
}
}
None
}
#[inline]
fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
where
Self: Sized,
Fold: FnMut(Acc, Self::Item) -> R,
R: Try<Output = Acc>,
{
self.iter.try_rfold(init, try_flatten_one(fold))
}
#[inline]
fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
self.iter.rfold(init, flatten_one(fold))
}
#[inline]
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
if let Some(n) = NonZero::new(n) {
self.iter.try_rfold(n, advance_by_one).map_or(Ok(()), Err)
} else {
// Just advance the outer iterator
self.iter.advance_back_by(0)
}
}
}

View file

@ -3016,8 +3016,13 @@ impl<T> [T] {
/// ```
/// let mut v = [-5i32, 4, 2, -3, 1];
///
/// // Find the median
/// v.select_nth_unstable(2);
/// // Find the items less than or equal to the median, the median, and greater than or equal to
/// // the median.
/// let (lesser, median, greater) = v.select_nth_unstable(2);
///
/// assert!(lesser == [-3, -5] || lesser == [-5, -3]);
/// assert_eq!(median, &mut 1);
/// assert!(greater == [4, 2] || greater == [2, 4]);
///
/// // We are only guaranteed the slice will be one of the following, based on the way we sort
/// // about the specified index.
@ -3067,8 +3072,13 @@ impl<T> [T] {
/// ```
/// let mut v = [-5i32, 4, 2, -3, 1];
///
/// // Find the median as if the slice were sorted in descending order.
/// v.select_nth_unstable_by(2, |a, b| b.cmp(a));
/// // Find the items less than or equal to the median, the median, and greater than or equal to
/// // the median as if the slice were sorted in descending order.
/// let (lesser, median, greater) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
///
/// assert!(lesser == [4, 2] || lesser == [2, 4]);
/// assert_eq!(median, &mut 1);
/// assert!(greater == [-3, -5] || greater == [-5, -3]);
///
/// // We are only guaranteed the slice will be one of the following, based on the way we sort
/// // about the specified index.
@ -3122,8 +3132,13 @@ impl<T> [T] {
/// ```
/// let mut v = [-5i32, 4, 1, -3, 2];
///
/// // Return the median as if the array were sorted according to absolute value.
/// v.select_nth_unstable_by_key(2, |a| a.abs());
/// // Find the items less than or equal to the median, the median, and greater than or equal to
/// // the median as if the slice were sorted according to absolute value.
/// let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
///
/// assert!(lesser == [1, 2] || lesser == [2, 1]);
/// assert_eq!(median, &mut -3);
/// assert!(greater == [4, -5] || greater == [-5, 4]);
///
/// // We are only guaranteed the slice will be one of the following, based on the way we sort
/// // about the specified index.

View file

@ -212,3 +212,69 @@ fn test_flatten_last() {
assert_eq!(it.advance_by(3), Ok(())); // 22..22
assert_eq!(it.clone().last(), None);
}
#[test]
fn test_flatten_one_shot() {
// This could be `filter_map`, but people often do flatten options.
let mut it = (0i8..10).flat_map(|i| NonZero::new(i % 7));
assert_eq!(it.size_hint(), (0, Some(10)));
assert_eq!(it.clone().count(), 8);
assert_eq!(it.clone().last(), NonZero::new(2));
// sum -> fold
let sum: i8 = it.clone().map(|n| n.get()).sum();
assert_eq!(sum, 24);
// the product overflows at 6, remaining are 7,8,9 -> 1,2
let one = NonZero::new(1i8).unwrap();
let product = it.try_fold(one, |acc, x| acc.checked_mul(x));
assert_eq!(product, None);
assert_eq!(it.size_hint(), (0, Some(3)));
assert_eq!(it.clone().count(), 2);
assert_eq!(it.advance_by(0), Ok(()));
assert_eq!(it.clone().next(), NonZero::new(1));
assert_eq!(it.advance_by(1), Ok(()));
assert_eq!(it.clone().next(), NonZero::new(2));
assert_eq!(it.advance_by(100), Err(NonZero::new(99).unwrap()));
assert_eq!(it.next(), None);
}
#[test]
fn test_flatten_one_shot_rev() {
let mut it = (0i8..10).flat_map(|i| NonZero::new(i % 7)).rev();
assert_eq!(it.size_hint(), (0, Some(10)));
assert_eq!(it.clone().count(), 8);
assert_eq!(it.clone().last(), NonZero::new(1));
// sum -> Rev fold -> rfold
let sum: i8 = it.clone().map(|n| n.get()).sum();
assert_eq!(sum, 24);
// Rev try_fold -> try_rfold
// the product overflows at 4, remaining are 3,2,1,0 -> 3,2,1
let one = NonZero::new(1i8).unwrap();
let product = it.try_fold(one, |acc, x| acc.checked_mul(x));
assert_eq!(product, None);
assert_eq!(it.size_hint(), (0, Some(4)));
assert_eq!(it.clone().count(), 3);
// Rev advance_by -> advance_back_by
assert_eq!(it.advance_by(0), Ok(()));
assert_eq!(it.clone().next(), NonZero::new(3));
assert_eq!(it.advance_by(1), Ok(()));
assert_eq!(it.clone().next(), NonZero::new(2));
assert_eq!(it.advance_by(100), Err(NonZero::new(98).unwrap()));
assert_eq!(it.next(), None);
}
#[test]
fn test_flatten_one_shot_arrays() {
let it = (0..10).flat_map(|i| [i]);
assert_eq!(it.size_hint(), (10, Some(10)));
assert_eq!(it.sum::<i32>(), 45);
let mut it = (0..10).flat_map(|_| -> [i32; 0] { [] });
assert_eq!(it.size_hint(), (0, Some(0)));
assert_eq!(it.next(), None);
}

View file

@ -318,13 +318,33 @@ pub fn temp_dir() -> PathBuf {
super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
}
#[cfg(not(target_vendor = "uwp"))]
#[cfg(all(not(target_vendor = "uwp"), not(target_vendor = "win7")))]
fn home_dir_crt() -> Option<PathBuf> {
unsafe {
// Defined in processthreadsapi.h.
const CURRENT_PROCESS_TOKEN: usize = -4_isize as usize;
super::fill_utf16_buf(
|buf, mut sz| {
match c::GetUserProfileDirectoryW(
ptr::invalid_mut(CURRENT_PROCESS_TOKEN),
buf,
&mut sz,
) {
0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0,
0 => sz,
_ => sz - 1, // sz includes the null terminator
}
},
super::os2path,
)
.ok()
}
}
#[cfg(target_vendor = "win7")]
fn home_dir_crt() -> Option<PathBuf> {
unsafe {
// The magic constant -4 can be used as the token passed to GetUserProfileDirectoryW below
// instead of us having to go through these multiple steps to get a token. However this is
// not implemented on Windows 7, only Windows 8 and up. When we drop support for Windows 7
// we can simplify this code. See #90144 for details.
use crate::sys::handle::Handle;
let me = c::GetCurrentProcess();

Some files were not shown because too many files have changed in this diff Show more