Merge from rustc
This commit is contained in:
commit
7a086ac887
525 changed files with 6884 additions and 3609 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -33,7 +33,7 @@
|
|||
[submodule "src/llvm-project"]
|
||||
path = src/llvm-project
|
||||
url = https://github.com/rust-lang/llvm-project.git
|
||||
branch = rustc/17.0-2023-12-14
|
||||
branch = rustc/18.0-2024-02-13
|
||||
shallow = true
|
||||
[submodule "src/doc/embedded-book"]
|
||||
path = src/doc/embedded-book
|
||||
|
|
1222
Cargo.lock
1222
Cargo.lock
File diff suppressed because it is too large
Load diff
|
@ -4,9 +4,10 @@ version = "0.0.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# FIXME: bumping memchr to 2.7.1 causes linker errors in MSVC thin-lto
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
memchr = "2.5.0"
|
||||
memchr = "=2.5.0"
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
|
|
|
@ -323,9 +323,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
)
|
||||
}
|
||||
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
|
||||
ExprKind::Err => {
|
||||
hir::ExprKind::Err(self.dcx().span_delayed_bug(e.span, "lowered ExprKind::Err"))
|
||||
}
|
||||
ExprKind::Err => hir::ExprKind::Err(self.dcx().has_errors().unwrap()),
|
||||
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
|
||||
|
||||
ExprKind::Paren(_) | ExprKind::ForLoop { .. } => {
|
||||
|
|
|
@ -1068,7 +1068,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> {
|
||||
match block {
|
||||
Some(block) => self.lower_block_expr(block),
|
||||
None => self.expr_err(span, self.dcx().span_delayed_bug(span, "no block")),
|
||||
None => self.expr_err(span, self.dcx().has_errors().unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1285,9 +1285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
|
||||
let kind = match &t.kind {
|
||||
TyKind::Infer => hir::TyKind::Infer,
|
||||
TyKind::Err => {
|
||||
hir::TyKind::Err(self.dcx().span_delayed_bug(t.span, "TyKind::Err lowered"))
|
||||
}
|
||||
TyKind::Err => hir::TyKind::Err(self.dcx().has_errors().unwrap()),
|
||||
// Lower the anonymous structs or unions in a nested lowering context.
|
||||
//
|
||||
// ```
|
||||
|
|
|
@ -1593,44 +1593,98 @@ fn deny_equality_constraints(
|
|||
}
|
||||
}
|
||||
}
|
||||
// Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
|
||||
if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
|
||||
if let [potential_param, potential_assoc] = &full_path.segments[..] {
|
||||
for param in &generics.params {
|
||||
if param.ident == potential_param.ident {
|
||||
for bound in ¶m.bounds {
|
||||
if let ast::GenericBound::Trait(trait_ref, TraitBoundModifiers::NONE) =
|
||||
bound
|
||||
|
||||
let mut suggest =
|
||||
|poly: &PolyTraitRef, potential_assoc: &PathSegment, predicate: &WhereEqPredicate| {
|
||||
if let [trait_segment] = &poly.trait_ref.path.segments[..] {
|
||||
let assoc = pprust::path_to_string(&ast::Path::from_ident(potential_assoc.ident));
|
||||
let ty = pprust::ty_to_string(&predicate.rhs_ty);
|
||||
let (args, span) = match &trait_segment.args {
|
||||
Some(args) => match args.deref() {
|
||||
ast::GenericArgs::AngleBracketed(args) => {
|
||||
let Some(arg) = args.args.last() else {
|
||||
return;
|
||||
};
|
||||
(format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()),
|
||||
};
|
||||
let removal_span = if generics.where_clause.predicates.len() == 1 {
|
||||
// We're removing th eonly where bound left, remove the whole thing.
|
||||
generics.where_clause.span
|
||||
} else {
|
||||
let mut span = predicate.span;
|
||||
let mut prev: Option<Span> = None;
|
||||
let mut preds = generics.where_clause.predicates.iter().peekable();
|
||||
// Find the predicate that shouldn't have been in the where bound list.
|
||||
while let Some(pred) = preds.next() {
|
||||
if let WherePredicate::EqPredicate(pred) = pred
|
||||
&& pred.span == predicate.span
|
||||
{
|
||||
if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] {
|
||||
let assoc = pprust::path_to_string(&ast::Path::from_ident(
|
||||
potential_assoc.ident,
|
||||
));
|
||||
let ty = pprust::ty_to_string(&predicate.rhs_ty);
|
||||
let (args, span) = match &trait_segment.args {
|
||||
Some(args) => match args.deref() {
|
||||
ast::GenericArgs::AngleBracketed(args) => {
|
||||
let Some(arg) = args.args.last() else {
|
||||
continue;
|
||||
};
|
||||
(format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
None => (
|
||||
format!("<{assoc} = {ty}>"),
|
||||
trait_segment.span().shrink_to_hi(),
|
||||
),
|
||||
};
|
||||
err.assoc2 = Some(errors::AssociatedSuggestion2 {
|
||||
span,
|
||||
args,
|
||||
predicate: predicate.span,
|
||||
trait_segment: trait_segment.ident,
|
||||
potential_assoc: potential_assoc.ident,
|
||||
});
|
||||
if let Some(next) = preds.peek() {
|
||||
// This is the first predicate, remove the trailing comma as well.
|
||||
span = span.with_hi(next.span().lo());
|
||||
} else if let Some(prev) = prev {
|
||||
// Remove the previous comma as well.
|
||||
span = span.with_lo(prev.hi());
|
||||
}
|
||||
}
|
||||
prev = Some(pred.span());
|
||||
}
|
||||
span
|
||||
};
|
||||
err.assoc2 = Some(errors::AssociatedSuggestion2 {
|
||||
span,
|
||||
args,
|
||||
predicate: removal_span,
|
||||
trait_segment: trait_segment.ident,
|
||||
potential_assoc: potential_assoc.ident,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
|
||||
// Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
|
||||
for bounds in generics.params.iter().map(|p| &p.bounds).chain(
|
||||
generics.where_clause.predicates.iter().filter_map(|pred| match pred {
|
||||
WherePredicate::BoundPredicate(p) => Some(&p.bounds),
|
||||
_ => None,
|
||||
}),
|
||||
) {
|
||||
for bound in bounds {
|
||||
if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
|
||||
if full_path.segments[..full_path.segments.len() - 1]
|
||||
.iter()
|
||||
.map(|segment| segment.ident.name)
|
||||
.zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name))
|
||||
.all(|(a, b)| a == b)
|
||||
&& let Some(potential_assoc) = full_path.segments.iter().last()
|
||||
{
|
||||
suggest(poly, potential_assoc, predicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
|
||||
if let [potential_param, potential_assoc] = &full_path.segments[..] {
|
||||
for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain(
|
||||
generics.where_clause.predicates.iter().filter_map(|pred| match pred {
|
||||
WherePredicate::BoundPredicate(p)
|
||||
if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind
|
||||
&& let [segment] = &path.segments[..] =>
|
||||
{
|
||||
Some((segment.ident, &p.bounds))
|
||||
}
|
||||
_ => None,
|
||||
}),
|
||||
) {
|
||||
if ident == potential_param.ident {
|
||||
for bound in bounds {
|
||||
if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
|
||||
suggest(poly, potential_assoc, predicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2479,7 +2479,8 @@ mod diags {
|
|||
&mut self,
|
||||
span: Span,
|
||||
) -> Option<(DiagnosticBuilder<'tcx>, usize)> {
|
||||
self.diags.buffered_mut_errors.remove(&span)
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
self.diags.buffered_mut_errors.swap_remove(&span)
|
||||
}
|
||||
|
||||
pub fn buffer_mut_error(&mut self, span: Span, t: DiagnosticBuilder<'tcx>, count: usize) {
|
||||
|
|
|
@ -1432,7 +1432,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
};
|
||||
let (sig, map) = tcx.instantiate_bound_regions(sig, |br| {
|
||||
let (unnormalized_sig, map) = tcx.instantiate_bound_regions(sig, |br| {
|
||||
use crate::renumber::RegionCtxt;
|
||||
|
||||
let region_ctxt_fn = || {
|
||||
|
@ -1454,7 +1454,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
region_ctxt_fn,
|
||||
)
|
||||
});
|
||||
debug!(?sig);
|
||||
debug!(?unnormalized_sig);
|
||||
// IMPORTANT: We have to prove well formed for the function signature before
|
||||
// we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc`
|
||||
// get normalized away, causing us to ignore the `'b: 'a` bound used by the function.
|
||||
|
@ -1464,7 +1464,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
//
|
||||
// See #91068 for an example.
|
||||
self.prove_predicates(
|
||||
sig.inputs_and_output.iter().map(|ty| {
|
||||
unnormalized_sig.inputs_and_output.iter().map(|ty| {
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
||||
ty.into(),
|
||||
)))
|
||||
|
@ -1472,7 +1472,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
term_location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
);
|
||||
let sig = self.normalize(sig, term_location);
|
||||
|
||||
let sig = self.normalize(unnormalized_sig, term_location);
|
||||
// HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))`
|
||||
// with built-in `Fn` implementations, since the impl may not be
|
||||
// well-formed itself.
|
||||
if sig != unnormalized_sig {
|
||||
self.prove_predicates(
|
||||
sig.inputs_and_output.iter().map(|ty| {
|
||||
ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::WellFormed(ty.into()),
|
||||
))
|
||||
}),
|
||||
term_location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
);
|
||||
}
|
||||
|
||||
self.check_call_dest(body, term, &sig, *destination, *target, term_location);
|
||||
|
||||
// The ordinary liveness rules will ensure that all
|
||||
|
|
|
@ -58,7 +58,8 @@ impl GatherUsedMutsVisitor<'_, '_, '_> {
|
|||
// be those that were never initialized - we will consider those as being used as
|
||||
// they will either have been removed by unreachable code optimizations; or linted
|
||||
// as unused variables.
|
||||
self.never_initialized_mut_locals.remove(&into.local);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
self.never_initialized_mut_locals.swap_remove(&into.local);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,8 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
|
|||
match attrs.instruction_set {
|
||||
None => {}
|
||||
Some(InstructionSetAttr::ArmA32) => {
|
||||
target_features.remove(&sym::thumb_mode);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
target_features.swap_remove(&sym::thumb_mode);
|
||||
}
|
||||
Some(InstructionSetAttr::ArmT32) => {
|
||||
target_features.insert(sym::thumb_mode);
|
||||
|
|
|
@ -122,7 +122,8 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
|
|||
where
|
||||
K: Borrow<Q>,
|
||||
{
|
||||
FxIndexMap::remove(self, k)
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
FxIndexMap::swap_remove(self, k)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -288,7 +288,7 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
|||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
|
||||
write!(f, "inside closure")
|
||||
} else {
|
||||
// Note: this triggers a `good_path_delayed_bug` state, which means that if we ever
|
||||
// Note: this triggers a `must_produce_diag` state, which means that if we ever
|
||||
// get here we must emit a diagnostic. We should never display a `FrameInfo` unless
|
||||
// we actually want to emit a warning or error to the user.
|
||||
write!(f, "inside `{}`", self.instance)
|
||||
|
@ -304,7 +304,7 @@ impl<'tcx> FrameInfo<'tcx> {
|
|||
errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
|
||||
} else {
|
||||
let instance = format!("{}", self.instance);
|
||||
// Note: this triggers a `good_path_delayed_bug` state, which means that if we ever get
|
||||
// Note: this triggers a `must_produce_diag` state, which means that if we ever get
|
||||
// here we must emit a diagnostic. We should never display a `FrameInfo` unless we
|
||||
// actually want to emit a warning or error to the user.
|
||||
errors::FrameNote { where_: "instance", span, instance, times: 0 }
|
||||
|
|
|
@ -49,7 +49,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
|
|||
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
|
||||
trace!("intern_shallow {:?}", alloc_id);
|
||||
// remove allocation
|
||||
let Some((_kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
|
||||
return Err(());
|
||||
};
|
||||
// Set allocation mutability as appropriate. This is used by LLVM to put things into
|
||||
|
|
|
@ -117,18 +117,14 @@ struct CfgChecker<'a, 'tcx> {
|
|||
impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
|
||||
#[track_caller]
|
||||
fn fail(&self, location: Location, msg: impl AsRef<str>) {
|
||||
let span = self.body.source_info(location).span;
|
||||
// We use `span_delayed_bug` as we might see broken MIR when other errors have already
|
||||
// occurred.
|
||||
self.tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
format!(
|
||||
"broken MIR in {:?} ({}) at {:?}:\n{}",
|
||||
self.body.source.instance,
|
||||
self.when,
|
||||
location,
|
||||
msg.as_ref()
|
||||
),
|
||||
// We might see broken MIR when other errors have already occurred.
|
||||
assert!(
|
||||
self.tcx.dcx().has_errors().is_some(),
|
||||
"broken MIR in {:?} ({}) at {:?}:\n{}",
|
||||
self.body.source.instance,
|
||||
self.when,
|
||||
location,
|
||||
msg.as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,6 @@ impl<'a> FromIterator<&'a str> for SmallCStr {
|
|||
|
||||
impl From<&ffi::CStr> for SmallCStr {
|
||||
fn from(s: &ffi::CStr) -> Self {
|
||||
Self { data: SmallVec::from_slice(s.to_bytes()) }
|
||||
Self { data: SmallVec::from_slice(s.to_bytes_with_nul()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,3 +43,11 @@ fn long() {
|
|||
fn internal_nul() {
|
||||
let _ = SmallCStr::new("abcd\0def");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_cstr() {
|
||||
let c = c"foo";
|
||||
let s: SmallCStr = c.into();
|
||||
assert_eq!(s.len_with_nul(), 4);
|
||||
assert_eq!(s.as_c_str(), c"foo");
|
||||
}
|
||||
|
|
|
@ -376,7 +376,7 @@ impl From<Cow<'static, str>> for DiagnosticMessage {
|
|||
}
|
||||
}
|
||||
|
||||
/// A workaround for good_path_delayed_bug ICEs when formatting types in disabled lints.
|
||||
/// A workaround for must_produce_diag ICEs when formatting types in disabled lints.
|
||||
///
|
||||
/// Delays formatting until `.into(): DiagnosticMessage` is used.
|
||||
pub struct DelayDm<F>(pub F);
|
||||
|
|
|
@ -85,11 +85,7 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
|
|||
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
|
||||
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
||||
match level {
|
||||
Level::Bug
|
||||
| Level::Fatal
|
||||
| Level::Error
|
||||
| Level::DelayedBug
|
||||
| Level::GoodPathDelayedBug => AnnotationType::Error,
|
||||
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => AnnotationType::Error,
|
||||
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
||||
Level::Note | Level::OnceNote => AnnotationType::Note,
|
||||
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
||||
|
|
|
@ -237,8 +237,7 @@ impl Diagnostic {
|
|||
match self.level {
|
||||
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
|
||||
|
||||
Level::GoodPathDelayedBug
|
||||
| Level::ForceWarning(_)
|
||||
Level::ForceWarning(_)
|
||||
| Level::Warning
|
||||
| Level::Note
|
||||
| Level::OnceNote
|
||||
|
|
|
@ -1635,7 +1635,8 @@ impl HumanEmitter {
|
|||
let mut to_add = FxHashMap::default();
|
||||
|
||||
for (depth, style) in depths {
|
||||
if multilines.remove(&depth).is_none() {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
if multilines.swap_remove(&depth).is_none() {
|
||||
to_add.insert(depth, style);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ use std::fmt;
|
|||
use std::hash::Hash;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::DerefMut;
|
||||
use std::panic;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
@ -435,7 +436,6 @@ struct DiagCtxtInner {
|
|||
lint_err_guars: Vec<ErrorGuaranteed>,
|
||||
/// The delayed bugs and their error guarantees.
|
||||
delayed_bugs: Vec<(DelayedDiagnostic, ErrorGuaranteed)>,
|
||||
good_path_delayed_bugs: Vec<DelayedDiagnostic>,
|
||||
|
||||
/// The number of stashed errors. Unlike the other counts, this can go up
|
||||
/// and down, so it doesn't guarantee anything.
|
||||
|
@ -446,13 +446,18 @@ struct DiagCtxtInner {
|
|||
/// The warning count shown to the user at the end.
|
||||
deduplicated_warn_count: usize,
|
||||
|
||||
emitter: Box<DynEmitter>,
|
||||
|
||||
/// Must we produce a diagnostic to justify the use of the expensive
|
||||
/// `trimmed_def_paths` function?
|
||||
must_produce_diag: bool,
|
||||
|
||||
/// Has this diagnostic context printed any diagnostics? (I.e. has
|
||||
/// `self.emitter.emit_diagnostic()` been called?
|
||||
has_printed: bool,
|
||||
|
||||
emitter: Box<DynEmitter>,
|
||||
/// This flag indicates that an expected diagnostic was emitted and suppressed.
|
||||
/// This is used for the `good_path_delayed_bugs` check.
|
||||
/// This is used for the `must_produce_diag` check.
|
||||
suppressed_expected_diag: bool,
|
||||
|
||||
/// This set contains the code of all emitted diagnostics to avoid
|
||||
|
@ -533,11 +538,6 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
|
|||
pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
|
||||
AtomicRef::new(&(default_track_diagnostic as _));
|
||||
|
||||
enum DelayedBugKind {
|
||||
Normal,
|
||||
GoodPath,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct DiagCtxtFlags {
|
||||
/// If false, warning-level lints are suppressed.
|
||||
|
@ -563,11 +563,16 @@ impl Drop for DiagCtxtInner {
|
|||
self.emit_stashed_diagnostics();
|
||||
|
||||
if self.err_guars.is_empty() {
|
||||
self.flush_delayed(DelayedBugKind::Normal)
|
||||
self.flush_delayed()
|
||||
}
|
||||
|
||||
if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
|
||||
self.flush_delayed(DelayedBugKind::GoodPath);
|
||||
if self.must_produce_diag {
|
||||
panic!(
|
||||
"must_produce_diag: trimmed_def_paths called but no diagnostics emitted; \
|
||||
use `DelayDm` for lints or `with_no_trimmed_paths` for debugging"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.check_unstable_expect_diagnostics {
|
||||
|
@ -609,12 +614,12 @@ impl DiagCtxt {
|
|||
err_guars: Vec::new(),
|
||||
lint_err_guars: Vec::new(),
|
||||
delayed_bugs: Vec::new(),
|
||||
good_path_delayed_bugs: Vec::new(),
|
||||
stashed_err_count: 0,
|
||||
deduplicated_err_count: 0,
|
||||
deduplicated_warn_count: 0,
|
||||
has_printed: false,
|
||||
emitter,
|
||||
must_produce_diag: false,
|
||||
has_printed: false,
|
||||
suppressed_expected_diag: false,
|
||||
taught_diagnostics: Default::default(),
|
||||
emitted_diagnostic_codes: Default::default(),
|
||||
|
@ -662,21 +667,51 @@ impl DiagCtxt {
|
|||
/// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as
|
||||
/// the overall count of emitted error diagnostics.
|
||||
pub fn reset_err_count(&self) {
|
||||
// Use destructuring so that if a field gets added to `DiagCtxtInner`, it's impossible to
|
||||
// fail to update this method as well.
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.stashed_err_count = 0;
|
||||
inner.deduplicated_err_count = 0;
|
||||
inner.deduplicated_warn_count = 0;
|
||||
inner.has_printed = false;
|
||||
let DiagCtxtInner {
|
||||
flags: _,
|
||||
err_guars,
|
||||
lint_err_guars,
|
||||
delayed_bugs,
|
||||
stashed_err_count,
|
||||
deduplicated_err_count,
|
||||
deduplicated_warn_count,
|
||||
emitter: _,
|
||||
must_produce_diag,
|
||||
has_printed,
|
||||
suppressed_expected_diag,
|
||||
taught_diagnostics,
|
||||
emitted_diagnostic_codes,
|
||||
emitted_diagnostics,
|
||||
stashed_diagnostics,
|
||||
future_breakage_diagnostics,
|
||||
check_unstable_expect_diagnostics,
|
||||
unstable_expect_diagnostics,
|
||||
fulfilled_expectations,
|
||||
ice_file: _,
|
||||
} = inner.deref_mut();
|
||||
|
||||
// actually free the underlying memory (which `clear` would not do)
|
||||
inner.err_guars = Default::default();
|
||||
inner.lint_err_guars = Default::default();
|
||||
inner.delayed_bugs = Default::default();
|
||||
inner.good_path_delayed_bugs = Default::default();
|
||||
inner.taught_diagnostics = Default::default();
|
||||
inner.emitted_diagnostic_codes = Default::default();
|
||||
inner.emitted_diagnostics = Default::default();
|
||||
inner.stashed_diagnostics = Default::default();
|
||||
// For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the
|
||||
// underlying memory (which `clear` would not do).
|
||||
*err_guars = Default::default();
|
||||
*lint_err_guars = Default::default();
|
||||
*delayed_bugs = Default::default();
|
||||
*stashed_err_count = 0;
|
||||
*deduplicated_err_count = 0;
|
||||
*deduplicated_warn_count = 0;
|
||||
*must_produce_diag = false;
|
||||
*has_printed = false;
|
||||
*suppressed_expected_diag = false;
|
||||
*taught_diagnostics = Default::default();
|
||||
*emitted_diagnostic_codes = Default::default();
|
||||
*emitted_diagnostics = Default::default();
|
||||
*stashed_diagnostics = Default::default();
|
||||
*future_breakage_diagnostics = Default::default();
|
||||
*check_unstable_expect_diagnostics = false;
|
||||
*unstable_expect_diagnostics = Default::default();
|
||||
*fulfilled_expectations = Default::default();
|
||||
}
|
||||
|
||||
/// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
|
||||
|
@ -702,7 +737,8 @@ impl DiagCtxt {
|
|||
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let key = (span.with_parent(None), key);
|
||||
let diag = inner.stashed_diagnostics.remove(&key)?;
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let diag = inner.stashed_diagnostics.swap_remove(&key)?;
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
inner.stashed_err_count -= 1;
|
||||
|
@ -775,11 +811,12 @@ impl DiagCtxt {
|
|||
match (errors.len(), warnings.len()) {
|
||||
(0, 0) => return,
|
||||
(0, _) => {
|
||||
// Use `inner.emitter` directly, otherwise the warning might not be emitted, e.g.
|
||||
// with a configuration like `--cap-lints allow --force-warn bare_trait_objects`.
|
||||
inner
|
||||
.emitter
|
||||
.emit_diagnostic(Diagnostic::new(Warning, DiagnosticMessage::Str(warnings)));
|
||||
// Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
|
||||
// configuration like `--cap-lints allow --force-warn bare_trait_objects`.
|
||||
inner.emit_diagnostic(Diagnostic::new(
|
||||
ForceWarning(None),
|
||||
DiagnosticMessage::Str(warnings),
|
||||
));
|
||||
}
|
||||
(_, 0) => {
|
||||
inner.emit_diagnostic(Diagnostic::new(Error, errors));
|
||||
|
@ -807,20 +844,23 @@ impl DiagCtxt {
|
|||
error_codes.sort();
|
||||
if error_codes.len() > 1 {
|
||||
let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
|
||||
inner.failure_note(format!(
|
||||
let msg1 = format!(
|
||||
"Some errors have detailed explanations: {}{}",
|
||||
error_codes[..limit].join(", "),
|
||||
if error_codes.len() > 9 { "..." } else { "." }
|
||||
));
|
||||
inner.failure_note(format!(
|
||||
);
|
||||
let msg2 = format!(
|
||||
"For more information about an error, try `rustc --explain {}`.",
|
||||
&error_codes[0]
|
||||
));
|
||||
);
|
||||
inner.emit_diagnostic(Diagnostic::new(FailureNote, msg1));
|
||||
inner.emit_diagnostic(Diagnostic::new(FailureNote, msg2));
|
||||
} else {
|
||||
inner.failure_note(format!(
|
||||
let msg = format!(
|
||||
"For more information about this error, try `rustc --explain {}`.",
|
||||
&error_codes[0]
|
||||
));
|
||||
);
|
||||
inner.emit_diagnostic(Diagnostic::new(FailureNote, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -843,10 +883,6 @@ impl DiagCtxt {
|
|||
self.inner.borrow_mut().taught_diagnostics.insert(code)
|
||||
}
|
||||
|
||||
pub fn force_print_diagnostic(&self, db: Diagnostic) {
|
||||
self.inner.borrow_mut().emitter.emit_diagnostic(db);
|
||||
}
|
||||
|
||||
pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow_mut().emit_diagnostic(diagnostic)
|
||||
}
|
||||
|
@ -934,7 +970,13 @@ impl DiagCtxt {
|
|||
}
|
||||
|
||||
pub fn flush_delayed(&self) {
|
||||
self.inner.borrow_mut().flush_delayed(DelayedBugKind::Normal);
|
||||
self.inner.borrow_mut().flush_delayed();
|
||||
}
|
||||
|
||||
/// Used when trimmed_def_paths is called and we must produce a diagnostic
|
||||
/// to justify its cost.
|
||||
pub fn set_must_produce_diag(&self) {
|
||||
self.inner.borrow_mut().must_produce_diag = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1108,13 +1150,6 @@ impl DiagCtxt {
|
|||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit()
|
||||
}
|
||||
|
||||
/// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`.
|
||||
// No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing.
|
||||
#[track_caller]
|
||||
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
||||
DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit()
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
|
||||
|
@ -1175,7 +1210,7 @@ impl DiagCtxt {
|
|||
span: impl Into<MultiSpan>,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, ()> {
|
||||
DiagnosticBuilder::new(self, Note, msg).with_span(span)
|
||||
self.struct_note(msg).with_span(span)
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
|
@ -1203,6 +1238,15 @@ impl DiagCtxt {
|
|||
DiagnosticBuilder::new(self, Help, msg)
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn struct_failure_note(
|
||||
&self,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> DiagnosticBuilder<'_, ()> {
|
||||
DiagnosticBuilder::new(self, FailureNote, msg)
|
||||
}
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
|
||||
|
@ -1266,42 +1310,39 @@ impl DiagCtxtInner {
|
|||
if diagnostic.has_future_breakage() {
|
||||
// Future breakages aren't emitted if they're Level::Allow,
|
||||
// but they still need to be constructed and stashed below,
|
||||
// so they'll trigger the good-path bug check.
|
||||
// so they'll trigger the must_produce_diag check.
|
||||
self.suppressed_expected_diag = true;
|
||||
self.future_breakage_diagnostics.push(diagnostic.clone());
|
||||
}
|
||||
|
||||
if matches!(diagnostic.level, DelayedBug | GoodPathDelayedBug)
|
||||
&& self.flags.eagerly_emit_delayed_bugs
|
||||
{
|
||||
// Note that because this comes before the `match` below,
|
||||
// `-Zeagerly-emit-delayed-bugs` continues to work even after we've
|
||||
// issued an error and stopped recording new delayed bugs.
|
||||
if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs {
|
||||
diagnostic.level = Error;
|
||||
}
|
||||
|
||||
match diagnostic.level {
|
||||
// This must come after the possible promotion of `DelayedBug`/`GoodPathDelayedBug` to
|
||||
// This must come after the possible promotion of `DelayedBug` to
|
||||
// `Error` above.
|
||||
Fatal | Error if self.treat_next_err_as_bug() => {
|
||||
diagnostic.level = Bug;
|
||||
}
|
||||
DelayedBug => {
|
||||
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
||||
// once *any* errors were emitted (and truncate `delayed_bugs`
|
||||
// when an error is first emitted, also), but maybe there's a case
|
||||
// in which that's not sound? otherwise this is really inefficient.
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for delayed bugs originates.
|
||||
#[allow(deprecated)]
|
||||
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
|
||||
self.delayed_bugs
|
||||
.push((DelayedDiagnostic::with_backtrace(diagnostic, backtrace), guar));
|
||||
return Some(guar);
|
||||
}
|
||||
GoodPathDelayedBug => {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
self.good_path_delayed_bugs
|
||||
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
return None;
|
||||
// If we have already emitted at least one error, we don't need
|
||||
// to record the delayed bug, because it'll never be used.
|
||||
return if let Some(guar) = self.has_errors_or_lint_errors() {
|
||||
Some(guar)
|
||||
} else {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for delayed bugs originates.
|
||||
#[allow(deprecated)]
|
||||
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
|
||||
self.delayed_bugs
|
||||
.push((DelayedDiagnostic::with_backtrace(diagnostic, backtrace), guar));
|
||||
Some(guar)
|
||||
};
|
||||
}
|
||||
Warning if !self.flags.can_emit_warnings => {
|
||||
if diagnostic.has_future_breakage() {
|
||||
|
@ -1367,6 +1408,16 @@ impl DiagCtxtInner {
|
|||
}
|
||||
|
||||
if is_error {
|
||||
// If we have any delayed bugs recorded, we can discard them
|
||||
// because they won't be used. (This should only occur if there
|
||||
// have been no errors previously emitted, because we don't add
|
||||
// new delayed bugs once the first error is emitted.)
|
||||
if !self.delayed_bugs.is_empty() {
|
||||
assert_eq!(self.lint_err_guars.len() + self.err_guars.len(), 0);
|
||||
self.delayed_bugs.clear();
|
||||
self.delayed_bugs.shrink_to_fit();
|
||||
}
|
||||
|
||||
// This `unchecked_error_guaranteed` is valid. It is where the
|
||||
// `ErrorGuaranteed` for errors and lint errors originates.
|
||||
#[allow(deprecated)]
|
||||
|
@ -1410,27 +1461,14 @@ impl DiagCtxtInner {
|
|||
.or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
|
||||
}
|
||||
|
||||
fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) {
|
||||
self.emit_diagnostic(Diagnostic::new(FailureNote, msg));
|
||||
}
|
||||
|
||||
fn flush_delayed(&mut self, kind: DelayedBugKind) {
|
||||
let (bugs, note1) = match kind {
|
||||
DelayedBugKind::Normal => (
|
||||
std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(),
|
||||
"no errors encountered even though delayed bugs were created",
|
||||
),
|
||||
DelayedBugKind::GoodPath => (
|
||||
std::mem::take(&mut self.good_path_delayed_bugs),
|
||||
"no warnings or errors encountered even though good path delayed bugs were created",
|
||||
),
|
||||
};
|
||||
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
||||
|
||||
if bugs.is_empty() {
|
||||
fn flush_delayed(&mut self) {
|
||||
if self.delayed_bugs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let bugs: Vec<_> =
|
||||
std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect();
|
||||
|
||||
// If backtraces are enabled, also print the query stack
|
||||
let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
|
||||
for (i, bug) in bugs.into_iter().enumerate() {
|
||||
|
@ -1454,6 +1492,8 @@ impl DiagCtxtInner {
|
|||
// frame them better (e.g. separate warnings from them). Also,
|
||||
// make it a note so it doesn't count as an error, because that
|
||||
// could trigger `-Ztreat-err-as-bug`, which we don't want.
|
||||
let note1 = "no errors encountered even though delayed bugs were created";
|
||||
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
||||
self.emit_diagnostic(Diagnostic::new(Note, note1));
|
||||
self.emit_diagnostic(Diagnostic::new(Note, note2));
|
||||
}
|
||||
|
@ -1462,7 +1502,7 @@ impl DiagCtxtInner {
|
|||
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
|
||||
|
||||
// "Undelay" the delayed bugs (into plain `Bug`s).
|
||||
if !matches!(bug.level, DelayedBug | GoodPathDelayedBug) {
|
||||
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 {
|
||||
|
@ -1534,7 +1574,6 @@ impl DelayedDiagnostic {
|
|||
/// Fatal yes FatalAbort/FatalError(*) yes - -
|
||||
/// Error yes ErrorGuaranteed yes - yes
|
||||
/// DelayedBug yes ErrorGuaranteed yes - -
|
||||
/// GoodPathDelayedBug - () yes - -
|
||||
/// ForceWarning - () yes - lint-only
|
||||
/// Warning - () yes yes yes
|
||||
/// Note - () rare yes -
|
||||
|
@ -1567,20 +1606,6 @@ pub enum Level {
|
|||
/// that should only be reached when compiling erroneous code.
|
||||
DelayedBug,
|
||||
|
||||
/// Like `DelayedBug`, but weaker: lets you register an error without emitting it. If
|
||||
/// compilation ends without any other diagnostics being emitted (and without an expected lint
|
||||
/// being suppressed), this will be emitted as a bug. Otherwise, it will be silently dropped.
|
||||
/// I.e. "expect other diagnostics are emitted (or suppressed)" semantics. Useful on code paths
|
||||
/// that should only be reached when emitting diagnostics, e.g. for expensive one-time
|
||||
/// diagnostic formatting operations.
|
||||
///
|
||||
/// FIXME(nnethercote) good path delayed bugs are semantically strange: if printed they produce
|
||||
/// an ICE, but they don't satisfy `is_error` and they don't guarantee an error is emitted.
|
||||
/// Plus there's the extra complication with expected (suppressed) lints. They have limited
|
||||
/// use, and are used in very few places, and "good path" isn't a good name. It would be good
|
||||
/// to remove them.
|
||||
GoodPathDelayedBug,
|
||||
|
||||
/// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
|
||||
/// from finishing.
|
||||
///
|
||||
|
@ -1625,7 +1650,7 @@ impl Level {
|
|||
fn color(self) -> ColorSpec {
|
||||
let mut spec = ColorSpec::new();
|
||||
match self {
|
||||
Bug | Fatal | Error | DelayedBug | GoodPathDelayedBug => {
|
||||
Bug | Fatal | Error | DelayedBug => {
|
||||
spec.set_fg(Some(Color::Red)).set_intense(true);
|
||||
}
|
||||
ForceWarning(_) | Warning => {
|
||||
|
@ -1645,7 +1670,7 @@ impl Level {
|
|||
|
||||
pub fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
Bug | DelayedBug | GoodPathDelayedBug => "error: internal compiler error",
|
||||
Bug | DelayedBug => "error: internal compiler error",
|
||||
Fatal | Error => "error",
|
||||
ForceWarning(_) | Warning => "warning",
|
||||
Note | OnceNote => "note",
|
||||
|
@ -1670,8 +1695,8 @@ impl Level {
|
|||
// subdiagnostic message?
|
||||
fn can_be_top_or_sub(&self) -> (bool, bool) {
|
||||
match self {
|
||||
Bug | DelayedBug | Fatal | Error | GoodPathDelayedBug | ForceWarning(_)
|
||||
| FailureNote | Allow | Expect(_) => (true, false),
|
||||
Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow
|
||||
| Expect(_) => (true, false),
|
||||
|
||||
Warning | Note | Help => (true, true),
|
||||
|
||||
|
|
|
@ -34,10 +34,10 @@ pub(super) fn failed_to_match_macro<'cx>(
|
|||
if try_success_result.is_ok() {
|
||||
// Nonterminal parser recovery might turn failed matches into successful ones,
|
||||
// but for that it must have emitted an error already
|
||||
tracker
|
||||
.cx
|
||||
.dcx()
|
||||
.span_delayed_bug(sp, "Macro matching returned a success on the second try");
|
||||
assert!(
|
||||
tracker.cx.dcx().has_errors().is_some(),
|
||||
"Macro matching returned a success on the second try"
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(result) = tracker.result {
|
||||
|
|
|
@ -758,8 +758,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
// since we should have emitten an error for them earlier, and they will
|
||||
// not be well-formed!
|
||||
if polarity == ty::ImplPolarity::Negative {
|
||||
self.tcx().dcx().span_delayed_bug(
|
||||
binding.span,
|
||||
assert!(
|
||||
self.tcx().dcx().has_errors().is_some(),
|
||||
"negative trait bounds should not have bindings",
|
||||
);
|
||||
continue;
|
||||
|
|
|
@ -218,7 +218,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
for def_ids in associated_types.values_mut() {
|
||||
for (projection_bound, span) in &projection_bounds {
|
||||
let def_id = projection_bound.projection_def_id();
|
||||
def_ids.remove(&def_id);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
def_ids.swap_remove(&def_id);
|
||||
if tcx.generics_require_sized_self(def_id) {
|
||||
tcx.emit_node_span_lint(
|
||||
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
||||
|
|
|
@ -1865,13 +1865,13 @@ fn check_variances_for_type_defn<'tcx>(
|
|||
let hir_param = &hir_generics.params[index];
|
||||
|
||||
if ty_param.def_id != hir_param.def_id.into() {
|
||||
// valid programs always have lifetimes before types in the generic parameter list
|
||||
// Valid programs always have lifetimes before types in the generic parameter list.
|
||||
// ty_generics are normalized to be in this required order, and variances are built
|
||||
// from ty generics, not from hir generics. but we need hir generics to get
|
||||
// a span out
|
||||
// a span out.
|
||||
//
|
||||
// if they aren't in the same order, then the user has written invalid code, and already
|
||||
// got an error about it (or I'm wrong about this)
|
||||
// If they aren't in the same order, then the user has written invalid code, and already
|
||||
// got an error about it (or I'm wrong about this).
|
||||
tcx.dcx().span_delayed_bug(
|
||||
hir_param.span,
|
||||
"hir generics and ty generics in different order",
|
||||
|
|
|
@ -15,7 +15,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, TyCtxtInferExt};
|
|||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
|
||||
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::traits::misc::{
|
||||
type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy,
|
||||
|
@ -25,13 +25,14 @@ use rustc_trait_selection::traits::ObligationCtxt;
|
|||
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn check_trait(
|
||||
tcx: TyCtxt<'_>,
|
||||
pub fn check_trait<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
impl_header: ty::ImplTraitHeader<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let lang_items = tcx.lang_items();
|
||||
let checker = Checker { tcx, trait_def_id, impl_def_id };
|
||||
let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
|
||||
let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop);
|
||||
res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy));
|
||||
res = res.and(
|
||||
|
@ -50,24 +51,25 @@ struct Checker<'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
impl_header: ty::ImplTraitHeader<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Checker<'tcx> {
|
||||
fn check(
|
||||
&self,
|
||||
trait_def_id: Option<DefId>,
|
||||
f: impl FnOnce(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>,
|
||||
f: impl FnOnce(&Self) -> Result<(), ErrorGuaranteed>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if Some(self.trait_def_id) == trait_def_id { f(self.tcx, self.impl_def_id) } else { Ok(()) }
|
||||
if Some(self.trait_def_id) == trait_def_id { f(self) } else { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_implementation_of_drop(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_did: LocalDefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
// Destructors only work on local ADT types.
|
||||
match tcx.type_of(impl_did).instantiate_identity().kind() {
|
||||
match header.trait_ref.self_ty().kind() {
|
||||
ty::Adt(def, _) if def.did().is_local() => return Ok(()),
|
||||
ty::Error(_) => return Ok(()),
|
||||
_ => {}
|
||||
|
@ -78,13 +80,13 @@ fn visit_implementation_of_drop(
|
|||
Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span }))
|
||||
}
|
||||
|
||||
fn visit_implementation_of_copy(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_did: LocalDefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let impl_header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
|
||||
|
||||
let self_type = tcx.type_of(impl_did).instantiate_identity();
|
||||
let self_type = impl_header.trait_ref.self_ty();
|
||||
debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
|
||||
|
||||
let param_env = tcx.param_env(impl_did);
|
||||
|
@ -92,56 +94,58 @@ fn visit_implementation_of_copy(
|
|||
|
||||
debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
|
||||
|
||||
if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) {
|
||||
if let ty::ImplPolarity::Negative = impl_header.polarity {
|
||||
return Ok(());
|
||||
}
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
|
||||
let cause = traits::ObligationCause::misc(span, impl_did);
|
||||
let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
|
||||
match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(CopyImplementationError::InfringingFields(fields)) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span))
|
||||
}
|
||||
Err(CopyImplementationError::NotAnAdt) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }))
|
||||
}
|
||||
Err(CopyImplementationError::HasDestructor) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_implementation_of_const_param_ty(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_did: LocalDefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let self_type = tcx.type_of(impl_did).instantiate_identity();
|
||||
fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
let self_type = header.trait_ref.self_ty();
|
||||
assert!(!self_type.has_escaping_bound_vars());
|
||||
|
||||
let param_env = tcx.param_env(impl_did);
|
||||
|
||||
if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) {
|
||||
if let ty::ImplPolarity::Negative = header.polarity {
|
||||
return Ok(());
|
||||
}
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
|
||||
let cause = traits::ObligationCause::misc(span, impl_did);
|
||||
let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
|
||||
match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(ConstParamTyImplementationError::InfrigingFields(fields)) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span))
|
||||
}
|
||||
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_implementation_of_coerce_unsized(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_did: LocalDefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let impl_did = checker.impl_def_id;
|
||||
debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
|
||||
|
||||
// Just compute this for the side-effects, in particular reporting
|
||||
|
@ -151,20 +155,20 @@ fn visit_implementation_of_coerce_unsized(
|
|||
tcx.at(span).ensure().coerce_unsized_info(impl_did)
|
||||
}
|
||||
|
||||
fn visit_implementation_of_dispatch_from_dyn(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_did: LocalDefId,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
let trait_ref = header.trait_ref;
|
||||
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
|
||||
|
||||
let span = tcx.def_span(impl_did);
|
||||
|
||||
let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
|
||||
|
||||
let source = tcx.type_of(impl_did).instantiate_identity();
|
||||
let source = trait_ref.self_ty();
|
||||
assert!(!source.has_escaping_bound_vars());
|
||||
let target = {
|
||||
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity();
|
||||
assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
|
||||
|
||||
trait_ref.args.type_at(1)
|
||||
|
|
|
@ -23,6 +23,7 @@ fn check_impl(
|
|||
tcx: TyCtxt<'_>,
|
||||
impl_def_id: LocalDefId,
|
||||
trait_ref: ty::TraitRef<'_>,
|
||||
trait_def: &ty::TraitDef,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
debug!(
|
||||
"(checking implementation) adding impl for trait '{:?}', item '{}'",
|
||||
|
@ -36,19 +37,20 @@ fn check_impl(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id)
|
||||
.and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id))
|
||||
enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id, trait_def)
|
||||
.and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id, trait_def))
|
||||
}
|
||||
|
||||
fn enforce_trait_manually_implementable(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_def_id: LocalDefId,
|
||||
trait_def_id: DefId,
|
||||
trait_def: &ty::TraitDef,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let impl_header_span = tcx.def_span(impl_def_id);
|
||||
|
||||
// Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]`
|
||||
if tcx.trait_def(trait_def_id).deny_explicit_impl {
|
||||
if trait_def.deny_explicit_impl {
|
||||
let trait_name = tcx.item_name(trait_def_id);
|
||||
let mut err = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
|
@ -67,8 +69,7 @@ fn enforce_trait_manually_implementable(
|
|||
return Err(err.emit());
|
||||
}
|
||||
|
||||
if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable =
|
||||
tcx.trait_def(trait_def_id).specialization_kind
|
||||
if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = trait_def.specialization_kind
|
||||
{
|
||||
if !tcx.features().specialization
|
||||
&& !tcx.features().min_specialization
|
||||
|
@ -87,8 +88,9 @@ fn enforce_empty_impls_for_marker_traits(
|
|||
tcx: TyCtxt<'_>,
|
||||
impl_def_id: LocalDefId,
|
||||
trait_def_id: DefId,
|
||||
trait_def: &ty::TraitDef,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if !tcx.trait_def(trait_def_id).is_marker {
|
||||
if !trait_def.is_marker {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -132,14 +134,15 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed>
|
|||
let mut res = tcx.ensure().specialization_graph_of(def_id);
|
||||
|
||||
for &impl_def_id in impls {
|
||||
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
|
||||
let trait_header = tcx.impl_trait_header(impl_def_id).unwrap().instantiate_identity();
|
||||
let trait_def = tcx.trait_def(trait_header.trait_ref.def_id);
|
||||
|
||||
res = res.and(check_impl(tcx, impl_def_id, trait_ref));
|
||||
res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref));
|
||||
res = res.and(check_impl(tcx, impl_def_id, trait_header.trait_ref, trait_def));
|
||||
res = res.and(check_object_overlap(tcx, impl_def_id, trait_header.trait_ref));
|
||||
|
||||
res = res.and(unsafety::check_item(tcx, impl_def_id, trait_ref));
|
||||
res = res.and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def));
|
||||
res = res.and(tcx.ensure().orphan_check_impl(impl_def_id));
|
||||
res = res.and(builtin::check_trait(tcx, def_id, impl_def_id));
|
||||
res = res.and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header));
|
||||
}
|
||||
|
||||
res
|
||||
|
|
|
@ -2,23 +2,23 @@
|
|||
//! crate or pertains to a type defined in this crate.
|
||||
|
||||
use rustc_errors::{codes::*, struct_span_code_err};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Unsafety;
|
||||
use rustc_middle::ty::{TraitRef, TyCtxt};
|
||||
use rustc_middle::ty::{ImplPolarity::*, ImplTraitHeader, TraitDef, TyCtxt};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
|
||||
pub(super) fn check_item(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
trait_ref: TraitRef<'_>,
|
||||
trait_header: ImplTraitHeader<'_>,
|
||||
trait_def: &TraitDef,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let item = tcx.hir().expect_item(def_id);
|
||||
let impl_ = item.expect_impl();
|
||||
let trait_def = tcx.trait_def(trait_ref.def_id);
|
||||
let unsafe_attr = impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
|
||||
match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
|
||||
let trait_ref = trait_header.trait_ref;
|
||||
let unsafe_attr =
|
||||
tcx.generics_of(def_id).params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
|
||||
match (trait_def.unsafety, unsafe_attr, trait_header.unsafety, trait_header.polarity) {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, Positive | Reservation) => {
|
||||
let span = tcx.def_span(def_id);
|
||||
return Err(struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
tcx.def_span(def_id),
|
||||
|
@ -27,7 +27,7 @@ pub(super) fn check_item(
|
|||
trait_ref.print_trait_sugared()
|
||||
)
|
||||
.with_span_suggestion_verbose(
|
||||
item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)),
|
||||
span.with_hi(span.lo() + rustc_span::BytePos(7)),
|
||||
"remove `unsafe` from this trait implementation",
|
||||
"",
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
|
@ -35,10 +35,11 @@ pub(super) fn check_item(
|
|||
.emit());
|
||||
}
|
||||
|
||||
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
(Unsafety::Unsafe, _, Unsafety::Normal, Positive | Reservation) => {
|
||||
let span = tcx.def_span(def_id);
|
||||
return Err(struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
tcx.def_span(def_id),
|
||||
span,
|
||||
E0200,
|
||||
"the trait `{}` requires an `unsafe impl` declaration",
|
||||
trait_ref.print_trait_sugared()
|
||||
|
@ -50,7 +51,7 @@ pub(super) fn check_item(
|
|||
trait_ref.print_trait_sugared()
|
||||
))
|
||||
.with_span_suggestion_verbose(
|
||||
item.span.shrink_to_lo(),
|
||||
span.shrink_to_lo(),
|
||||
"add `unsafe` to this trait implementation",
|
||||
"unsafe ",
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
|
@ -58,10 +59,11 @@ pub(super) fn check_item(
|
|||
.emit());
|
||||
}
|
||||
|
||||
(Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
(Unsafety::Normal, Some(attr_name), Unsafety::Normal, Positive | Reservation) => {
|
||||
let span = tcx.def_span(def_id);
|
||||
return Err(struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
tcx.def_span(def_id),
|
||||
span,
|
||||
E0569,
|
||||
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
|
||||
attr_name
|
||||
|
@ -73,7 +75,7 @@ pub(super) fn check_item(
|
|||
trait_ref.print_trait_sugared()
|
||||
))
|
||||
.with_span_suggestion_verbose(
|
||||
item.span.shrink_to_lo(),
|
||||
span.shrink_to_lo(),
|
||||
"add `unsafe` to this trait implementation",
|
||||
"unsafe ",
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
|
@ -81,14 +83,14 @@ pub(super) fn check_item(
|
|||
.emit());
|
||||
}
|
||||
|
||||
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
|
||||
(_, _, Unsafety::Unsafe, Negative) => {
|
||||
// Reported in AST validation
|
||||
tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl");
|
||||
assert!(tcx.dcx().has_errors().is_some(), "unsafe negative impl");
|
||||
Ok(())
|
||||
}
|
||||
(_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
|
||||
| (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
|
||||
| (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
|
||||
(_, _, Unsafety::Normal, Negative)
|
||||
| (Unsafety::Unsafe, _, Unsafety::Unsafe, Positive | Reservation)
|
||||
| (Unsafety::Normal, Some(_), Unsafety::Unsafe, Positive | Reservation)
|
||||
| (Unsafety::Normal, None, Unsafety::Normal, _) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1539,6 +1539,7 @@ fn impl_trait_header(
|
|||
};
|
||||
ty::EarlyBinder::bind(ty::ImplTraitHeader {
|
||||
trait_ref,
|
||||
unsafety: impl_.unsafety,
|
||||
polarity: polarity_of_impl(tcx, def_id, impl_, item.span)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1873,7 +1873,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
lifetime_ref: &'tcx hir::Lifetime,
|
||||
bad_def: ResolvedArg,
|
||||
) {
|
||||
let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id);
|
||||
assert_eq!(old_value, Some(bad_def));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,8 +187,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
|
|||
}
|
||||
|
||||
tcx.sess.time("wf_checking", || {
|
||||
tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
|
||||
})?;
|
||||
tcx.hir().par_for_each_module(|module| {
|
||||
let _ = tcx.ensure().check_mod_type_wf(module);
|
||||
})
|
||||
});
|
||||
|
||||
if tcx.features().rustc_attrs {
|
||||
collect::test_opaque_hidden_types(tcx)?;
|
||||
|
|
|
@ -139,10 +139,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
| ty::Never
|
||||
| ty::Dynamic(_, _, ty::DynStar)
|
||||
| ty::Error(_) => {
|
||||
let reported = self
|
||||
let guar = self
|
||||
.dcx()
|
||||
.span_delayed_bug(span, format!("`{t:?}` should be sized but is not?"));
|
||||
return Err(reported);
|
||||
return Err(guar);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -98,8 +98,11 @@ struct CollectRetsVisitor<'tcx> {
|
|||
|
||||
impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if let hir::ExprKind::Ret(_) = expr.kind {
|
||||
self.ret_exprs.push(expr);
|
||||
match expr.kind {
|
||||
hir::ExprKind::Ret(_) => self.ret_exprs.push(expr),
|
||||
// `return` in closures does not return from the outer function
|
||||
hir::ExprKind::Closure(_) => return,
|
||||
_ => {}
|
||||
}
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
@ -1845,13 +1848,31 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
|||
}
|
||||
|
||||
let parent_id = fcx.tcx.hir().get_parent_item(id);
|
||||
let parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id);
|
||||
let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id);
|
||||
// When suggesting return, we need to account for closures and async blocks, not just items.
|
||||
for (_, node) in fcx.tcx.hir().parent_iter(id) {
|
||||
match node {
|
||||
hir::Node::Expr(&hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { .. }),
|
||||
..
|
||||
}) => {
|
||||
parent_item = node;
|
||||
break;
|
||||
}
|
||||
hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(expr), Some(_), Some((fn_id, fn_decl, _, _))) =
|
||||
(expression, blk_id, fcx.get_node_fn_decl(parent_item))
|
||||
{
|
||||
if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) {
|
||||
fcx.suggest_missing_break_or_return_expr(
|
||||
&mut err, expr, fn_decl, expected, found, id, fn_id,
|
||||
&mut err,
|
||||
expr,
|
||||
fn_decl,
|
||||
expected,
|
||||
found,
|
||||
id,
|
||||
parent_id.into(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -963,14 +963,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
owner_id,
|
||||
..
|
||||
}) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)),
|
||||
Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. })
|
||||
if let Node::Item(&hir::Item {
|
||||
ident,
|
||||
kind: hir::ItemKind::Fn(ref sig, ..),
|
||||
owner_id,
|
||||
..
|
||||
}) = self.tcx.parent_hir_node(hir_id) =>
|
||||
{
|
||||
Node::Expr(&hir::Expr {
|
||||
hir_id,
|
||||
kind:
|
||||
hir::ExprKind::Closure(hir::Closure {
|
||||
kind: hir::ClosureKind::Coroutine(..), ..
|
||||
}),
|
||||
..
|
||||
}) => {
|
||||
let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) {
|
||||
Node::Item(&hir::Item {
|
||||
ident,
|
||||
kind: hir::ItemKind::Fn(ref sig, ..),
|
||||
owner_id,
|
||||
..
|
||||
}) => (ident, sig, owner_id),
|
||||
Node::TraitItem(&hir::TraitItem {
|
||||
ident,
|
||||
kind: hir::TraitItemKind::Fn(ref sig, ..),
|
||||
owner_id,
|
||||
..
|
||||
}) => (ident, sig, owner_id),
|
||||
Node::ImplItem(&hir::ImplItem {
|
||||
ident,
|
||||
kind: hir::ImplItemKind::Fn(ref sig, ..),
|
||||
owner_id,
|
||||
..
|
||||
}) => (ident, sig, owner_id),
|
||||
_ => return None,
|
||||
};
|
||||
Some((
|
||||
hir::HirId::make_owner(owner_id.def_id),
|
||||
&sig.decl,
|
||||
|
@ -1558,7 +1579,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let ctxt = {
|
||||
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
|
||||
debug_assert!(enclosing_breakables.stack.len() == index + 1);
|
||||
enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
enclosing_breakables.by_id.swap_remove(&id).expect("missing breakable context");
|
||||
enclosing_breakables.stack.pop().expect("missing breakable context")
|
||||
};
|
||||
(ctxt, result)
|
||||
|
|
|
@ -1726,7 +1726,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
|
||||
fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
|
||||
pub(crate) fn get_parent_fn_decl(
|
||||
&self,
|
||||
blk_id: hir::HirId,
|
||||
) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
|
||||
let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id);
|
||||
self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident))
|
||||
}
|
||||
|
|
|
@ -827,6 +827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
hir::FnRetTy::Return(hir_ty) => {
|
||||
if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind
|
||||
// FIXME: account for RPITIT.
|
||||
&& let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::OpaqueTy(op_ty), ..
|
||||
}) = self.tcx.hir_node(item_id.hir_id())
|
||||
|
@ -1038,33 +1039,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let hir::FnRetTy::Return(ty) = fn_decl.output {
|
||||
let ty = self.astconv().ast_ty_to_ty(ty);
|
||||
let bound_vars = self.tcx.late_bound_vars(fn_id);
|
||||
let ty = self
|
||||
.tcx
|
||||
.instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
|
||||
let ty = match self.tcx.asyncness(fn_id.owner) {
|
||||
ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
|
||||
span_bug!(fn_decl.output.span(), "failed to get output type of async function")
|
||||
}),
|
||||
ty::Asyncness::No => ty,
|
||||
};
|
||||
let ty = self.normalize(expr.span, ty);
|
||||
if self.can_coerce(found, ty) {
|
||||
if let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner()
|
||||
&& let Some(span) = expr.span.find_ancestor_inside(*owner_node.span())
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"you might have meant to return this value",
|
||||
vec![
|
||||
(span.shrink_to_lo(), "return ".to_string()),
|
||||
(span.shrink_to_hi(), ";".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
let scope = self
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(id)
|
||||
.filter(|(_, node)| {
|
||||
matches!(
|
||||
node,
|
||||
Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
|
||||
| Node::Item(_)
|
||||
| Node::TraitItem(_)
|
||||
| Node::ImplItem(_)
|
||||
)
|
||||
})
|
||||
.next();
|
||||
let in_closure =
|
||||
matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
|
||||
|
||||
let can_return = match fn_decl.output {
|
||||
hir::FnRetTy::Return(ty) => {
|
||||
let ty = self.astconv().ast_ty_to_ty(ty);
|
||||
let bound_vars = self.tcx.late_bound_vars(fn_id);
|
||||
let ty = self
|
||||
.tcx
|
||||
.instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
|
||||
let ty = match self.tcx.asyncness(fn_id.owner) {
|
||||
ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
|
||||
span_bug!(
|
||||
fn_decl.output.span(),
|
||||
"failed to get output type of async function"
|
||||
)
|
||||
}),
|
||||
ty::Asyncness::No => ty,
|
||||
};
|
||||
let ty = self.normalize(expr.span, ty);
|
||||
self.can_coerce(found, ty)
|
||||
}
|
||||
hir::FnRetTy::DefaultReturn(_) if in_closure => {
|
||||
self.ret_coercion.as_ref().map_or(false, |ret| {
|
||||
let ret_ty = ret.borrow().expected_ty();
|
||||
self.can_coerce(found, ret_ty)
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if can_return
|
||||
&& let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner()
|
||||
&& let Some(span) = expr.span.find_ancestor_inside(*owner_node.span())
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"you might have meant to return this value",
|
||||
vec![
|
||||
(span.shrink_to_lo(), "return ".to_string()),
|
||||
(span.shrink_to_hi(), ";".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -304,6 +304,10 @@ fn typeck_with_fallback<'tcx>(
|
|||
|
||||
let typeck_results = fcx.resolve_type_vars_in_body(body);
|
||||
|
||||
// We clone the defined opaque types during writeback in the new solver
|
||||
// because we have to use them during normalization.
|
||||
let _ = fcx.infcx.take_opaque_types();
|
||||
|
||||
// Consistency check our TypeckResults instance can hold all ItemLocalIds
|
||||
// it will need to hold.
|
||||
assert_eq!(typeck_results.hir_owner, id.owner);
|
||||
|
|
|
@ -518,12 +518,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
|||
.report_mismatched_types(&cause, method_self_ty, self_ty, terr)
|
||||
.emit();
|
||||
} else {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"{} was a subtype of {} but now is not?",
|
||||
self_ty,
|
||||
method_self_ty
|
||||
);
|
||||
error!("{self_ty} was a subtype of {method_self_ty} but now is not?");
|
||||
// This must already have errored elsewhere.
|
||||
self.dcx().has_errors().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1073,12 +1073,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// for instance
|
||||
self.tcx.at(span).type_of(*def_id).instantiate_identity()
|
||||
!= rcvr_ty
|
||||
&& self
|
||||
.tcx
|
||||
.at(span)
|
||||
.type_of(*def_id)
|
||||
.instantiate_identity()
|
||||
!= rcvr_ty
|
||||
}
|
||||
(Mode::Path, false, _) => true,
|
||||
_ => false,
|
||||
|
@ -1092,7 +1086,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
inherent_impls_candidate.sort();
|
||||
inherent_impls_candidate.dedup();
|
||||
|
||||
// number of type to shows at most.
|
||||
// number of types to show at most
|
||||
let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
|
||||
let type_candidates = inherent_impls_candidate
|
||||
.iter()
|
||||
|
|
|
@ -221,8 +221,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
if base_ty.is_none() {
|
||||
// When encountering `return [0][0]` outside of a `fn` body we can encounter a base
|
||||
// that isn't in the type table. We assume more relevant errors have already been
|
||||
// emitted, so we delay an ICE if none have. (#64638)
|
||||
self.tcx().dcx().span_delayed_bug(e.span, format!("bad base: `{base:?}`"));
|
||||
// emitted. (#64638)
|
||||
assert!(self.tcx().dcx().has_errors().is_some(), "bad base: `{base:?}`");
|
||||
}
|
||||
if let Some(base_ty) = base_ty
|
||||
&& let ty::Ref(_, base_ty_inner, _) = *base_ty.kind()
|
||||
|
@ -562,7 +562,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_opaque_types(&mut self) {
|
||||
let opaque_types = self.fcx.infcx.take_opaque_types();
|
||||
// We clone the opaques instead of stealing them here as they are still used for
|
||||
// normalization in the next generation trait solver.
|
||||
//
|
||||
// FIXME(-Znext-solver): Opaque types defined after this would simply get dropped
|
||||
// at the end of typeck. While this seems unlikely to happen in practice this
|
||||
// should still get fixed. Either by preventing writeback from defining new opaque
|
||||
// types or by using this function at the end of writeback and running it as a
|
||||
// fixpoint.
|
||||
let opaque_types = self.fcx.infcx.clone_opaque_types();
|
||||
for (opaque_type_key, decl) in opaque_types {
|
||||
let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
|
||||
let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
|
||||
|
|
|
@ -285,13 +285,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
|
|||
T: Relate<'tcx>,
|
||||
{
|
||||
let Trace { at, trace, a_is_expected } = self;
|
||||
at.infcx.commit_if_ok(|_| {
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.sub(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |_| InferOk { value: (), obligations: fields.obligations })
|
||||
})
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.sub(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |_| InferOk { value: (), obligations: fields.obligations })
|
||||
}
|
||||
|
||||
/// Makes `a == b`; the expectation is set by the call to
|
||||
|
@ -302,13 +300,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
|
|||
T: Relate<'tcx>,
|
||||
{
|
||||
let Trace { at, trace, a_is_expected } = self;
|
||||
at.infcx.commit_if_ok(|_| {
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.equate(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |_| InferOk { value: (), obligations: fields.obligations })
|
||||
})
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.equate(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |_| InferOk { value: (), obligations: fields.obligations })
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
|
@ -317,13 +313,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
|
|||
T: Relate<'tcx>,
|
||||
{
|
||||
let Trace { at, trace, a_is_expected } = self;
|
||||
at.infcx.commit_if_ok(|_| {
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.lub(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |t| InferOk { value: t, obligations: fields.obligations })
|
||||
})
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.lub(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |t| InferOk { value: t, obligations: fields.obligations })
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
|
@ -332,13 +326,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
|
|||
T: Relate<'tcx>,
|
||||
{
|
||||
let Trace { at, trace, a_is_expected } = self;
|
||||
at.infcx.commit_if_ok(|_| {
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.glb(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |t| InferOk { value: t, obligations: fields.obligations })
|
||||
})
|
||||
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
|
||||
fields
|
||||
.glb(a_is_expected)
|
||||
.relate(a, b)
|
||||
.map(move |t| InferOk { value: t, obligations: fields.obligations })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -620,42 +620,40 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
variables1: &OriginalQueryValues<'tcx>,
|
||||
variables2: impl Fn(BoundVar) -> GenericArg<'tcx>,
|
||||
) -> InferResult<'tcx, ()> {
|
||||
self.commit_if_ok(|_| {
|
||||
let mut obligations = vec![];
|
||||
for (index, value1) in variables1.var_values.iter().enumerate() {
|
||||
let value2 = variables2(BoundVar::new(index));
|
||||
let mut obligations = vec![];
|
||||
for (index, value1) in variables1.var_values.iter().enumerate() {
|
||||
let value2 = variables2(BoundVar::new(index));
|
||||
|
||||
match (value1.unpack(), value2.unpack()) {
|
||||
(GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
|
||||
obligations.extend(
|
||||
self.at(cause, param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, v1, v2)?
|
||||
.into_obligations(),
|
||||
);
|
||||
}
|
||||
(GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
|
||||
if re1.is_erased() && re2.is_erased() =>
|
||||
{
|
||||
// no action needed
|
||||
}
|
||||
(GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
|
||||
obligations.extend(
|
||||
self.at(cause, param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, v1, v2)?
|
||||
.into_obligations(),
|
||||
);
|
||||
}
|
||||
(GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
|
||||
let ok = self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, v1, v2)?;
|
||||
obligations.extend(ok.into_obligations());
|
||||
}
|
||||
_ => {
|
||||
bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
|
||||
}
|
||||
match (value1.unpack(), value2.unpack()) {
|
||||
(GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
|
||||
obligations.extend(
|
||||
self.at(cause, param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, v1, v2)?
|
||||
.into_obligations(),
|
||||
);
|
||||
}
|
||||
(GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
|
||||
if re1.is_erased() && re2.is_erased() =>
|
||||
{
|
||||
// no action needed
|
||||
}
|
||||
(GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
|
||||
obligations.extend(
|
||||
self.at(cause, param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, v1, v2)?
|
||||
.into_obligations(),
|
||||
);
|
||||
}
|
||||
(GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
|
||||
let ok = self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, v1, v2)?;
|
||||
obligations.extend(ok.into_obligations());
|
||||
}
|
||||
_ => {
|
||||
bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
|
||||
}
|
||||
}
|
||||
Ok(InferOk { value: (), obligations })
|
||||
})
|
||||
}
|
||||
Ok(InferOk { value: (), obligations })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -132,20 +132,6 @@ pub struct TypeErrCtxt<'a, 'tcx> {
|
|||
Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
|
||||
}
|
||||
|
||||
impl Drop for TypeErrCtxt<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
if self.dcx().has_errors().is_some() {
|
||||
// Ok, emitted an error.
|
||||
} else {
|
||||
// Didn't emit an error; maybe it was created but not yet emitted.
|
||||
self.infcx
|
||||
.tcx
|
||||
.sess
|
||||
.good_path_delayed_bug("used a `TypeErrCtxt` without raising an error or lint");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
pub fn dcx(&self) -> &'tcx DiagCtxt {
|
||||
self.infcx.tcx.dcx()
|
||||
|
|
|
@ -802,14 +802,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
|||
}
|
||||
|
||||
// Errors in earlier passes can yield error variables without
|
||||
// resolution errors here; delay ICE in favor of those errors.
|
||||
self.tcx().dcx().span_delayed_bug(
|
||||
self.var_infos[node_idx].origin.span(),
|
||||
format!(
|
||||
"collect_error_for_expanding_node() could not find \
|
||||
error for var {node_idx:?} in universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \
|
||||
upper_bounds={upper_bounds:#?}"
|
||||
),
|
||||
// resolution errors here; ICE if no errors have been emitted yet.
|
||||
assert!(
|
||||
self.tcx().dcx().has_errors().is_some(),
|
||||
"collect_error_for_expanding_node() could not find error for var {node_idx:?} in \
|
||||
universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \
|
||||
upper_bounds={upper_bounds:#?}",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1325,6 +1325,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn clone_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> {
|
||||
debug_assert_ne!(self.defining_use_anchor, DefiningAnchor::Error);
|
||||
self.inner.borrow().opaque_type_storage.opaque_types.clone()
|
||||
}
|
||||
|
||||
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
|
||||
self.resolve_vars_if_possible(t).to_string()
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ impl<'tcx> OpaqueTypeStorage<'tcx> {
|
|||
if let Some(idx) = idx {
|
||||
self.opaque_types.get_mut(&key).unwrap().hidden_type = idx;
|
||||
} else {
|
||||
match self.opaque_types.remove(&key) {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
match self.opaque_types.swap_remove(&key) {
|
||||
None => bug!("reverted opaque type inference that was never registered: {:?}", key),
|
||||
Some(_) => {}
|
||||
}
|
||||
|
|
|
@ -300,9 +300,9 @@ where
|
|||
self.components_must_outlive(origin, subcomponents, region, category);
|
||||
}
|
||||
Component::UnresolvedInferenceVariable(v) => {
|
||||
// ignore this, we presume it will yield an error
|
||||
// later, since if a type variable is not resolved by
|
||||
// this point it never will be
|
||||
// Ignore this, we presume it will yield an error later,
|
||||
// since if a type variable is not resolved by this point
|
||||
// it never will be.
|
||||
self.tcx.dcx().span_delayed_bug(
|
||||
origin.span(),
|
||||
format!("unresolved inference variable in outlives: {v:?}"),
|
||||
|
|
|
@ -172,13 +172,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
self.bound_from_components(components, visited)
|
||||
}
|
||||
Component::UnresolvedInferenceVariable(v) => {
|
||||
// ignore this, we presume it will yield an error
|
||||
// later, since if a type variable is not resolved by
|
||||
// this point it never will be
|
||||
// Ignore this, we presume it will yield an error later, since
|
||||
// if a type variable is not resolved by this point it never
|
||||
// will be.
|
||||
self.tcx
|
||||
.dcx()
|
||||
.delayed_bug(format!("unresolved inference variable in outlives: {v:?}"));
|
||||
// add a bound that never holds
|
||||
// Add a bound that never holds.
|
||||
VerifyBound::AnyBound(vec![])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -431,14 +431,13 @@ pub fn check_ast_node_inner<'a, T: EarlyLintPass>(
|
|||
// If not, that means that we somehow buffered a lint for a node id
|
||||
// that was not lint-checked (perhaps it doesn't exist?). This is a bug.
|
||||
for (id, lints) in cx.context.buffered.map {
|
||||
for early_lint in lints {
|
||||
sess.dcx().span_delayed_bug(
|
||||
early_lint.span,
|
||||
format!(
|
||||
"failed to process buffered lint here (dummy = {})",
|
||||
id == ast::DUMMY_NODE_ID
|
||||
),
|
||||
if !lints.is_empty() {
|
||||
assert!(
|
||||
sess.dcx().has_errors().is_some(),
|
||||
"failed to process buffered lint here (dummy = {})",
|
||||
id == ast::DUMMY_NODE_ID
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -651,9 +651,11 @@ trait UnusedDelimLint {
|
|||
|
||||
fn is_expr_delims_necessary(
|
||||
inner: &ast::Expr,
|
||||
ctx: UnusedDelimsCtx,
|
||||
followed_by_block: bool,
|
||||
followed_by_else: bool,
|
||||
) -> bool {
|
||||
let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
|
||||
|
||||
if followed_by_else {
|
||||
match inner.kind {
|
||||
ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
|
||||
|
@ -662,6 +664,13 @@ trait UnusedDelimLint {
|
|||
}
|
||||
}
|
||||
|
||||
// Check it's range in LetScrutineeExpr
|
||||
if let ast::ExprKind::Range(..) = inner.kind
|
||||
&& matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`.
|
||||
{
|
||||
let mut innermost = inner;
|
||||
|
@ -1007,8 +1016,7 @@ impl UnusedDelimLint for UnusedParens {
|
|||
) {
|
||||
match value.kind {
|
||||
ast::ExprKind::Paren(ref inner) => {
|
||||
let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
|
||||
if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else)
|
||||
if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
|
||||
&& value.attrs.is_empty()
|
||||
&& !value.span.from_expansion()
|
||||
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
|
||||
|
@ -1334,7 +1342,7 @@ impl UnusedDelimLint for UnusedBraces {
|
|||
// FIXME(const_generics): handle paths when #67075 is fixed.
|
||||
if let [stmt] = inner.stmts.as_slice() {
|
||||
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
|
||||
if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
|
||||
if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
|
||||
&& (ctx != UnusedDelimsCtx::AnonConst
|
||||
|| (matches!(expr.kind, ast::ExprKind::Lit(_))
|
||||
&& !expr.span.from_expansion()))
|
||||
|
|
|
@ -708,7 +708,8 @@ impl LintBuffer {
|
|||
}
|
||||
|
||||
pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
|
||||
self.map.remove(&id).unwrap_or_default()
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
self.map.swap_remove(&id).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn buffer_lint(
|
||||
|
|
|
@ -8,7 +8,12 @@ edition = "2021"
|
|||
libc = "0.2.73"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
# FIXME: updating cc past 1.0.79 breaks libstd bootstrapping, pin
|
||||
# to the last working version here so `cargo update` doesn't cause the
|
||||
# a higher version to be selected
|
||||
# https://github.com/rust-lang/cc-rs/issues/913
|
||||
# 1.0.{84, 85} fix this but have been yanked
|
||||
[build-dependencies]
|
||||
# tidy-alphabetical-start
|
||||
cc = "1.0.69"
|
||||
cc = "=1.0.79"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
@ -139,7 +139,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
|
|||
RustMappingRegions, NumMappingRegions)) {
|
||||
MappingRegions.emplace_back(
|
||||
fromRust(Region.Count), fromRust(Region.FalseCount),
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
|
||||
coverage::CounterMappingRegion::MCDCParameters{},
|
||||
#endif
|
||||
Region.FileID, Region.ExpandedFileID,
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
#include "llvm/Support/PGOOptions.h"
|
||||
#endif
|
||||
#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
|
||||
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
|
||||
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
|
||||
|
@ -368,10 +371,10 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
|
|||
}
|
||||
|
||||
extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
||||
#ifdef LLVM_RUSTLLVM
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures();
|
||||
return FeatTable.size();
|
||||
#else
|
||||
return 0;
|
||||
|
@ -380,10 +383,10 @@ extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
|||
|
||||
extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index,
|
||||
const char** Feature, const char** Desc) {
|
||||
#ifdef LLVM_RUSTLLVM
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures();
|
||||
const SubtargetFeatureKV Feat = FeatTable[Index];
|
||||
*Feature = Feat.Key;
|
||||
*Desc = Feat.Desc;
|
||||
|
@ -749,6 +752,9 @@ LLVMRustOptimize(
|
|||
FS,
|
||||
#endif
|
||||
PGOOptions::IRInstr, PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
} else if (PGOUsePath) {
|
||||
assert(!PGOSampleUsePath);
|
||||
|
@ -758,6 +764,9 @@ LLVMRustOptimize(
|
|||
FS,
|
||||
#endif
|
||||
PGOOptions::IRUse, PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
} else if (PGOSampleUsePath) {
|
||||
PGOOpt = PGOOptions(PGOSampleUsePath, "", "",
|
||||
|
@ -766,6 +775,9 @@ LLVMRustOptimize(
|
|||
FS,
|
||||
#endif
|
||||
PGOOptions::SampleUse, PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
} else if (DebugInfoForProfiling) {
|
||||
PGOOpt = PGOOptions("", "", "",
|
||||
|
@ -774,6 +786,9 @@ LLVMRustOptimize(
|
|||
FS,
|
||||
#endif
|
||||
PGOOptions::NoAction, PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
}
|
||||
|
||||
|
|
|
@ -1801,6 +1801,9 @@ extern "C" LLVMRustResult LLVMRustWriteImportLibrary(
|
|||
std::string{}, // ExtName
|
||||
std::string{}, // SymbolName
|
||||
std::string{}, // AliasTarget
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
std::string{}, // ExportAs
|
||||
#endif
|
||||
ordinal, // Ordinal
|
||||
ordinal_present, // Noname
|
||||
false, // Data
|
||||
|
|
|
@ -617,7 +617,7 @@ impl<'hir> Map<'hir> {
|
|||
Node::Item(_)
|
||||
| Node::ForeignItem(_)
|
||||
| Node::TraitItem(_)
|
||||
| Node::Expr(Expr { kind: ExprKind::Closure { .. }, .. })
|
||||
| Node::Expr(Expr { kind: ExprKind::Closure(_), .. })
|
||||
| Node::ImplItem(_)
|
||||
// The input node `id` must be enclosed in the method's body as opposed
|
||||
// to some other place such as its return type (fixes #114918).
|
||||
|
|
|
@ -207,6 +207,11 @@ pub fn explain_lint_level_source(
|
|||
err: &mut Diagnostic,
|
||||
) {
|
||||
let name = lint.name_lower();
|
||||
if let Level::Allow = level {
|
||||
// Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
|
||||
// triggering. (#121009)
|
||||
return;
|
||||
}
|
||||
match src {
|
||||
LintLevelSource::Default => {
|
||||
err.note_once(format!("`#[{}({})]` on by default", level.as_str(), name));
|
||||
|
|
|
@ -35,6 +35,16 @@ impl<'tcx> IntoKind for Const<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Const<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstTy<TyCtxt<'tcx>> for Const<'tcx> {
|
||||
fn ty(self) -> Ty<'tcx> {
|
||||
self.ty()
|
||||
|
@ -63,11 +73,13 @@ impl<'tcx> Const<'tcx> {
|
|||
self.0.kind
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline]
|
||||
pub fn flags(self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline]
|
||||
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
|
|
|
@ -28,7 +28,7 @@ use crate::ty::{
|
|||
self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind,
|
||||
ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate,
|
||||
PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid,
|
||||
Visibility,
|
||||
TypeVisitable, Visibility,
|
||||
};
|
||||
use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
|
||||
use rustc_ast::{self as ast, attr};
|
||||
|
@ -87,7 +87,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
type GenericArg = ty::GenericArg<'tcx>;
|
||||
type Term = ty::Term<'tcx>;
|
||||
|
||||
type Binder<T> = Binder<'tcx, T>;
|
||||
type Binder<T: TypeVisitable<TyCtxt<'tcx>>> = Binder<'tcx, T>;
|
||||
type BoundVars = &'tcx List<ty::BoundVariableKind>;
|
||||
type BoundVar = ty::BoundVariableKind;
|
||||
type CanonicalVars = CanonicalVarInfos<'tcx>;
|
||||
|
||||
type Ty = Ty<'tcx>;
|
||||
|
@ -151,6 +153,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
) -> Self::Const {
|
||||
Const::new_bound(self, debruijn, var, ty)
|
||||
}
|
||||
|
||||
fn expect_error_or_delayed_bug() {
|
||||
let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs());
|
||||
assert!(has_errors.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
|
||||
|
@ -1046,12 +1053,6 @@ impl<'tcx> TyCtxtAt<'tcx> {
|
|||
name: Symbol,
|
||||
def_kind: DefKind,
|
||||
) -> TyCtxtFeed<'tcx, LocalDefId> {
|
||||
// This function modifies `self.definitions` using a side-effect.
|
||||
// We need to ensure that these side effects are re-run by the incr. comp. engine.
|
||||
// Depending on the forever-red node will tell the graph that the calling query
|
||||
// needs to be re-evaluated.
|
||||
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
|
||||
|
||||
// The following call has the side effect of modifying the tables inside `definitions`.
|
||||
// These very tables are relied on by the incr. comp. engine to decode DepNodes and to
|
||||
// decode the on-disk cache.
|
||||
|
@ -1080,6 +1081,12 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
let data = def_kind.def_path_data(name);
|
||||
let def_id = self.untracked.definitions.write().create_def(parent, data);
|
||||
|
||||
// This function modifies `self.definitions` using a side-effect.
|
||||
// We need to ensure that these side effects are re-run by the incr. comp. engine.
|
||||
// Depending on the forever-red node will tell the graph that the calling query
|
||||
// needs to be re-evaluated.
|
||||
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
|
||||
|
||||
let feed = self.feed_local_def_id(def_id);
|
||||
feed.def_kind(def_kind);
|
||||
// Unique types created for closures participate in type privacy checking.
|
||||
|
|
|
@ -252,6 +252,7 @@ pub struct ImplHeader<'tcx> {
|
|||
pub struct ImplTraitHeader<'tcx> {
|
||||
pub trait_ref: ty::TraitRef<'tcx>,
|
||||
pub polarity: ImplPolarity,
|
||||
pub unsafety: hir::Unsafety,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
|
||||
|
@ -503,6 +504,16 @@ impl<'tcx> IntoKind for Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Ty<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyParamRegion {
|
||||
/// Does this early bound region have a name? Early bound regions normally
|
||||
/// always have names except when using anonymous lifetimes (`'_`).
|
||||
|
|
|
@ -29,6 +29,16 @@ pub struct Predicate<'tcx>(
|
|||
pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
|
||||
);
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Predicate<'tcx> {
|
||||
/// Gets the inner `ty::Binder<'tcx, PredicateKind<'tcx>>`.
|
||||
#[inline]
|
||||
|
@ -36,11 +46,13 @@ impl<'tcx> Predicate<'tcx> {
|
|||
self.0.internee
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline(always)]
|
||||
pub fn flags(self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline(always)]
|
||||
pub fn outer_exclusive_binder(self) -> DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
|
|
|
@ -3156,13 +3156,12 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
|
|||
// this is pub to be able to intra-doc-link it
|
||||
pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap<Symbol> {
|
||||
// Trimming paths is expensive and not optimized, since we expect it to only be used for error
|
||||
// reporting.
|
||||
// reporting. Record the fact that we did it, so we can abort if we later found it was
|
||||
// unnecessary.
|
||||
//
|
||||
// For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths`
|
||||
// wrapper can be used to suppress this query, in exchange for full paths being formatted.
|
||||
tcx.sess.good_path_delayed_bug(
|
||||
"trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging",
|
||||
);
|
||||
// The `rustc_middle::ty::print::with_no_trimmed_paths` wrapper can be used to suppress this
|
||||
// checking, in exchange for full paths being formatted.
|
||||
tcx.sess.record_trimmed_def_paths();
|
||||
|
||||
// Once constructed, unique namespace+symbol pairs will have a `Some(_)` entry, while
|
||||
// non-unique pairs will have a `None` entry.
|
||||
|
|
|
@ -26,6 +26,19 @@ impl<'tcx> rustc_type_ir::IntoKind for Region<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Region<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.type_flags()
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
|
||||
match **self {
|
||||
ty::ReBound(debruijn, _) => debruijn.shifted_in(1),
|
||||
_ => ty::INNERMOST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Region<'tcx> {
|
||||
#[inline]
|
||||
pub fn new_early_param(
|
||||
|
|
|
@ -942,6 +942,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> rustc_type_ir::BoundVars<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
|
||||
fn bound_vars(&self) -> &'tcx List<ty::BoundVariableKind> {
|
||||
self.bound_vars
|
||||
}
|
||||
|
||||
fn has_no_bound_vars(&self) -> bool {
|
||||
self.bound_vars.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Binder<'tcx, T> {
|
||||
/// Skips the binder and returns the "bound" value. This is a
|
||||
/// risky thing to do because it's easy to get confused about
|
||||
|
@ -1808,6 +1818,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
self.0.0
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline(always)]
|
||||
pub fn flags(self) -> TypeFlags {
|
||||
self.0.0.flags
|
||||
|
@ -2362,6 +2373,20 @@ impl<'tcx> Ty<'tcx> {
|
|||
/// to represent the closure kind, because it has not yet been
|
||||
/// inferred. Once upvar inference (in `rustc_hir_analysis/src/check/upvar.rs`)
|
||||
/// is complete, that type variable will be unified.
|
||||
///
|
||||
/// To be noted that you can use [`ClosureArgs::kind()`] or [`CoroutineClosureArgs::kind()`]
|
||||
/// to get the same information, which you can get by calling [`GenericArgs::as_closure()`]
|
||||
/// or [`GenericArgs::as_coroutine_closure()`], depending on the type of the closure.
|
||||
///
|
||||
/// Otherwise, this method can be used as follows:
|
||||
///
|
||||
/// ```rust,ignore (snippet of compiler code)
|
||||
/// let TyKind::Closure(def_id, [closure_fn_kind_ty, ..]) = closure_ty.kind()
|
||||
/// && let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind()
|
||||
/// {
|
||||
/// // your code
|
||||
/// }
|
||||
/// ```
|
||||
pub fn to_opt_closure_kind(self) -> Option<ty::ClosureKind> {
|
||||
match self.kind() {
|
||||
Int(int_ty) => match int_ty {
|
||||
|
|
|
@ -1320,6 +1320,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
ty
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline]
|
||||
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
|
|
|
@ -1,140 +1,10 @@
|
|||
use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sso::SsoHashSet;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
|
||||
|
||||
pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
|
||||
/// Returns `true` if `self` has any late-bound regions that are either
|
||||
/// bound by `binder` or bound by some binder outside of `binder`.
|
||||
/// If `binder` is `ty::INNERMOST`, this indicates whether
|
||||
/// there are any late-bound regions that appear free.
|
||||
fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
|
||||
self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break()
|
||||
}
|
||||
|
||||
/// Returns `true` if this type has any regions that escape `binder` (and
|
||||
/// hence are not bound by it).
|
||||
fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
|
||||
self.has_vars_bound_at_or_above(binder.shifted_in(1))
|
||||
}
|
||||
|
||||
/// Return `true` if this type has regions that are not a part of the type.
|
||||
/// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)`
|
||||
/// would return `true`. The latter can occur when traversing through the
|
||||
/// former.
|
||||
///
|
||||
/// See [`HasEscapingVarsVisitor`] for more information.
|
||||
fn has_escaping_bound_vars(&self) -> bool {
|
||||
self.has_vars_bound_at_or_above(ty::INNERMOST)
|
||||
}
|
||||
|
||||
fn has_type_flags(&self, flags: TypeFlags) -> bool {
|
||||
let res =
|
||||
self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags);
|
||||
trace!(?self, ?flags, ?res, "has_type_flags");
|
||||
res
|
||||
}
|
||||
fn has_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PROJECTION)
|
||||
}
|
||||
fn has_inherent_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
|
||||
}
|
||||
fn has_opaque_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
|
||||
}
|
||||
fn has_coroutines(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_COROUTINE)
|
||||
}
|
||||
fn references_error(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_ERROR)
|
||||
}
|
||||
fn error_reported(&self) -> Result<(), ErrorGuaranteed> {
|
||||
if self.references_error() {
|
||||
// We must include lint errors and delayed bugs here.
|
||||
if let Some(reported) =
|
||||
ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs())
|
||||
{
|
||||
Err(reported)
|
||||
} else {
|
||||
bug!("expected some kind of error in `error_reported`");
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn has_non_region_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM)
|
||||
}
|
||||
fn has_infer_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_INFER)
|
||||
}
|
||||
fn has_infer_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INFER)
|
||||
}
|
||||
fn has_non_region_infer(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER)
|
||||
}
|
||||
fn has_infer(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER)
|
||||
}
|
||||
fn has_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
|
||||
}
|
||||
fn has_non_region_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
|
||||
}
|
||||
fn has_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM)
|
||||
}
|
||||
/// "Free" regions in this context means that it has any region
|
||||
/// that is not (a) erased or (b) late-bound.
|
||||
fn has_free_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
|
||||
}
|
||||
|
||||
fn has_erased_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_ERASED)
|
||||
}
|
||||
|
||||
/// True if there are any un-erased free regions.
|
||||
fn has_erasable_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
|
||||
}
|
||||
|
||||
/// Indicates whether this value references only 'global'
|
||||
/// generic parameters that are the same regardless of what fn we are
|
||||
/// in. This is used for caching.
|
||||
fn is_global(&self) -> bool {
|
||||
!self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES)
|
||||
}
|
||||
|
||||
/// True if there are any late-bound regions
|
||||
fn has_bound_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_BOUND)
|
||||
}
|
||||
/// True if there are any late-bound non-region variables
|
||||
fn has_non_region_bound_vars(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND)
|
||||
}
|
||||
/// True if there are any bound variables
|
||||
fn has_bound_vars(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_BOUND_VARS)
|
||||
}
|
||||
|
||||
/// Indicates whether this value still has parameters/placeholders/inference variables
|
||||
/// which could be replaced later, in a way that would change the results of `impl`
|
||||
/// specialization.
|
||||
fn still_further_specializable(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitableExt<'tcx> for T {}
|
||||
pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Region folder
|
||||
|
@ -370,185 +240,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
struct FoundEscapingVars;
|
||||
|
||||
/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
|
||||
/// bound region or a bound type.
|
||||
///
|
||||
/// So, for example, consider a type like the following, which has two binders:
|
||||
///
|
||||
/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
|
||||
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
|
||||
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope
|
||||
///
|
||||
/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
|
||||
/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
|
||||
/// fn type*, that type has an escaping region: `'a`.
|
||||
///
|
||||
/// Note that what I'm calling an "escaping var" is often just called a "free var". However,
|
||||
/// we already use the term "free var". It refers to the regions or types that we use to represent
|
||||
/// bound regions or type params on a fn definition while we are type checking its body.
|
||||
///
|
||||
/// To clarify, conceptually there is no particular difference between
|
||||
/// an "escaping" var and a "free" var. However, there is a big
|
||||
/// difference in practice. Basically, when "entering" a binding
|
||||
/// level, one is generally required to do some sort of processing to
|
||||
/// a bound var, such as replacing it with a fresh/placeholder
|
||||
/// var, or making an entry in the environment to represent the
|
||||
/// scope to which it is attached, etc. An escaping var represents
|
||||
/// a bound var for which this processing has not yet been done.
|
||||
struct HasEscapingVarsVisitor {
|
||||
/// Anything bound by `outer_index` or "above" is escaping.
|
||||
outer_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasEscapingVarsVisitor {
|
||||
type BreakTy = FoundEscapingVars;
|
||||
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: &Binder<'tcx, T>,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
self.outer_index.shift_in(1);
|
||||
let result = t.super_visit_with(self);
|
||||
self.outer_index.shift_out(1);
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// If the outer-exclusive-binder is *strictly greater* than
|
||||
// `outer_index`, that means that `t` contains some content
|
||||
// bound at `outer_index` or above (because
|
||||
// `outer_exclusive_binder` is always 1 higher than the
|
||||
// content in `t`). Therefore, `t` has some escaping vars.
|
||||
if t.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// If the region is bound by `outer_index` or anything outside
|
||||
// of outer index, then it escapes the binders we have
|
||||
// visited.
|
||||
if r.bound_at_or_above_binder(self.outer_index) {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// If the outer-exclusive-binder is *strictly greater* than
|
||||
// `outer_index`, that means that `ct` contains some content
|
||||
// bound at `outer_index` or above (because
|
||||
// `outer_exclusive_binder` is always 1 higher than the
|
||||
// content in `t`). Therefore, `t` has some escaping vars.
|
||||
if ct.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if predicate.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
struct FoundFlags;
|
||||
|
||||
// FIXME: Optimize for checking for infer flags
|
||||
struct HasTypeFlagsVisitor {
|
||||
flags: ty::TypeFlags,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for HasTypeFlagsVisitor {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.flags.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this visitor traverses values down to the level of
|
||||
// `Ty`/`Const`/`Predicate`, but not within those types. This is because the
|
||||
// type flags at the outer layer are enough. So it's faster than it first
|
||||
// looks, particular for `Ty`/`Predicate` where it's just a field access.
|
||||
//
|
||||
// N.B. The only case where this isn't totally true is binders, which also
|
||||
// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
|
||||
// are present, regardless of whether those bound variables are used. This
|
||||
// is important for anonymization of binders in `TyCtxt::erase_regions`. We
|
||||
// specifically detect this case in `visit_binder`.
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
|
||||
type BreakTy = FoundFlags;
|
||||
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: &Binder<'tcx, T>,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
// If we're looking for the HAS_BINDER_VARS flag, check if the
|
||||
// binder has vars. This won't be present in the binder's bound
|
||||
// value, so we need to check here too.
|
||||
if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() {
|
||||
return ControlFlow::Break(FoundFlags);
|
||||
}
|
||||
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
let flags = t.flags();
|
||||
if flags.intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call, as usual for `Region`.
|
||||
let flags = r.type_flags();
|
||||
if flags.intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
if c.flags().intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
if predicate.flags().intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects all the late-bound regions at the innermost binding level
|
||||
/// into a hash set.
|
||||
struct LateBoundRegionsCollector {
|
||||
|
|
|
@ -319,7 +319,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// them.
|
||||
let mut fake_borrows = match_has_guard.then(FxIndexSet::default);
|
||||
|
||||
let mut otherwise = None;
|
||||
let otherwise_block = self.cfg.start_new_block();
|
||||
|
||||
// This will generate code to test scrutinee_place and
|
||||
// branch to the appropriate arm block
|
||||
|
@ -327,46 +327,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
match_start_span,
|
||||
scrutinee_span,
|
||||
block,
|
||||
&mut otherwise,
|
||||
otherwise_block,
|
||||
candidates,
|
||||
&mut fake_borrows,
|
||||
);
|
||||
|
||||
if let Some(otherwise_block) = otherwise {
|
||||
// See the doc comment on `match_candidates` for why we may have an
|
||||
// otherwise block. Match checking will ensure this is actually
|
||||
// unreachable.
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
// See the doc comment on `match_candidates` for why we may have an
|
||||
// otherwise block. Match checking will ensure this is actually
|
||||
// unreachable.
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
|
||||
// Matching on a `scrutinee_place` with an uninhabited type doesn't
|
||||
// generate any memory reads by itself, and so if the place "expression"
|
||||
// contains unsafe operations like raw pointer dereferences or union
|
||||
// field projections, we wouldn't know to require an `unsafe` block
|
||||
// around a `match` equivalent to `std::intrinsics::unreachable()`.
|
||||
// See issue #47412 for this hole being discovered in the wild.
|
||||
//
|
||||
// HACK(eddyb) Work around the above issue by adding a dummy inspection
|
||||
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
|
||||
//
|
||||
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
|
||||
// This is currently needed to not allow matching on an uninitialized,
|
||||
// uninhabited value. If we get never patterns, those will check that
|
||||
// the place is initialized, and so this read would only be used to
|
||||
// check safety.
|
||||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||
// Matching on a `scrutinee_place` with an uninhabited type doesn't
|
||||
// generate any memory reads by itself, and so if the place "expression"
|
||||
// contains unsafe operations like raw pointer dereferences or union
|
||||
// field projections, we wouldn't know to require an `unsafe` block
|
||||
// around a `match` equivalent to `std::intrinsics::unreachable()`.
|
||||
// See issue #47412 for this hole being discovered in the wild.
|
||||
//
|
||||
// HACK(eddyb) Work around the above issue by adding a dummy inspection
|
||||
// of `scrutinee_place`, specifically by applying `ReadForMatch`.
|
||||
//
|
||||
// NOTE: ReadForMatch also checks that the scrutinee is initialized.
|
||||
// This is currently needed to not allow matching on an uninitialized,
|
||||
// uninhabited value. If we get never patterns, those will check that
|
||||
// the place is initialized, and so this read would only be used to
|
||||
// check safety.
|
||||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||
|
||||
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
|
||||
self.cfg.push_fake_read(
|
||||
otherwise_block,
|
||||
source_info,
|
||||
cause_matched_place,
|
||||
scrutinee_place,
|
||||
);
|
||||
}
|
||||
|
||||
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
|
||||
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
|
||||
self.cfg.push_fake_read(
|
||||
otherwise_block,
|
||||
source_info,
|
||||
cause_matched_place,
|
||||
scrutinee_place,
|
||||
);
|
||||
}
|
||||
|
||||
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
|
||||
|
||||
// Link each leaf candidate to the `pre_binding_block` of the next one.
|
||||
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
|
||||
|
||||
|
@ -1163,7 +1161,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
span: Span,
|
||||
scrutinee_span: Span,
|
||||
start_block: BasicBlock,
|
||||
otherwise_block: &mut Option<BasicBlock>,
|
||||
otherwise_block: BasicBlock,
|
||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
|
@ -1210,7 +1208,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
span: Span,
|
||||
scrutinee_span: Span,
|
||||
start_block: BasicBlock,
|
||||
otherwise_block: &mut Option<BasicBlock>,
|
||||
otherwise_block: BasicBlock,
|
||||
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
|
@ -1243,11 +1241,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// never reach this point.
|
||||
if unmatched_candidates.is_empty() {
|
||||
let source_info = self.source_info(span);
|
||||
if let Some(otherwise) = *otherwise_block {
|
||||
self.cfg.goto(block, source_info, otherwise);
|
||||
} else {
|
||||
*otherwise_block = Some(block);
|
||||
}
|
||||
self.cfg.goto(block, source_info, otherwise_block);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1428,7 +1422,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
scrutinee_span: Span,
|
||||
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
||||
block: BasicBlock,
|
||||
otherwise_block: &mut Option<BasicBlock>,
|
||||
otherwise_block: BasicBlock,
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
|
||||
|
@ -1453,7 +1447,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let match_pairs = mem::take(&mut first_candidate.match_pairs);
|
||||
first_candidate.pre_binding_block = Some(block);
|
||||
|
||||
let mut otherwise = None;
|
||||
let remainder_start = self.cfg.start_new_block();
|
||||
for match_pair in match_pairs {
|
||||
let PatKind::Or { ref pats } = &match_pair.pattern.kind else {
|
||||
bug!("Or-patterns should have been sorted to the end");
|
||||
|
@ -1463,7 +1457,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
first_candidate.visit_leaves(|leaf_candidate| {
|
||||
self.test_or_pattern(
|
||||
leaf_candidate,
|
||||
&mut otherwise,
|
||||
remainder_start,
|
||||
pats,
|
||||
or_span,
|
||||
&match_pair.place,
|
||||
|
@ -1472,8 +1466,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block());
|
||||
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
|
@ -1491,7 +1483,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn test_or_pattern<'pat>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
otherwise: &mut Option<BasicBlock>,
|
||||
otherwise: BasicBlock,
|
||||
pats: &'pat [Box<Pat<'tcx>>],
|
||||
or_span: Span,
|
||||
place: &PlaceBuilder<'tcx>,
|
||||
|
@ -1503,8 +1495,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard, self))
|
||||
.collect();
|
||||
let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
|
||||
let otherwise = if candidate.otherwise_block.is_some() {
|
||||
&mut candidate.otherwise_block
|
||||
let otherwise = if let Some(otherwise_block) = candidate.otherwise_block {
|
||||
otherwise_block
|
||||
} else {
|
||||
otherwise
|
||||
};
|
||||
|
@ -1680,8 +1672,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
span: Span,
|
||||
scrutinee_span: Span,
|
||||
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
|
||||
block: BasicBlock,
|
||||
otherwise_block: &mut Option<BasicBlock>,
|
||||
start_block: BasicBlock,
|
||||
otherwise_block: BasicBlock,
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
// extract the match-pair from the highest priority candidate
|
||||
|
@ -1749,12 +1741,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
debug!("untested_candidates: {}", candidates.len());
|
||||
|
||||
// The block that we should branch to if none of the
|
||||
// `target_candidates` match. This is either the block where we
|
||||
// start matching the untested candidates if there are any,
|
||||
// otherwise it's the `otherwise_block`.
|
||||
let remainder_start = &mut None;
|
||||
let remainder_start =
|
||||
if candidates.is_empty() { &mut *otherwise_block } else { remainder_start };
|
||||
// `target_candidates` match.
|
||||
let remainder_start = if !candidates.is_empty() {
|
||||
let remainder_start = self.cfg.start_new_block();
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
remainder_start,
|
||||
otherwise_block,
|
||||
candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
remainder_start
|
||||
} else {
|
||||
otherwise_block
|
||||
};
|
||||
|
||||
// For each outcome of test, process the candidates that still
|
||||
// apply. Collect a list of blocks where control flow will
|
||||
|
@ -1775,24 +1776,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
candidate_start
|
||||
} else {
|
||||
*remainder_start.get_or_insert_with(|| self.cfg.start_new_block())
|
||||
remainder_start
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !candidates.is_empty() {
|
||||
let remainder_start = remainder_start.unwrap_or_else(|| self.cfg.start_new_block());
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
remainder_start,
|
||||
otherwise_block,
|
||||
candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
}
|
||||
|
||||
self.perform_test(span, scrutinee_span, block, &match_place, &test, target_blocks);
|
||||
// Perform the test, branching to one of N blocks.
|
||||
self.perform_test(span, scrutinee_span, start_block, &match_place, &test, target_blocks);
|
||||
}
|
||||
|
||||
/// Determine the fake borrows that are needed from a set of places that
|
||||
|
|
|
@ -33,7 +33,7 @@ struct UnsafetyVisitor<'a, 'tcx> {
|
|||
body_target_features: &'tcx [Symbol],
|
||||
/// When inside the LHS of an assignment to a field, this is the type
|
||||
/// of the LHS and the span of the assignment expression.
|
||||
assignment_info: Option<(Ty<'tcx>, Span)>,
|
||||
assignment_info: Option<Ty<'tcx>>,
|
||||
in_union_destructure: bool,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
inside_adt: bool,
|
||||
|
@ -473,10 +473,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
if let ty::Adt(adt_def, _) = lhs.ty.kind()
|
||||
&& adt_def.is_union()
|
||||
{
|
||||
if let Some((assigned_ty, assignment_span)) = self.assignment_info {
|
||||
if let Some(assigned_ty) = self.assignment_info {
|
||||
if assigned_ty.needs_drop(self.tcx, self.param_env) {
|
||||
// This would be unsafe, but should be outright impossible since we reject such unions.
|
||||
self.tcx.dcx().span_delayed_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
|
||||
// This would be unsafe, but should be outright impossible since we
|
||||
// reject such unions.
|
||||
assert!(
|
||||
self.tcx.dcx().has_errors().is_some(),
|
||||
"union fields that need dropping should be impossible: \
|
||||
{assigned_ty}"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.requires_unsafe(expr.span, AccessToUnionField);
|
||||
|
@ -492,14 +497,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
|
||||
}
|
||||
|
||||
// Second, check for accesses to union fields
|
||||
// don't have any special handling for AssignOp since it causes a read *and* write to lhs
|
||||
// Second, check for accesses to union fields. Don't have any
|
||||
// special handling for AssignOp since it causes a read *and*
|
||||
// write to lhs.
|
||||
if matches!(expr.kind, ExprKind::Assign { .. }) {
|
||||
self.assignment_info = Some((lhs.ty, expr.span));
|
||||
self.assignment_info = Some(lhs.ty);
|
||||
visit::walk_expr(self, lhs);
|
||||
self.assignment_info = None;
|
||||
visit::walk_expr(self, &self.thir()[rhs]);
|
||||
return; // we have already visited everything by now
|
||||
return; // We have already visited everything by now.
|
||||
}
|
||||
}
|
||||
ExprKind::Borrow { borrow_kind, arg } => {
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::thir::cx::region::Scope;
|
|||
use crate::thir::cx::Cx;
|
||||
use crate::thir::util::UserAnnotatedTyHelpers;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
|
@ -20,7 +21,8 @@ use rustc_middle::ty::GenericArgs;
|
|||
use rustc_middle::ty::{
|
||||
self, AdtKind, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, UserType,
|
||||
};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{sym, Span, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
|
||||
|
||||
impl<'tcx> Cx<'tcx> {
|
||||
|
@ -894,7 +896,14 @@ impl<'tcx> Cx<'tcx> {
|
|||
Res::Def(DefKind::ConstParam, def_id) => {
|
||||
let hir_id = self.tcx.local_def_id_to_hir_id(def_id.expect_local());
|
||||
let generics = self.tcx.generics_of(hir_id.owner);
|
||||
let index = generics.param_def_id_to_index[&def_id];
|
||||
let Some(&index) = generics.param_def_id_to_index.get(&def_id) else {
|
||||
self.tcx.dcx().has_errors().unwrap();
|
||||
// We already errored about a late bound const
|
||||
return ExprKind::Literal {
|
||||
lit: &Spanned { span: DUMMY_SP, node: LitKind::Err },
|
||||
neg: false,
|
||||
};
|
||||
};
|
||||
let name = self.tcx.hir().name(hir_id);
|
||||
let param = ty::ParamConst::new(index, name);
|
||||
|
||||
|
|
|
@ -153,8 +153,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
// a hard error when we don't have a valtree or when we find something in
|
||||
// the valtree that is not structural; then this can all be made a lot simpler.
|
||||
|
||||
let structural =
|
||||
traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
|
||||
let structural = traits::search_for_structural_match_violation(self.tcx(), cv.ty());
|
||||
debug!(
|
||||
"search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
|
||||
cv.ty(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use std::mem;
|
||||
|
@ -132,6 +132,9 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
|||
let body = self.builder.body;
|
||||
let tcx = self.builder.tcx;
|
||||
let place_ty = place_ref.ty(body, tcx).ty;
|
||||
if place_ty.references_error() {
|
||||
return MovePathResult::Error;
|
||||
}
|
||||
match elem {
|
||||
ProjectionElem::Deref => match place_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => {
|
||||
|
|
|
@ -243,10 +243,11 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
|
|||
// old value is being dropped.
|
||||
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
|
||||
if assigned_ty.needs_drop(self.tcx, self.param_env) {
|
||||
// This would be unsafe, but should be outright impossible since we reject such unions.
|
||||
self.tcx.dcx().span_delayed_bug(
|
||||
self.source_info.span,
|
||||
format!("union fields that need dropping should be impossible: {assigned_ty}")
|
||||
// This would be unsafe, but should be outright impossible since we reject
|
||||
// such unions.
|
||||
assert!(
|
||||
self.tcx.dcx().has_errors().is_some(),
|
||||
"union fields that need dropping should be impossible: {assigned_ty}"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use rustc_data_structures::graph::WithNumNodes;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir;
|
||||
use rustc_span::{BytePos, Span, DUMMY_SP};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
|
||||
use crate::coverage::spans::from_mir::SpanFromMir;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
mod from_mir;
|
||||
|
@ -61,7 +62,7 @@ pub(super) fn generate_coverage_spans(
|
|||
basic_coverage_blocks,
|
||||
);
|
||||
let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans);
|
||||
mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| {
|
||||
mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| {
|
||||
// Each span produced by the generator represents an ordinary code region.
|
||||
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
|
||||
}));
|
||||
|
@ -85,18 +86,36 @@ pub(super) fn generate_coverage_spans(
|
|||
Some(CoverageSpans { bcb_has_mappings, mappings })
|
||||
}
|
||||
|
||||
/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that
|
||||
/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s.
|
||||
/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent
|
||||
/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the
|
||||
/// `merged_spans` vectors, and the `Span`s to cover the extent of the combined `Span`s.
|
||||
///
|
||||
/// Note: A span merged into another CoverageSpan may come from a `BasicBlock` that
|
||||
/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches
|
||||
/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock`
|
||||
/// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
|
||||
#[derive(Debug, Clone)]
|
||||
struct CoverageSpan {
|
||||
#[derive(Debug)]
|
||||
struct CurrCovspan {
|
||||
/// This is used as the basis for [`PrevCovspan::original_span`], so it must
|
||||
/// not be modified.
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl CurrCovspan {
|
||||
fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
|
||||
Self { span, bcb, is_closure }
|
||||
}
|
||||
|
||||
fn into_prev(self) -> PrevCovspan {
|
||||
let Self { span, bcb, is_closure } = self;
|
||||
PrevCovspan { original_span: span, span, bcb, merged_spans: vec![span], is_closure }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
// This is only called in cases where `curr` is a closure span that has
|
||||
// been carved out of `prev`.
|
||||
debug_assert!(self.is_closure);
|
||||
self.into_prev().into_refined()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PrevCovspan {
|
||||
original_span: Span,
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
/// List of all the original spans from MIR that have been merged into this
|
||||
|
@ -105,37 +124,82 @@ struct CoverageSpan {
|
|||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl CoverageSpan {
|
||||
fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
|
||||
Self { span, bcb, merged_spans: vec![span], is_closure }
|
||||
impl PrevCovspan {
|
||||
fn is_mergeable(&self, other: &CurrCovspan) -> bool {
|
||||
self.bcb == other.bcb && !self.is_closure && !other.is_closure
|
||||
}
|
||||
|
||||
pub fn merge_from(&mut self, other: &Self) {
|
||||
fn merge_from(&mut self, other: &CurrCovspan) {
|
||||
debug_assert!(self.is_mergeable(other));
|
||||
self.span = self.span.to(other.span);
|
||||
self.merged_spans.extend_from_slice(&other.merged_spans);
|
||||
self.merged_spans.push(other.span);
|
||||
}
|
||||
|
||||
pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
|
||||
fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
|
||||
self.merged_spans.retain(|span| span.hi() <= cutoff_pos);
|
||||
if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() {
|
||||
self.span = self.span.with_hi(max_hi);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_mergeable(&self, other: &Self) -> bool {
|
||||
self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure)
|
||||
fn into_dup(self) -> DuplicateCovspan {
|
||||
let Self { original_span, span, bcb, merged_spans: _, is_closure } = self;
|
||||
// Only unmodified spans end up in `pending_dups`.
|
||||
debug_assert_eq!(original_span, span);
|
||||
DuplicateCovspan { span, bcb, is_closure }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_in_same_bcb(&self, other: &Self) -> bool {
|
||||
self.bcb == other.bcb
|
||||
fn refined_copy(&self) -> RefinedCovspan {
|
||||
let &Self { original_span: _, span, bcb, merged_spans: _, is_closure } = self;
|
||||
RefinedCovspan { span, bcb, is_closure }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
self.refined_copy()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a
|
||||
/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to:
|
||||
#[derive(Debug)]
|
||||
struct DuplicateCovspan {
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl DuplicateCovspan {
|
||||
/// Returns a copy of this covspan, as a [`RefinedCovspan`].
|
||||
/// Should only be called in places that would otherwise clone this covspan.
|
||||
fn refined_copy(&self) -> RefinedCovspan {
|
||||
let &Self { span, bcb, is_closure } = self;
|
||||
RefinedCovspan { span, bcb, is_closure }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
// Even though we consume self, we can just reuse the copying impl.
|
||||
self.refined_copy()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RefinedCovspan {
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl RefinedCovspan {
|
||||
fn is_mergeable(&self, other: &Self) -> bool {
|
||||
self.bcb == other.bcb && !self.is_closure && !other.is_closure
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, other: &Self) {
|
||||
debug_assert!(self.is_mergeable(other));
|
||||
self.span = self.span.to(other.span);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the initial set of coverage spans (one per MIR `Statement` or `Terminator`) into a
|
||||
/// minimal set of coverage spans, using the BCB CFG to determine where it is safe and useful to:
|
||||
///
|
||||
/// * Remove duplicate source code coverage regions
|
||||
/// * Merge spans that represent continuous (both in source code and control flow), non-branching
|
||||
|
@ -145,43 +209,33 @@ struct SpansRefiner<'a> {
|
|||
/// The BasicCoverageBlock Control Flow Graph (BCB CFG).
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
|
||||
/// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative
|
||||
/// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative
|
||||
/// dominance between the `BasicCoverageBlock`s of equal `Span`s.
|
||||
sorted_spans_iter: std::vec::IntoIter<CoverageSpan>,
|
||||
sorted_spans_iter: std::vec::IntoIter<SpanFromMir>,
|
||||
|
||||
/// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the
|
||||
/// The current coverage span to compare to its `prev`, to possibly merge, discard, force the
|
||||
/// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to
|
||||
/// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next
|
||||
/// iteration.
|
||||
some_curr: Option<CoverageSpan>,
|
||||
some_curr: Option<CurrCovspan>,
|
||||
|
||||
/// The original `span` for `curr`, in case `curr.span()` is modified. The `curr_original_span`
|
||||
/// **must not be mutated** (except when advancing to the next `curr`), even if `curr.span()`
|
||||
/// is mutated.
|
||||
curr_original_span: Span,
|
||||
|
||||
/// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`.
|
||||
/// The coverage span from a prior iteration; typically assigned from that iteration's `curr`.
|
||||
/// If that `curr` was discarded, `prev` retains its value from the previous iteration.
|
||||
some_prev: Option<CoverageSpan>,
|
||||
some_prev: Option<PrevCovspan>,
|
||||
|
||||
/// Assigned from `curr_original_span` from the previous iteration. The `prev_original_span`
|
||||
/// **must not be mutated** (except when advancing to the next `prev`), even if `prev.span()`
|
||||
/// is mutated.
|
||||
prev_original_span: Span,
|
||||
|
||||
/// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and
|
||||
/// One or more coverage spans with the same `Span` but different `BasicCoverageBlock`s, and
|
||||
/// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list.
|
||||
/// If a new `curr` span also fits this criteria (compared to an existing list of
|
||||
/// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to
|
||||
/// `pending_dups`), that `curr` moves to `prev` before possibly being added to
|
||||
/// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups`
|
||||
/// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev`
|
||||
/// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a
|
||||
/// `prev` with a matching `Span`)
|
||||
pending_dups: Vec<CoverageSpan>,
|
||||
pending_dups: Vec<DuplicateCovspan>,
|
||||
|
||||
/// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression`
|
||||
/// will also be injected into the MIR for each `CoverageSpan`.
|
||||
refined_spans: Vec<CoverageSpan>,
|
||||
/// The final coverage spans to add to the coverage map. A `Counter` or `Expression`
|
||||
/// will also be injected into the MIR for each BCB that has associated spans.
|
||||
refined_spans: Vec<RefinedCovspan>,
|
||||
}
|
||||
|
||||
impl<'a> SpansRefiner<'a> {
|
||||
|
@ -190,15 +244,13 @@ impl<'a> SpansRefiner<'a> {
|
|||
/// and carving holes in spans when they overlap in unwanted ways.
|
||||
fn refine_sorted_spans(
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
sorted_spans: Vec<CoverageSpan>,
|
||||
) -> Vec<CoverageSpan> {
|
||||
sorted_spans: Vec<SpanFromMir>,
|
||||
) -> Vec<RefinedCovspan> {
|
||||
let this = Self {
|
||||
basic_coverage_blocks,
|
||||
sorted_spans_iter: sorted_spans.into_iter(),
|
||||
some_curr: None,
|
||||
curr_original_span: DUMMY_SP,
|
||||
some_prev: None,
|
||||
prev_original_span: DUMMY_SP,
|
||||
pending_dups: Vec::new(),
|
||||
refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2),
|
||||
};
|
||||
|
@ -206,9 +258,9 @@ impl<'a> SpansRefiner<'a> {
|
|||
this.to_refined_spans()
|
||||
}
|
||||
|
||||
/// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and
|
||||
/// de-duplicated `CoverageSpan`s.
|
||||
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
|
||||
/// Iterate through the sorted coverage spans, and return the refined list of merged and
|
||||
/// de-duplicated spans.
|
||||
fn to_refined_spans(mut self) -> Vec<RefinedCovspan> {
|
||||
while self.next_coverage_span() {
|
||||
// For the first span we don't have `prev` set, so most of the
|
||||
// span-processing steps don't make sense yet.
|
||||
|
@ -221,16 +273,15 @@ impl<'a> SpansRefiner<'a> {
|
|||
let prev = self.prev();
|
||||
let curr = self.curr();
|
||||
|
||||
if curr.is_mergeable(prev) {
|
||||
if prev.is_mergeable(curr) {
|
||||
debug!(" same bcb (and neither is a closure), merge with prev={prev:?}");
|
||||
let prev = self.take_prev();
|
||||
self.curr_mut().merge_from(&prev);
|
||||
// Note that curr.span may now differ from curr_original_span
|
||||
let curr = self.take_curr();
|
||||
self.prev_mut().merge_from(&curr);
|
||||
} else if prev.span.hi() <= curr.span.lo() {
|
||||
debug!(
|
||||
" different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}",
|
||||
);
|
||||
let prev = self.take_prev();
|
||||
let prev = self.take_prev().into_refined();
|
||||
self.refined_spans.push(prev);
|
||||
} else if prev.is_closure {
|
||||
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
|
||||
|
@ -241,9 +292,9 @@ impl<'a> SpansRefiner<'a> {
|
|||
self.take_curr(); // Discards curr.
|
||||
} else if curr.is_closure {
|
||||
self.carve_out_span_for_closure();
|
||||
} else if self.prev_original_span == curr.span {
|
||||
// `prev` and `curr` have the same span, or would have had the
|
||||
// same span before `prev` was modified by other spans.
|
||||
} else if prev.original_span == prev.span && prev.span == curr.span {
|
||||
// Prev and curr have the same span, and prev's span hasn't
|
||||
// been modified by other spans.
|
||||
self.update_pending_dups();
|
||||
} else {
|
||||
self.cutoff_prev_at_overlapping_curr();
|
||||
|
@ -253,14 +304,14 @@ impl<'a> SpansRefiner<'a> {
|
|||
// Drain any remaining dups into the output.
|
||||
for dup in self.pending_dups.drain(..) {
|
||||
debug!(" ...adding at least one pending dup={:?}", dup);
|
||||
self.refined_spans.push(dup);
|
||||
self.refined_spans.push(dup.into_refined());
|
||||
}
|
||||
|
||||
// There is usually a final span remaining in `prev` after the loop ends,
|
||||
// so add it to the output as well.
|
||||
if let Some(prev) = self.some_prev.take() {
|
||||
debug!(" AT END, adding last prev={prev:?}");
|
||||
self.refined_spans.push(prev);
|
||||
self.refined_spans.push(prev.into_refined());
|
||||
}
|
||||
|
||||
// Do one last merge pass, to simplify the output.
|
||||
|
@ -274,7 +325,7 @@ impl<'a> SpansRefiner<'a> {
|
|||
}
|
||||
});
|
||||
|
||||
// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage
|
||||
// Remove spans derived from closures, originally added to ensure the coverage
|
||||
// regions for the current function leave room for the closure's own coverage regions
|
||||
// (injected separately, from the closure's own MIR).
|
||||
self.refined_spans.retain(|covspan| !covspan.is_closure);
|
||||
|
@ -282,34 +333,29 @@ impl<'a> SpansRefiner<'a> {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn curr(&self) -> &CoverageSpan {
|
||||
fn curr(&self) -> &CurrCovspan {
|
||||
self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn curr_mut(&mut self) -> &mut CoverageSpan {
|
||||
self.some_curr.as_mut().unwrap_or_else(|| bug!("some_curr is None (curr_mut)"))
|
||||
}
|
||||
|
||||
/// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the
|
||||
/// `curr` coverage span.
|
||||
#[track_caller]
|
||||
fn take_curr(&mut self) -> CoverageSpan {
|
||||
fn take_curr(&mut self) -> CurrCovspan {
|
||||
self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)"))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn prev(&self) -> &CoverageSpan {
|
||||
fn prev(&self) -> &PrevCovspan {
|
||||
self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)"))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn prev_mut(&mut self) -> &mut CoverageSpan {
|
||||
fn prev_mut(&mut self) -> &mut PrevCovspan {
|
||||
self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)"))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn take_prev(&mut self) -> CoverageSpan {
|
||||
fn take_prev(&mut self) -> PrevCovspan {
|
||||
self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)"))
|
||||
}
|
||||
|
||||
|
@ -335,7 +381,7 @@ impl<'a> SpansRefiner<'a> {
|
|||
if last_dup.span.hi() <= self.curr().span.lo() {
|
||||
for dup in self.pending_dups.drain(..) {
|
||||
debug!(" ...adding at least one pending={:?}", dup);
|
||||
self.refined_spans.push(dup);
|
||||
self.refined_spans.push(dup.into_refined());
|
||||
}
|
||||
} else {
|
||||
self.pending_dups.clear();
|
||||
|
@ -343,11 +389,10 @@ impl<'a> SpansRefiner<'a> {
|
|||
assert!(self.pending_dups.is_empty());
|
||||
}
|
||||
|
||||
/// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order.
|
||||
/// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order.
|
||||
fn next_coverage_span(&mut self) -> bool {
|
||||
if let Some(curr) = self.some_curr.take() {
|
||||
self.some_prev = Some(curr);
|
||||
self.prev_original_span = self.curr_original_span;
|
||||
self.some_prev = Some(curr.into_prev());
|
||||
}
|
||||
while let Some(curr) = self.sorted_spans_iter.next() {
|
||||
debug!("FOR curr={:?}", curr);
|
||||
|
@ -362,10 +407,7 @@ impl<'a> SpansRefiner<'a> {
|
|||
closure?); prev={prev:?}",
|
||||
);
|
||||
} else {
|
||||
// Save a copy of the original span for `curr` in case the `CoverageSpan` is changed
|
||||
// by `self.curr_mut().merge_from(prev)`.
|
||||
self.curr_original_span = curr.span;
|
||||
self.some_curr.replace(curr);
|
||||
self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure));
|
||||
self.maybe_flush_pending_dups();
|
||||
return true;
|
||||
}
|
||||
|
@ -388,11 +430,11 @@ impl<'a> SpansRefiner<'a> {
|
|||
let has_post_closure_span = prev.span.hi() > right_cutoff;
|
||||
|
||||
if has_pre_closure_span {
|
||||
let mut pre_closure = self.prev().clone();
|
||||
let mut pre_closure = self.prev().refined_copy();
|
||||
pre_closure.span = pre_closure.span.with_hi(left_cutoff);
|
||||
debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure);
|
||||
|
||||
for mut dup in self.pending_dups.iter().cloned() {
|
||||
for mut dup in self.pending_dups.iter().map(DuplicateCovspan::refined_copy) {
|
||||
dup.span = dup.span.with_hi(left_cutoff);
|
||||
debug!(" ...and at least one pre_closure dup={:?}", dup);
|
||||
self.refined_spans.push(dup);
|
||||
|
@ -402,9 +444,7 @@ impl<'a> SpansRefiner<'a> {
|
|||
}
|
||||
|
||||
if has_post_closure_span {
|
||||
// Mutate `prev.span()` to start after the closure (and discard curr).
|
||||
// (**NEVER** update `prev_original_span` because it affects the assumptions
|
||||
// about how the `CoverageSpan`s are ordered.)
|
||||
// Mutate `prev.span` to start after the closure (and discard curr).
|
||||
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
|
||||
debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev());
|
||||
|
||||
|
@ -413,25 +453,26 @@ impl<'a> SpansRefiner<'a> {
|
|||
dup.span = dup.span.with_lo(right_cutoff);
|
||||
}
|
||||
|
||||
let closure_covspan = self.take_curr(); // Prevent this curr from becoming prev.
|
||||
// Prevent this curr from becoming prev.
|
||||
let closure_covspan = self.take_curr().into_refined();
|
||||
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
|
||||
} else {
|
||||
self.pending_dups.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Called if `curr.span` equals `prev_original_span` (and potentially equal to all
|
||||
/// Called if `curr.span` equals `prev.original_span` (and potentially equal to all
|
||||
/// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed.
|
||||
/// If prev.span() was merged into other spans (with matching BCB, for instance),
|
||||
/// `prev.span.hi()` will be greater than (further right of) `prev_original_span.hi()`.
|
||||
/// `prev.span.hi()` will be greater than (further right of) `prev.original_span.hi()`.
|
||||
/// If prev.span() was split off to the right of a closure, prev.span().lo() will be
|
||||
/// greater than prev_original_span.lo(). The actual span of `prev_original_span` is
|
||||
/// greater than prev.original_span.lo(). The actual span of `prev.original_span` is
|
||||
/// not as important as knowing that `prev()` **used to have the same span** as `curr()`,
|
||||
/// which means their sort order is still meaningful for determining the dominator
|
||||
/// relationship.
|
||||
///
|
||||
/// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if
|
||||
/// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held,
|
||||
/// When two coverage spans have the same `Span`, dominated spans can be discarded; but if
|
||||
/// neither coverage span dominates the other, both (or possibly more than two) are held,
|
||||
/// until their disposition is determined. In this latter case, the `prev` dup is moved into
|
||||
/// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration.
|
||||
fn update_pending_dups(&mut self) {
|
||||
|
@ -439,9 +480,15 @@ impl<'a> SpansRefiner<'a> {
|
|||
let curr_bcb = self.curr().bcb;
|
||||
|
||||
// Equal coverage spans are ordered by dominators before dominated (if any), so it should be
|
||||
// impossible for `curr` to dominate any previous `CoverageSpan`.
|
||||
// impossible for `curr` to dominate any previous coverage span.
|
||||
debug_assert!(!self.basic_coverage_blocks.dominates(curr_bcb, prev_bcb));
|
||||
|
||||
// `prev` is a duplicate of `curr`, so add it to the list of pending dups.
|
||||
// If it dominates `curr`, it will be removed by the subsequent discard step.
|
||||
let prev = self.take_prev().into_dup();
|
||||
debug!(?prev, "adding prev to pending dups");
|
||||
self.pending_dups.push(prev);
|
||||
|
||||
let initial_pending_count = self.pending_dups.len();
|
||||
if initial_pending_count > 0 {
|
||||
self.pending_dups
|
||||
|
@ -454,42 +501,6 @@ impl<'a> SpansRefiner<'a> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.basic_coverage_blocks.dominates(prev_bcb, curr_bcb) {
|
||||
debug!(
|
||||
" different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}",
|
||||
self.prev()
|
||||
);
|
||||
self.cutoff_prev_at_overlapping_curr();
|
||||
// If one span dominates the other, associate the span with the code from the dominated
|
||||
// block only (`curr`), and discard the overlapping portion of the `prev` span. (Note
|
||||
// that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still
|
||||
// be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.)
|
||||
//
|
||||
// For example:
|
||||
// match somenum {
|
||||
// x if x < 1 => { ... }
|
||||
// }...
|
||||
//
|
||||
// The span for the first `x` is referenced by both the pattern block (every time it is
|
||||
// evaluated) and the arm code (only when matched). The counter will be applied only to
|
||||
// the dominated block. This allows coverage to track and highlight things like the
|
||||
// assignment of `x` above, if the branch is matched, making `x` available to the arm
|
||||
// code; and to track and highlight the question mark `?` "try" operator at the end of
|
||||
// a function call returning a `Result`, so the `?` is covered when the function returns
|
||||
// an `Err`, and not counted as covered if the function always returns `Ok`.
|
||||
} else {
|
||||
// Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.)
|
||||
// If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as
|
||||
// well; but if `curr` is added to refined_spans, the `pending_dups` will also be added.
|
||||
debug!(
|
||||
" different bcbs but SAME spans, and neither dominates, so keep curr for \
|
||||
next iter, and, pending upcoming spans (unless overlapping) add prev={:?}",
|
||||
self.prev()
|
||||
);
|
||||
let prev = self.take_prev();
|
||||
self.pending_dups.push(prev);
|
||||
}
|
||||
}
|
||||
|
||||
/// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_
|
||||
|
@ -512,7 +523,7 @@ impl<'a> SpansRefiner<'a> {
|
|||
debug!(" ... no non-overlapping statements to add");
|
||||
} else {
|
||||
debug!(" ... adding modified prev={:?}", self.prev());
|
||||
let prev = self.take_prev();
|
||||
let prev = self.take_prev().into_refined();
|
||||
self.refined_spans.push(prev);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,6 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
|||
use crate::coverage::graph::{
|
||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||
};
|
||||
use crate::coverage::spans::CoverageSpan;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
/// Traverses the MIR body to produce an initial collection of coverage-relevant
|
||||
|
@ -22,7 +21,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
|||
mir_body: &mir::Body<'_>,
|
||||
hir_info: &ExtractedHirInfo,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<CoverageSpan> {
|
||||
) -> Vec<SpanFromMir> {
|
||||
let &ExtractedHirInfo { body_span, .. } = hir_info;
|
||||
|
||||
let mut initial_spans = vec![];
|
||||
|
@ -61,7 +60,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
|||
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
|
||||
});
|
||||
|
||||
initial_spans.into_iter().map(SpanFromMir::into_coverage_span).collect::<Vec<_>>()
|
||||
initial_spans
|
||||
}
|
||||
|
||||
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
|
||||
|
@ -119,10 +118,10 @@ fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
|||
initial_spans.extend(extra_spans);
|
||||
}
|
||||
|
||||
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
|
||||
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
|
||||
// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of
|
||||
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One coverage span is generated
|
||||
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
|
||||
// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
|
||||
// merge some coverage spans, at which point a coverage span may represent multiple
|
||||
// `Statement`s and/or `Terminator`s.)
|
||||
fn bcb_to_initial_coverage_spans<'a, 'tcx>(
|
||||
mir_body: &'a mir::Body<'tcx>,
|
||||
|
@ -316,7 +315,7 @@ fn unexpand_into_body_span_with_prev(
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SpanFromMir {
|
||||
pub(super) struct SpanFromMir {
|
||||
/// A span that has been extracted from MIR and then "un-expanded" back to
|
||||
/// within the current function's `body_span`. After various intermediate
|
||||
/// processing steps, this span is emitted as part of the final coverage
|
||||
|
@ -324,10 +323,10 @@ struct SpanFromMir {
|
|||
///
|
||||
/// With the exception of `fn_sig_span`, this should always be contained
|
||||
/// within `body_span`.
|
||||
span: Span,
|
||||
pub(super) span: Span,
|
||||
visible_macro: Option<Symbol>,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
pub(super) bcb: BasicCoverageBlock,
|
||||
pub(super) is_closure: bool,
|
||||
}
|
||||
|
||||
impl SpanFromMir {
|
||||
|
@ -343,9 +342,4 @@ impl SpanFromMir {
|
|||
) -> Self {
|
||||
Self { span, visible_macro, bcb, is_closure }
|
||||
}
|
||||
|
||||
fn into_coverage_span(self) -> CoverageSpan {
|
||||
let Self { span, visible_macro: _, bcb, is_closure } = self;
|
||||
CoverageSpan::new(span, bcb, is_closure)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -398,7 +398,8 @@ impl<'alloc> Candidates<'alloc> {
|
|||
let candidates = entry.get_mut();
|
||||
Self::vec_filter_candidates(p, candidates, f, at);
|
||||
if candidates.len() == 0 {
|
||||
entry.remove();
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
entry.swap_remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -561,9 +561,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
.ok()?;
|
||||
dest.into()
|
||||
}
|
||||
CastKind::FnPtrToPtr
|
||||
| CastKind::PtrToPtr
|
||||
| CastKind::PointerCoercion(
|
||||
CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
|
||||
let src = self.evaluated[value].as_ref()?;
|
||||
let src = self.ecx.read_immediate(src).ok()?;
|
||||
let to = self.ecx.layout_of(to).ok()?;
|
||||
let ret = self.ecx.ptr_to_ptr(&src, to).ok()?;
|
||||
ret.into()
|
||||
}
|
||||
CastKind::PointerCoercion(
|
||||
ty::adjustment::PointerCoercion::MutToConstPointer
|
||||
| ty::adjustment::PointerCoercion::ArrayToPointer
|
||||
| ty::adjustment::PointerCoercion::UnsafeFnPointer,
|
||||
|
@ -571,8 +576,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
let src = self.evaluated[value].as_ref()?;
|
||||
let src = self.ecx.read_immediate(src).ok()?;
|
||||
let to = self.ecx.layout_of(to).ok()?;
|
||||
let ret = self.ecx.ptr_to_ptr(&src, to).ok()?;
|
||||
ret.into()
|
||||
ImmTy::from_immediate(*src, to).into()
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
|
|
|
@ -265,7 +265,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
|
|||
let body = &tcx.mir_const(def).borrow();
|
||||
|
||||
if body.return_ty().references_error() {
|
||||
tcx.dcx().span_delayed_bug(body.span, "mir_const_qualif: MIR had errors");
|
||||
assert!(tcx.dcx().has_errors().is_some(), "mir_const_qualif: MIR had errors");
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
use rustc_type_ir::visit::TypeVisitableExt;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, ConstTy,
|
||||
InferCtxtLike, Interner, IntoKind, PlaceholderLike,
|
||||
|
@ -62,8 +63,8 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc
|
|||
|
||||
let value = value.fold_with(&mut canonicalizer);
|
||||
// FIXME: Restore these assertions. Should we uplift type flags?
|
||||
// assert!(!value.has_infer(), "unexpected infer in {value:?}");
|
||||
// assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
|
||||
assert!(!value.has_infer(), "unexpected infer in {value:?}");
|
||||
assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
|
||||
|
||||
let (max_universe, variables) = canonicalizer.finalize();
|
||||
|
||||
|
|
|
@ -900,7 +900,7 @@ impl<'a> Parser<'a> {
|
|||
// fn foo() -> Foo {
|
||||
// field: value,
|
||||
// }
|
||||
info!(?maybe_struct_name, ?self.token);
|
||||
debug!(?maybe_struct_name, ?self.token);
|
||||
let mut snapshot = self.create_snapshot_for_diagnostic();
|
||||
let path = Path {
|
||||
segments: ThinVec::new(),
|
||||
|
|
|
@ -957,8 +957,9 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
|||
// available as we'd like it to be.
|
||||
// FIXME: only remove `libc` when `stdbuild` is active.
|
||||
// FIXME: remove special casing for `test`.
|
||||
remaining_lib_features.remove(&sym::libc);
|
||||
remaining_lib_features.remove(&sym::test);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
remaining_lib_features.swap_remove(&sym::libc);
|
||||
remaining_lib_features.swap_remove(&sym::test);
|
||||
|
||||
/// For each feature in `defined_features`..
|
||||
///
|
||||
|
@ -996,7 +997,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
|||
unnecessary_stable_feature_lint(tcx, *span, feature, since);
|
||||
}
|
||||
}
|
||||
remaining_lib_features.remove(&feature);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
remaining_lib_features.swap_remove(&feature);
|
||||
|
||||
// `feature` is the feature doing the implying, but `implied_by` is the feature with
|
||||
// the attribute that establishes this relationship. `implied_by` is guaranteed to be a
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::query::plumbing::CycleError;
|
|||
use crate::query::DepKind;
|
||||
use crate::query::{QueryContext, QueryStackFrame};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{DiagCtxt, Diagnostic, DiagnosticBuilder, Level};
|
||||
use rustc_errors::{DiagCtxt, DiagnosticBuilder};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
|
@ -628,15 +628,15 @@ pub fn print_query_stack<Qcx: QueryContext>(
|
|||
};
|
||||
if Some(count_printed) < num_frames || num_frames.is_none() {
|
||||
// Only print to stderr as many stack frames as `num_frames` when present.
|
||||
let mut diag = Diagnostic::new(
|
||||
Level::FailureNote,
|
||||
format!(
|
||||
"#{} [{:?}] {}",
|
||||
count_printed, query_info.query.dep_kind, query_info.query.description
|
||||
),
|
||||
);
|
||||
diag.span = query_info.job.span.into();
|
||||
dcx.force_print_diagnostic(diag);
|
||||
// FIXME: needs translation
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
dcx.struct_failure_note(format!(
|
||||
"#{} [{:?}] {}",
|
||||
count_printed, query_info.query.dep_kind, query_info.query.description
|
||||
))
|
||||
.with_span(query_info.job.span)
|
||||
.emit();
|
||||
count_printed += 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -429,16 +429,16 @@ where
|
|||
let formatter = query.format_value();
|
||||
if old_hash != new_hash {
|
||||
// We have an inconsistency. This can happen if one of the two
|
||||
// results is tainted by errors. In this case, delay a bug to
|
||||
// ensure compilation is doomed.
|
||||
qcx.dep_context().sess().dcx().delayed_bug(format!(
|
||||
// results is tainted by errors.
|
||||
assert!(
|
||||
qcx.dep_context().sess().dcx().has_errors().is_some(),
|
||||
"Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
|
||||
computed={:#?}\nfed={:#?}",
|
||||
query.dep_kind(),
|
||||
key,
|
||||
formatter(&result),
|
||||
formatter(&cached_result),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,8 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
|
|||
} else {
|
||||
// This trait import is definitely used, in a way other than
|
||||
// method resolution.
|
||||
self.r.maybe_unused_trait_imports.remove(&def_id);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
self.r.maybe_unused_trait_imports.swap_remove(&def_id);
|
||||
if let Some(i) = self.unused_imports.get_mut(&self.base_id) {
|
||||
i.unused.remove(&id);
|
||||
}
|
||||
|
|
|
@ -460,7 +460,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
|||
return (err, Vec::new());
|
||||
}
|
||||
|
||||
let (found, candidates) = self.try_lookup_name_relaxed(
|
||||
let (found, mut candidates) = self.try_lookup_name_relaxed(
|
||||
&mut err,
|
||||
source,
|
||||
path,
|
||||
|
@ -473,10 +473,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
|||
return (err, candidates);
|
||||
}
|
||||
|
||||
let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
|
||||
if self.suggest_shadowed(&mut err, source, path, following_seg, span) {
|
||||
// if there is already a shadowed name, don'suggest candidates for importing
|
||||
candidates.clear();
|
||||
}
|
||||
|
||||
// if we have suggested using pattern matching, then don't add needless suggestions
|
||||
// for typos.
|
||||
let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
|
||||
fallback |= self.suggest_typo(&mut err, source, path, following_seg, span, &base_error);
|
||||
|
||||
if fallback {
|
||||
|
@ -872,25 +874,6 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
|||
let ident_span = path.last().map_or(span, |ident| ident.ident.span);
|
||||
let typo_sugg =
|
||||
self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);
|
||||
let is_in_same_file = &|sp1, sp2| {
|
||||
let source_map = self.r.tcx.sess.source_map();
|
||||
let file1 = source_map.span_to_filename(sp1);
|
||||
let file2 = source_map.span_to_filename(sp2);
|
||||
file1 == file2
|
||||
};
|
||||
// print 'you might have meant' if the candidate is (1) is a shadowed name with
|
||||
// accessible definition and (2) either defined in the same crate as the typo
|
||||
// (could be in a different file) or introduced in the same file as the typo
|
||||
// (could belong to a different crate)
|
||||
if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg
|
||||
&& res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))
|
||||
{
|
||||
err.span_label(
|
||||
sugg_span,
|
||||
format!("you might have meant to refer to this {}", res.descr()),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
let mut fallback = false;
|
||||
let typo_sugg = typo_sugg.to_opt_suggestion();
|
||||
if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {
|
||||
|
@ -918,6 +901,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
|||
fallback
|
||||
}
|
||||
|
||||
fn suggest_shadowed(
|
||||
&mut self,
|
||||
err: &mut Diagnostic,
|
||||
source: PathSource<'_>,
|
||||
path: &[Segment],
|
||||
following_seg: Option<&Segment>,
|
||||
span: Span,
|
||||
) -> bool {
|
||||
let is_expected = &|res| source.is_expected(res);
|
||||
let typo_sugg =
|
||||
self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);
|
||||
let is_in_same_file = &|sp1, sp2| {
|
||||
let source_map = self.r.tcx.sess.source_map();
|
||||
let file1 = source_map.span_to_filename(sp1);
|
||||
let file2 = source_map.span_to_filename(sp2);
|
||||
file1 == file2
|
||||
};
|
||||
// print 'you might have meant' if the candidate is (1) is a shadowed name with
|
||||
// accessible definition and (2) either defined in the same crate as the typo
|
||||
// (could be in a different file) or introduced in the same file as the typo
|
||||
// (could belong to a different crate)
|
||||
if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg
|
||||
&& res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))
|
||||
{
|
||||
err.span_label(
|
||||
sugg_span,
|
||||
format!("you might have meant to refer to this {}", res.descr()),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn err_code_special_cases(
|
||||
&mut self,
|
||||
err: &mut Diagnostic,
|
||||
|
|
|
@ -1435,12 +1435,16 @@ impl CheckCfg {
|
|||
//
|
||||
// When adding a new config here you should also update
|
||||
// `tests/ui/check-cfg/well-known-values.rs`.
|
||||
//
|
||||
// Don't forget to update `src/doc/unstable-book/src/compiler-flags/check-cfg.md`
|
||||
// in the unstable book as well!
|
||||
|
||||
ins!(sym::debug_assertions, no_values);
|
||||
|
||||
// These three are never set by rustc, but we set them anyway: they
|
||||
// should not trigger a lint because `cargo doc`, `cargo test`, and
|
||||
// `cargo miri run` (respectively) can set them.
|
||||
// These four are never set by rustc, but we set them anyway: they
|
||||
// should not trigger a lint because `cargo clippy`, `cargo doc`,
|
||||
// `cargo test` and `cargo miri run` (respectively) can set them.
|
||||
ins!(sym::clippy, no_values);
|
||||
ins!(sym::doc, no_values);
|
||||
ins!(sym::doctest, no_values);
|
||||
ins!(sym::miri, no_values);
|
||||
|
|
|
@ -322,10 +322,9 @@ impl Session {
|
|||
}
|
||||
}
|
||||
|
||||
/// Used for code paths of expensive computations that should only take place when
|
||||
/// warnings or errors are emitted. If no messages are emitted ("good path"), then
|
||||
/// it's likely a bug.
|
||||
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
||||
/// Record the fact that we called `trimmed_def_paths`, and do some
|
||||
/// checking about whether its cost was justified.
|
||||
pub fn record_trimmed_def_paths(&self) {
|
||||
if self.opts.unstable_opts.print_type_sizes
|
||||
|| self.opts.unstable_opts.query_dep_graph
|
||||
|| self.opts.unstable_opts.dump_mir.is_some()
|
||||
|
@ -336,7 +335,7 @@ impl Session {
|
|||
return;
|
||||
}
|
||||
|
||||
self.dcx().good_path_delayed_bug(msg)
|
||||
self.dcx().set_must_produce_diag()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -546,8 +545,8 @@ impl Session {
|
|||
if fuel.remaining == 0 && !fuel.out_of_fuel {
|
||||
if self.dcx().can_emit_warnings() {
|
||||
// We only call `msg` in case we can actually emit warnings.
|
||||
// Otherwise, this could cause a `good_path_delayed_bug` to
|
||||
// trigger (issue #79546).
|
||||
// Otherwise, this could cause a `must_produce_diag` ICE
|
||||
// (issue #79546).
|
||||
self.dcx().emit_warn(errors::OptimisationFuelExhausted { msg: msg() });
|
||||
}
|
||||
fuel.out_of_fuel = true;
|
||||
|
|
|
@ -3,33 +3,27 @@
|
|||
//! of our more general approach to "lazy normalization".
|
||||
//!
|
||||
//! This is done by first normalizing both sides of the goal, ending up in
|
||||
//! either a concrete type, rigid projection, opaque, or an infer variable.
|
||||
//! either a concrete type, rigid alias, or an infer variable.
|
||||
//! These are related further according to the rules below:
|
||||
//!
|
||||
//! (1.) If we end up with a rigid projection and a rigid projection, then we
|
||||
//! relate those projections structurally.
|
||||
//! (1.) If we end up with two rigid aliases, then we relate them structurally.
|
||||
//!
|
||||
//! (2.) If we end up with a rigid projection and an alias, then the opaque will
|
||||
//! have its hidden type defined to be that rigid projection.
|
||||
//!
|
||||
//! (3.) If we end up with an opaque and an opaque, then we assemble two
|
||||
//! candidates, one defining the LHS to be the hidden type of the RHS, and vice
|
||||
//! versa.
|
||||
//!
|
||||
//! (4.) If we end up with an infer var and an opaque or rigid projection, then
|
||||
//! (2.) If we end up with an infer var and a rigid alias, then
|
||||
//! we assign the alias to the infer var.
|
||||
//!
|
||||
//! (5.) If we end up with an opaque and a rigid (non-projection) type, then we
|
||||
//! define the hidden type of the opaque to be the rigid type.
|
||||
//!
|
||||
//! (6.) Otherwise, if we end with two rigid (non-projection) or infer types,
|
||||
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
|
||||
//! relate them structurally.
|
||||
//!
|
||||
//! Subtle: when relating an opaque to another type, we emit a
|
||||
//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque.
|
||||
//! This nested goal starts out as ambiguous and does not actually define the opaque.
|
||||
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
|
||||
//! `NormalizesTo` goal, at which point the opaque is actually defined.
|
||||
|
||||
use super::{EvalCtxt, GoalSource};
|
||||
use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
|
@ -59,37 +53,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
(Some(alias), None) => {
|
||||
(Some(_), None) => {
|
||||
if rhs.is_infer() {
|
||||
self.relate(param_env, lhs, variance, rhs)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else if alias.is_opaque(tcx) {
|
||||
// FIXME: This doesn't account for variance.
|
||||
self.define_opaque(param_env, alias, rhs)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
(None, Some(alias)) => {
|
||||
(None, Some(_)) => {
|
||||
if lhs.is_infer() {
|
||||
self.relate(param_env, lhs, variance, rhs)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else if alias.is_opaque(tcx) {
|
||||
// FIXME: This doesn't account for variance.
|
||||
self.define_opaque(param_env, alias, lhs)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
(Some(alias_lhs), Some(alias_rhs)) => {
|
||||
self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs)
|
||||
self.relate(param_env, alias_lhs, variance, alias_rhs)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
|
||||
/// Normalize the `term` to equate it later. This does not define opaque types.
|
||||
/// Normalize the `term` to equate it later.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn try_normalize_term(
|
||||
&mut self,
|
||||
|
@ -98,10 +87,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
|
||||
match term.unpack() {
|
||||
ty::TermKind::Ty(ty) => {
|
||||
// We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`.
|
||||
Ok(self
|
||||
.try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty)
|
||||
.map(Into::into))
|
||||
Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into))
|
||||
}
|
||||
ty::TermKind::Const(_) => {
|
||||
if let Some(alias) = term.to_alias_ty(self.tcx()) {
|
||||
|
@ -119,51 +105,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn define_opaque(
|
||||
fn try_normalize_ty_recur(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
opaque: ty::AliasTy<'tcx>,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
self.add_goal(
|
||||
GoalSource::Misc,
|
||||
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }),
|
||||
);
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
fn relate_rigid_alias_or_opaque(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
lhs: ty::AliasTy<'tcx>,
|
||||
variance: ty::Variance,
|
||||
rhs: ty::AliasTy<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let tcx = self.tcx();
|
||||
let mut candidates = vec![];
|
||||
if lhs.is_opaque(tcx) {
|
||||
candidates.extend(
|
||||
self.probe_misc_candidate("define-lhs-opaque")
|
||||
.enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())),
|
||||
);
|
||||
depth: usize,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if !self.tcx().recursion_limit().value_within_limit(depth) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if rhs.is_opaque(tcx) {
|
||||
candidates.extend(
|
||||
self.probe_misc_candidate("define-rhs-opaque")
|
||||
.enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())),
|
||||
let ty::Alias(_, alias) = *ty.kind() else {
|
||||
return Some(ty);
|
||||
};
|
||||
|
||||
match self.commit_if_ok(|this| {
|
||||
let normalized_ty = this.next_ty_infer();
|
||||
let normalizes_to_goal = Goal::new(
|
||||
this.tcx(),
|
||||
param_env,
|
||||
ty::NormalizesTo { alias, term: normalized_ty.into() },
|
||||
);
|
||||
}
|
||||
|
||||
candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| {
|
||||
ecx.relate(param_env, lhs, variance, rhs)?;
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}));
|
||||
|
||||
if let Some(result) = self.try_merge_responses(&candidates) {
|
||||
Ok(result)
|
||||
} else {
|
||||
self.flounder(&candidates)
|
||||
this.add_goal(GoalSource::Misc, normalizes_to_goal);
|
||||
this.try_evaluate_added_goals()?;
|
||||
let ty = this.resolve_vars_if_possible(normalized_ty);
|
||||
Ok(this.try_normalize_ty_recur(param_env, depth + 1, ty))
|
||||
}) {
|
||||
Ok(ty) => ty,
|
||||
Err(NoSolution) => Some(ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,11 +276,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
&mut self,
|
||||
goal: Goal<'tcx, G>,
|
||||
) -> Vec<Candidate<'tcx>> {
|
||||
let Some(normalized_self_ty) =
|
||||
self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
|
||||
let Ok(normalized_self_ty) =
|
||||
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
|
||||
else {
|
||||
debug!("overflow while evaluating self type");
|
||||
return self.forced_ambiguity(MaybeCause::Overflow);
|
||||
return vec![];
|
||||
};
|
||||
|
||||
if normalized_self_ty.is_ty_var() {
|
||||
|
@ -338,6 +337,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
let mut consider_impls_for_simplified_type = |simp| {
|
||||
if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
|
||||
for &impl_def_id in impls_for_type {
|
||||
// For every `default impl`, there's always a non-default `impl`
|
||||
// that will *also* apply. There's no reason to register a candidate
|
||||
// for this impl, since it is *not* proof that the trait goal holds.
|
||||
if tcx.defaultness(impl_def_id).is_default() {
|
||||
return;
|
||||
}
|
||||
|
||||
match G::consider_impl_candidate(self, goal, impl_def_id) {
|
||||
Ok(candidate) => candidates.push(candidate),
|
||||
Err(NoSolution) => (),
|
||||
|
@ -441,6 +447,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
let tcx = self.tcx();
|
||||
let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
|
||||
for &impl_def_id in trait_impls.blanket_impls() {
|
||||
// For every `default impl`, there's always a non-default `impl`
|
||||
// that will *also* apply. There's no reason to register a candidate
|
||||
// for this impl, since it is *not* proof that the trait goal holds.
|
||||
if tcx.defaultness(impl_def_id).is_default() {
|
||||
return;
|
||||
}
|
||||
|
||||
match G::consider_impl_candidate(self, goal, impl_def_id) {
|
||||
Ok(candidate) => candidates.push(candidate),
|
||||
Err(NoSolution) => (),
|
||||
|
@ -635,19 +648,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
match self.try_normalize_ty(goal.param_env, alias_ty.self_ty()) {
|
||||
// Recurse on the self type of the projection.
|
||||
Some(next_self_ty) => {
|
||||
self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates);
|
||||
}
|
||||
// Bail if we overflow when normalizing, adding an ambiguous candidate.
|
||||
None => {
|
||||
if let Ok(result) =
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
|
||||
{
|
||||
candidates.push(Candidate { source: CandidateSource::AliasBound, result });
|
||||
}
|
||||
// Recurse on the self type of the projection.
|
||||
match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) {
|
||||
Ok(next_self_ty) => {
|
||||
self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates)
|
||||
}
|
||||
Err(NoSolution) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -857,19 +863,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
let tcx = self.tcx();
|
||||
let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
|
||||
let trait_ref = goal.predicate.trait_ref(tcx);
|
||||
#[derive(Debug)]
|
||||
struct Overflow;
|
||||
let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
|
||||
Some(ty) => Ok(ty),
|
||||
None => Err(Overflow),
|
||||
};
|
||||
let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty);
|
||||
|
||||
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
|
||||
Err(Overflow) => {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
|
||||
}
|
||||
Ok(Ok(())) => Err(NoSolution),
|
||||
Ok(Err(_)) => {
|
||||
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? {
|
||||
Ok(()) => Err(NoSolution),
|
||||
Err(_) => {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,13 @@
|
|||
//! about it on zulip.
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
|
||||
use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::infer::canonical::CanonicalVarInfos;
|
||||
use rustc_middle::traits::solve::{
|
||||
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
|
||||
QueryResult, Response,
|
||||
};
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex};
|
||||
use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex};
|
||||
use rustc_middle::ty::{
|
||||
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
|
||||
};
|
||||
|
@ -267,71 +265,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
|
||||
}
|
||||
|
||||
/// Normalize a type when it is structually matched on.
|
||||
/// Normalize a type for when it is structurally matched on.
|
||||
///
|
||||
/// In nearly all cases this function must be used before matching on a type.
|
||||
/// This function is necessary in nearly all cases before matching on a type.
|
||||
/// Not doing so is likely to be incomplete and therefore unsound during
|
||||
/// coherence.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn try_normalize_ty(
|
||||
fn structurally_normalize_ty(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty)
|
||||
}
|
||||
|
||||
fn try_normalize_ty_recur(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
define_opaque_types: DefineOpaqueTypes,
|
||||
depth: usize,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if !self.tcx().recursion_limit().value_within_limit(depth) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty::Alias(kind, alias) = *ty.kind() else {
|
||||
return Some(ty);
|
||||
};
|
||||
|
||||
// We do no always define opaque types eagerly to allow non-defining uses
|
||||
// in the defining scope. However, if we can unify this opaque to an existing
|
||||
// opaque, then we should attempt to eagerly reveal the opaque, and we fall
|
||||
// through.
|
||||
if let DefineOpaqueTypes::No = define_opaque_types
|
||||
&& let Reveal::UserFacing = param_env.reveal()
|
||||
&& let ty::Opaque = kind
|
||||
&& let Some(def_id) = alias.def_id.as_local()
|
||||
&& self.can_define_opaque_ty(def_id)
|
||||
{
|
||||
if self
|
||||
.unify_existing_opaque_tys(
|
||||
param_env,
|
||||
OpaqueTypeKey { def_id, args: alias.args },
|
||||
self.next_ty_infer(),
|
||||
)
|
||||
.is_empty()
|
||||
{
|
||||
return Some(ty);
|
||||
}
|
||||
}
|
||||
|
||||
match self.commit_if_ok(|this| {
|
||||
let normalized_ty = this.next_ty_infer();
|
||||
let normalizes_to_goal = Goal::new(
|
||||
this.tcx(),
|
||||
) -> Result<Ty<'tcx>, NoSolution> {
|
||||
if let ty::Alias(..) = ty.kind() {
|
||||
let normalized_ty = self.next_ty_infer();
|
||||
let alias_relate_goal = Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::NormalizesTo { alias, term: normalized_ty.into() },
|
||||
ty::PredicateKind::AliasRelate(
|
||||
ty.into(),
|
||||
normalized_ty.into(),
|
||||
AliasRelationDirection::Equate,
|
||||
),
|
||||
);
|
||||
this.add_goal(GoalSource::Misc, normalizes_to_goal);
|
||||
this.try_evaluate_added_goals()?;
|
||||
let ty = this.resolve_vars_if_possible(normalized_ty);
|
||||
Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty))
|
||||
}) {
|
||||
Ok(ty) => ty,
|
||||
Err(NoSolution) => Some(ty),
|
||||
self.add_goal(GoalSource::Misc, alias_relate_goal);
|
||||
self.try_evaluate_added_goals()?;
|
||||
Ok(self.resolve_vars_if_possible(normalized_ty))
|
||||
} else {
|
||||
Ok(ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,21 +58,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
let expected = match self.try_normalize_ty(goal.param_env, expected) {
|
||||
Some(ty) => {
|
||||
if ty.is_ty_var() {
|
||||
return self.evaluate_added_goals_and_make_canonical_response(
|
||||
Certainty::AMBIGUOUS,
|
||||
);
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
|
||||
}
|
||||
};
|
||||
let expected = self.structurally_normalize_ty(goal.param_env, expected)?;
|
||||
if expected.is_ty_var() {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
||||
}
|
||||
|
||||
// Otherwise, define a new opaque type
|
||||
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
|
||||
|
|
|
@ -584,11 +584,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|||
let a_ty = goal.predicate.self_ty();
|
||||
// We need to normalize the b_ty since it's matched structurally
|
||||
// in the other functions below.
|
||||
let b_ty = match ecx
|
||||
.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
|
||||
{
|
||||
Some(b_ty) => b_ty,
|
||||
None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
|
||||
let Ok(b_ty) = ecx.structurally_normalize_ty(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref.args.type_at(1),
|
||||
) else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
|
||||
|
|
|
@ -524,13 +524,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
|||
if let Entry::Occupied(v) = vid_map.entry(*smaller) {
|
||||
let smaller_deps = v.into_mut();
|
||||
smaller_deps.larger.insert(*larger);
|
||||
smaller_deps.larger.remove(&target);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
smaller_deps.larger.swap_remove(&target);
|
||||
}
|
||||
|
||||
if let Entry::Occupied(v) = vid_map.entry(*larger) {
|
||||
let larger_deps = v.into_mut();
|
||||
larger_deps.smaller.insert(*smaller);
|
||||
larger_deps.smaller.remove(&target);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
larger_deps.smaller.swap_remove(&target);
|
||||
}
|
||||
}
|
||||
(&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => {
|
||||
|
@ -543,13 +545,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
|||
if let Entry::Occupied(v) = vid_map.entry(*smaller) {
|
||||
let smaller_deps = v.into_mut();
|
||||
smaller_deps.larger.insert(*larger);
|
||||
smaller_deps.larger.remove(&target);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
smaller_deps.larger.swap_remove(&target);
|
||||
}
|
||||
|
||||
if let Entry::Occupied(v) = vid_map.entry(*larger) {
|
||||
let larger_deps = v.into_mut();
|
||||
larger_deps.smaller.insert(*smaller);
|
||||
larger_deps.smaller.remove(&target);
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
larger_deps.smaller.swap_remove(&target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,9 +62,10 @@ pub fn is_const_evaluatable<'tcx>(
|
|||
|
||||
match unexpanded_ct.kind() {
|
||||
ty::ConstKind::Expr(_) => {
|
||||
// FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but
|
||||
// currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it
|
||||
// is evaluatable or not. For now we just ICE until this is implemented.
|
||||
// FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete,
|
||||
// but currently it is not possible to evaluate `ConstKind::Expr` so we are unable
|
||||
// to tell if it is evaluatable or not. For now we just ICE until this is
|
||||
// implemented.
|
||||
Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
"evaluating `ConstKind::Expr` is not currently supported",
|
||||
|
|
|
@ -236,9 +236,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// It could be that we don't report an error because we have seen an `ErrorReported` from another source.
|
||||
// We should probably be able to fix most of these, but some are delayed bugs that get a proper error
|
||||
// after this function.
|
||||
// It could be that we don't report an error because we have seen an `ErrorReported` from
|
||||
// another source. We should probably be able to fix most of these, but some are delayed
|
||||
// bugs that get a proper error after this function.
|
||||
reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
|
||||
}
|
||||
|
||||
|
@ -519,7 +519,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
trait_ref,
|
||||
span,
|
||||
) {
|
||||
GetSafeTransmuteErrorAndReason::Silent => return self.dcx().span_delayed_bug(span, "silent safe transmute error"),
|
||||
GetSafeTransmuteErrorAndReason::Silent => {
|
||||
return self.dcx().span_delayed_bug(
|
||||
span, "silent safe transmute error"
|
||||
);
|
||||
}
|
||||
GetSafeTransmuteErrorAndReason::Error {
|
||||
err_msg,
|
||||
safe_transmute_explanation,
|
||||
|
@ -3112,10 +3116,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
obligation.param_env,
|
||||
trait_ref.args.const_at(3),
|
||||
) else {
|
||||
span_bug!(
|
||||
self.dcx().span_delayed_bug(
|
||||
span,
|
||||
"Unable to construct rustc_transmute::Assume where it was previously possible"
|
||||
"Unable to construct rustc_transmute::Assume where it was previously possible",
|
||||
);
|
||||
return GetSafeTransmuteErrorAndReason::Silent;
|
||||
};
|
||||
|
||||
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
|
||||
|
|
|
@ -555,7 +555,7 @@ fn virtual_call_violations_for_method<'tcx>(
|
|||
|
||||
// NOTE: This check happens last, because it results in a lint, and not a
|
||||
// hard error.
|
||||
if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| {
|
||||
if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, _span)| {
|
||||
// dyn Trait is okay:
|
||||
//
|
||||
// trait Trait {
|
||||
|
@ -594,7 +594,10 @@ fn virtual_call_violations_for_method<'tcx>(
|
|||
// would already have reported an error at the definition of the
|
||||
// auto trait.
|
||||
if pred_trait_ref.args.len() != 1 {
|
||||
tcx.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters");
|
||||
assert!(
|
||||
tcx.dcx().has_errors().is_some(),
|
||||
"auto traits cannot have generic parameters"
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -566,6 +566,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// For every `default impl`, there's always a non-default `impl`
|
||||
// that will *also* apply. There's no reason to register a candidate
|
||||
// for this impl, since it is *not* proof that the trait goal holds.
|
||||
if self.tcx().defaultness(impl_def_id).is_default() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.reject_fn_ptr_impls(
|
||||
impl_def_id,
|
||||
obligation,
|
||||
|
|
|
@ -192,13 +192,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
&mut obligations,
|
||||
);
|
||||
|
||||
obligations.extend(self.infcx.commit_if_ok(|_| {
|
||||
obligations.extend(
|
||||
self.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate)
|
||||
.map(|InferOk { obligations, .. }| obligations)
|
||||
.map_err(|_| Unimplemented)
|
||||
})?);
|
||||
.map_err(|_| Unimplemented)?,
|
||||
);
|
||||
|
||||
// FIXME(compiler-errors): I don't think this is needed.
|
||||
if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() {
|
||||
|
@ -532,13 +532,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
&mut nested,
|
||||
);
|
||||
|
||||
nested.extend(self.infcx.commit_if_ok(|_| {
|
||||
nested.extend(
|
||||
self.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref)
|
||||
.map(|InferOk { obligations, .. }| obligations)
|
||||
.map_err(|_| Unimplemented)
|
||||
})?);
|
||||
.map_err(|_| Unimplemented)?,
|
||||
);
|
||||
|
||||
// Check supertraits hold. This is so that their associated type bounds
|
||||
// will be checked in the code below.
|
||||
|
|
|
@ -1605,6 +1605,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
on_ambiguity: impl FnOnce(),
|
||||
) -> ControlFlow<T, ()> {
|
||||
let mut idx = 0;
|
||||
let mut in_parent_alias_type = false;
|
||||
|
||||
loop {
|
||||
let (kind, alias_ty) = match *self_ty.kind() {
|
||||
ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
|
||||
|
@ -1618,6 +1620,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
for bound in
|
||||
self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
|
||||
{
|
||||
// HACK: On subsequent recursions, we only care about bounds that don't
|
||||
// share the same type as `self_ty`. This is because for truly rigid
|
||||
// projections, we will never be able to equate, e.g. `<T as Tr>::A`
|
||||
// with `<<T as Tr>::A as Tr>::A`.
|
||||
if in_parent_alias_type {
|
||||
match bound.kind().skip_binder() {
|
||||
ty::ClauseKind::Trait(tr) if tr.self_ty() == self_ty => continue,
|
||||
ty::ClauseKind::Projection(p) if p.self_ty() == self_ty => continue,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for_each(self, bound, idx)?;
|
||||
idx += 1;
|
||||
}
|
||||
|
@ -1627,6 +1641,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
} else {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
in_parent_alias_type = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -522,7 +522,8 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
|
|||
for (p, _) in predicates {
|
||||
if let Some(poly_trait_ref) = p.as_trait_clause() {
|
||||
if Some(poly_trait_ref.def_id()) == sized_trait {
|
||||
types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue