Merge from rustc

This commit is contained in:
Ralf Jung 2024-02-14 19:08:48 +01:00
commit 7a086ac887
525 changed files with 6884 additions and 3609 deletions

2
.gitmodules vendored
View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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" }

View file

@ -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 { .. } => {

View file

@ -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()),
}
}

View file

@ -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.
//
// ```

View file

@ -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 &param.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);
}
}
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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)]

View file

@ -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 }

View file

@ -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

View file

@ -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(),
);
}

View file

@ -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()) }
}
}

View file

@ -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");
}

View file

@ -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);

View file

@ -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,

View file

@ -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

View file

@ -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);
}
}

View file

@ -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),

View file

@ -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 {

View file

@ -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;

View file

@ -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,

View file

@ -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",

View file

@ -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)

View file

@ -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

View file

@ -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(()),
}
}

View file

@ -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)
})
})

View file

@ -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));
}
}

View file

@ -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)?;

View file

@ -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);
}
})
}

View file

@ -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(),
);
}

View file

@ -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)

View file

@ -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))
}

View file

@ -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,
);
}
}

View file

@ -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);

View file

@ -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();
}
}
}

View file

@ -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()

View file

@ -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);

View file

@ -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 })
}
}

View file

@ -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 })
}
}

View file

@ -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()

View file

@ -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:#?}",
);
}

View file

@ -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()
}

View file

@ -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(_) => {}
}

View file

@ -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:?}"),

View file

@ -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![])
}
}

View file

@ -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;
}
}
}

View file

@ -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()))

View file

@ -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(

View file

@ -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

View file

@ -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,

View file

@ -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);
}

View file

@ -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

View file

@ -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).

View file

@ -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));

View file

@ -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

View file

@ -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.

View file

@ -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 (`'_`).

View file

@ -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

View file

@ -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.

View file

@ -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(

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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 } => {

View file

@ -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);

View file

@ -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(),

View file

@ -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(..) => {

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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();
}
}

View file

@ -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,
},

View file

@ -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();
}

View file

@ -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();

View file

@ -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(),

View file

@ -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

View file

@ -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;
}

View file

@ -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),
));
);
}
}
}

View file

@ -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);
}

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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),
}
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}

View file

@ -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)?;

View file

@ -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));

View file

@ -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);
}
}
}

View file

@ -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",

View file

@ -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(

View file

@ -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;
}

View file

@ -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,

View file

@ -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.

View file

@ -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;
}
}

View file

@ -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