1
Fork 0

Auto merge of #111287 - matthiaskrgr:rollup-9lzax2c, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #110577 (Use fulfillment to check `Drop` impl compatibility)
 - #110610 (Add Terminator conversion from MIR to SMIR, part #1)
 - #110985 (Fix spans in LLVM-generated inline asm errors)
 - #110989 (Make the BUG_REPORT_URL configurable by tools )
 - #111167 (debuginfo: split method declaration and definition)
 - #111230 (add hint for =< as <=)
 - #111279 (More robust debug assertions for `Instance::resolve` on built-in traits with non-standard trait items)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-05-06 14:16:55 +00:00
commit 905d5a38d6
42 changed files with 1064 additions and 410 deletions

View file

@ -4093,6 +4093,7 @@ dependencies = [
name = "rustc_smir"
version = "0.0.0"
dependencies = [
"rustc_hir",
"rustc_middle",
"rustc_span",
"tracing",

View file

@ -322,7 +322,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
let tcx = self.tcx;
let def_id = instance.def_id();
let containing_scope = get_containing_scope(self, instance);
let (containing_scope, is_method) = get_containing_scope(self, instance);
let span = tcx.def_span(def_id);
let loc = self.lookup_debug_loc(span.lo());
let file_metadata = file_metadata(self, &loc.file);
@ -378,8 +378,29 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
}
unsafe {
return llvm::LLVMRustDIBuilderCreateFunction(
// When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because
// LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition.
// When we use this `decl` below, the subprogram definition gets created at the CU level
// with a DW_AT_specification pointing back to the type's declaration.
let decl = is_method.then(|| unsafe {
llvm::LLVMRustDIBuilderCreateMethod(
DIB(self),
containing_scope,
name.as_ptr().cast(),
name.len(),
linkage_name.as_ptr().cast(),
linkage_name.len(),
file_metadata,
loc.line,
function_type_metadata,
flags,
spflags & !DISPFlags::SPFlagDefinition,
template_parameters,
)
});
return unsafe {
llvm::LLVMRustDIBuilderCreateFunction(
DIB(self),
containing_scope,
name.as_ptr().cast(),
@ -394,9 +415,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
spflags,
maybe_definition_llfn,
template_parameters,
None,
);
}
decl,
)
};
fn get_function_signature<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
@ -493,14 +514,16 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
names
}
/// Returns a scope, plus `true` if that's a type scope for "class" methods,
/// otherwise `false` for plain namespace scopes.
fn get_containing_scope<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
instance: Instance<'tcx>,
) -> &'ll DIScope {
) -> (&'ll DIScope, bool) {
// First, let's see if this is a method within an inherent impl. Because
// if yes, we want to make the result subroutine DIE a child of the
// subroutine's self-type.
let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| {
if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) {
// If the method does *not* belong to a trait, proceed
if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
@ -511,39 +534,33 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
// Only "class" methods are generally understood by LLVM,
// so avoid methods on other types (e.g., `<*mut T>::null`).
match impl_self_ty.kind() {
ty::Adt(def, ..) if !def.is_box() => {
// Again, only create type information if full debuginfo is enabled
if cx.sess().opts.debuginfo == DebugInfo::Full
&& !impl_self_ty.has_param()
{
Some(type_di_node(cx, impl_self_ty))
} else {
Some(namespace::item_namespace(cx, def.did()))
}
if let ty::Adt(def, ..) = impl_self_ty.kind() && !def.is_box() {
// Again, only create type information if full debuginfo is enabled
if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.has_param()
{
return (type_di_node(cx, impl_self_ty), true);
} else {
return (namespace::item_namespace(cx, def.did()), false);
}
_ => None,
}
} else {
// For trait method impls we still use the "parallel namespace"
// strategy
None
}
});
}
self_type.unwrap_or_else(|| {
namespace::item_namespace(
cx,
DefId {
krate: instance.def_id().krate,
index: cx
.tcx
.def_key(instance.def_id())
.parent
.expect("get_containing_scope: missing parent?"),
},
)
})
let scope = namespace::item_namespace(
cx,
DefId {
krate: instance.def_id().krate,
index: cx
.tcx
.def_key(instance.def_id())
.parent
.expect("get_containing_scope: missing parent?"),
},
);
(scope, false)
}
}

View file

@ -1987,6 +1987,21 @@ extern "C" {
Decl: Option<&'a DIDescriptor>,
) -> &'a DISubprogram;
pub fn LLVMRustDIBuilderCreateMethod<'a>(
Builder: &DIBuilder<'a>,
Scope: &'a DIDescriptor,
Name: *const c_char,
NameLen: size_t,
LinkageName: *const c_char,
LinkageNameLen: size_t,
File: &'a DIFile,
LineNo: c_uint,
Ty: &'a DIType,
Flags: DIFlags,
SPFlags: DISPFlags,
TParam: &'a DIArray,
) -> &'a DISubprogram;
pub fn LLVMRustDIBuilderCreateBasicType<'a>(
Builder: &DIBuilder<'a>,
Name: *const c_char,

View file

@ -1821,9 +1821,15 @@ impl SharedEmitterMain {
let source = sess
.source_map()
.new_source_file(FileName::inline_asm_source_code(&buffer), buffer);
let source_span = Span::with_root_ctxt(source.start_pos, source.end_pos);
let spans: Vec<_> =
spans.iter().map(|sp| source_span.from_inner(*sp)).collect();
let spans: Vec<_> = spans
.iter()
.map(|sp| {
Span::with_root_ctxt(
source.normalized_byte_pos(sp.start as u32),
source.normalized_byte_pos(sp.end as u32),
)
})
.collect();
err.span_note(spans, "instantiated into assembly here");
}

View file

@ -25,7 +25,7 @@ use rustc_data_structures::profiling::{
use rustc_data_structures::sync::SeqCst;
use rustc_errors::registry::{InvalidErrorCode, Registry};
use rustc_errors::{
DiagnosticMessage, ErrorGuaranteed, PResult, SubdiagnosticMessage, TerminalUrl,
DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl,
};
use rustc_feature::find_gated_cfg;
use rustc_fluent_macro::fluent_messages;
@ -55,7 +55,7 @@ use std::panic::{self, catch_unwind};
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;
use std::sync::LazyLock;
use std::sync::OnceLock;
use std::time::Instant;
// This import blocks the use of panicking `print` and `println` in all the code
@ -119,7 +119,7 @@ pub const EXIT_SUCCESS: i32 = 0;
/// Exit status code used for compilation failures and invalid flags.
pub const EXIT_FAILURE: i32 = 1;
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
@ -1196,35 +1196,58 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
}
}
static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
LazyLock::new(|| {
let hook = panic::take_hook();
panic::set_hook(Box::new(|info| {
// If the error was caused by a broken pipe then this is not a bug.
// Write the error and return immediately. See #98700.
#[cfg(windows)]
if let Some(msg) = info.payload().downcast_ref::<String>() {
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
{
early_error_no_abort(ErrorOutputType::default(), &msg);
return;
}
};
/// Stores the default panic hook, from before [`install_ice_hook`] was called.
static DEFAULT_HOOK: OnceLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
OnceLock::new();
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
(*DEFAULT_HOOK)(info);
/// Installs a panic hook that will print the ICE message on unexpected panics.
///
/// The hook is intended to be useable even by external tools. You can pass a custom
/// `bug_report_url`, or report arbitrary info in `extra_info`. Note that `extra_info` is called in
/// a context where *the thread is currently panicking*, so it must not panic or the process will
/// abort.
///
/// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to
/// extra_info.
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) {
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
// full backtraces. When a compiler ICE happens, we want to gather
// as much information as possible to present in the issue opened
// by the user. Compiler developers and other rustc users can
// opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
// (e.g. `RUST_BACKTRACE=1`)
if std::env::var("RUST_BACKTRACE").is_err() {
std::env::set_var("RUST_BACKTRACE", "full");
}
// Separate the output with an empty line
eprintln!();
let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook);
panic::set_hook(Box::new(move |info| {
// If the error was caused by a broken pipe then this is not a bug.
// Write the error and return immediately. See #98700.
#[cfg(windows)]
if let Some(msg) = info.payload().downcast_ref::<String>() {
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") {
early_error_no_abort(ErrorOutputType::default(), &msg);
return;
}
};
// Print the ICE message
report_ice(info, BUG_REPORT_URL);
}));
hook
});
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
(*default_hook)(info);
// Separate the output with an empty line
eprintln!();
}
// Print the ICE message
report_ice(info, bug_report_url, extra_info);
}));
}
/// Prints the ICE message, including query stack, but without backtrace.
///
@ -1232,7 +1255,7 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
///
/// When `install_ice_hook` is called, this function will be called as the panic
/// hook.
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
@ -1277,6 +1300,10 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
interface::try_print_query_stack(&handler, num_frames);
// We don't trust this callback not to panic itself, so run it at the end after we're sure we've
// printed all the relevant info.
extra_info(&handler);
#[cfg(windows)]
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
// Trigger a debugger if we crashed during bootstrap
@ -1284,22 +1311,6 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
}
}
/// Installs a panic hook that will print the ICE message on unexpected panics.
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
pub fn install_ice_hook() {
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
// full backtraces. When a compiler ICE happens, we want to gather
// as much information as possible to present in the issue opened
// by the user. Compiler developers and other rustc users can
// opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
// (e.g. `RUST_BACKTRACE=1`)
if std::env::var("RUST_BACKTRACE").is_err() {
std::env::set_var("RUST_BACKTRACE", "full");
}
LazyLock::force(&DEFAULT_HOOK);
}
/// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version.
pub fn init_rustc_env_logger() {
@ -1370,7 +1381,7 @@ pub fn main() -> ! {
init_rustc_env_logger();
signal_handler::install();
let mut callbacks = TimePassesCallbacks::default();
install_ice_hook();
install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
let exit_code = catch_with_exit_code(|| {
let args = env::args_os()
.enumerate()

View file

@ -1,12 +1,14 @@
// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
//
// We don't do any drop checking during hir typeck.
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::util::IgnoreRegions;
use rustc_middle::ty::{self, Predicate, Ty, TyCtxt};
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits::{self, ObligationCtxt};
use crate::errors;
use crate::hir::def_id::{DefId, LocalDefId};
@ -43,21 +45,20 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro
}
}
let dtor_self_type = tcx.type_of(drop_impl_did).subst_identity();
let dtor_predicates = tcx.predicates_of(drop_impl_did);
match dtor_self_type.kind() {
ty::Adt(adt_def, self_to_impl_substs) => {
ty::Adt(adt_def, adt_to_impl_substs) => {
ensure_drop_params_and_item_params_correspond(
tcx,
drop_impl_did.expect_local(),
adt_def.did(),
self_to_impl_substs,
adt_to_impl_substs,
)?;
ensure_drop_predicates_are_implied_by_item_defn(
tcx,
dtor_predicates,
drop_impl_did.expect_local(),
adt_def.did().expect_local(),
self_to_impl_substs,
adt_to_impl_substs,
)
}
_ => {
@ -78,9 +79,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
tcx: TyCtxt<'tcx>,
drop_impl_did: LocalDefId,
self_type_did: DefId,
drop_impl_substs: SubstsRef<'tcx>,
adt_to_impl_substs: SubstsRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else {
let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, IgnoreRegions::No) else {
return Ok(())
};
@ -111,237 +112,94 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
/// implied by assuming the predicates attached to self_type_did.
fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
tcx: TyCtxt<'tcx>,
dtor_predicates: ty::GenericPredicates<'tcx>,
self_type_did: LocalDefId,
self_to_impl_substs: SubstsRef<'tcx>,
drop_impl_def_id: LocalDefId,
adt_def_id: LocalDefId,
adt_to_impl_substs: SubstsRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let mut result = Ok(());
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
// Here is an example, analogous to that from
// `compare_impl_method`.
// Take the param-env of the adt and substitute the substs that show up in
// the implementation's self type. This gives us the assumptions that the
// self ty of the implementation is allowed to know just from it being a
// well-formed adt, since that's all we're allowed to assume while proving
// the Drop implementation is not specialized.
//
// Consider a struct type:
//
// struct Type<'c, 'b:'c, 'a> {
// x: &'a Contents // (contents are irrelevant;
// y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
// }
//
// and a Drop impl:
//
// impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
// fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
// }
//
// We start out with self_to_impl_substs, that maps the generic
// parameters of Type to that of the Drop impl.
//
// self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
//
// Applying this to the predicates (i.e., assumptions) provided by the item
// definition yields the instantiated assumptions:
//
// ['y : 'z]
//
// We then check all of the predicates of the Drop impl:
//
// ['y:'z, 'x:'y]
//
// and ensure each is in the list of instantiated
// assumptions. Here, `'y:'z` is present, but `'x:'y` is
// absent. So we report an error that the Drop impl injected a
// predicate that is not present on the struct definition.
// We don't need to normalize this param-env or anything, since we're only
// substituting it with free params, so no additional param-env normalization
// can occur on top of what has been done in the param_env query itself.
let param_env = ty::EarlyBinder(tcx.param_env(adt_def_id))
.subst(tcx, adt_to_impl_substs)
.with_constness(tcx.constness(drop_impl_def_id));
// We can assume the predicates attached to struct/enum definition
// hold.
let generic_assumptions = tcx.predicates_of(self_type_did);
for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) {
let normalize_cause = traits::ObligationCause::misc(span, adt_def_id);
let pred = ocx.normalize(&normalize_cause, param_env, pred);
let cause = traits::ObligationCause::new(span, adt_def_id, traits::DropImpl);
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred));
}
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
// All of the custom error reporting logic is to preserve parity with the old
// error messages.
//
// They can probably get removed with better treatment of the new `DropImpl`
// obligation cause code, and perhaps some custom logic in `report_region_errors`.
debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
let self_param_env = tcx.param_env(self_type_did);
// An earlier version of this code attempted to do this checking
// via the traits::fulfill machinery. However, it ran into trouble
// since the fulfill machinery merely turns outlives-predicates
// 'a:'b and T:'b into region inference constraints. It is simpler
// just to look for all the predicates directly.
assert_eq!(dtor_predicates.parent, None);
for &(predicate, predicate_sp) in dtor_predicates.predicates {
// (We do not need to worry about deep analysis of type
// expressions etc because the Drop impls are already forced
// to take on a structure that is roughly an alpha-renaming of
// the generic parameters of the item definition.)
// This path now just checks *all* predicates via an instantiation of
// the `SimpleEqRelation`, which simply forwards to the `relate` machinery
// after taking care of anonymizing late bound regions.
//
// However, it may be more efficient in the future to batch
// the analysis together via the fulfill (see comment above regarding
// the usage of the fulfill machinery), rather than the
// repeated `.iter().any(..)` calls.
// This closure is a more robust way to check `Predicate` equality
// than simple `==` checks (which were the previous implementation).
// It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`,
// `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait),
// while delegating on simple equality for the other `Predicate`.
// This implementation solves (Issue #59497) and (Issue #58311).
// It is unclear to me at the moment whether the approach based on `relate`
// could be extended easily also to the other `Predicate`.
let predicate_matches_closure = |p: Predicate<'tcx>| {
let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env);
let predicate = predicate.kind();
let p = p.kind();
match (predicate.skip_binder(), p.skip_binder()) {
(
ty::PredicateKind::Clause(ty::Clause::Trait(a)),
ty::PredicateKind::Clause(ty::Clause::Trait(b)),
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
(
ty::PredicateKind::Clause(ty::Clause::Projection(a)),
ty::PredicateKind::Clause(ty::Clause::Projection(b)),
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
(
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
ty_a,
lt_a,
))),
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
ty_b,
lt_b,
))),
) => {
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
}
(ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => {
relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok()
}
_ => predicate == p,
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
let mut guar = None;
let mut root_predicates = FxHashSet::default();
for error in errors {
let root_predicate = error.root_obligation.predicate;
if root_predicates.insert(root_predicate) {
let item_span = tcx.def_span(adt_def_id);
let self_descr = tcx.def_descr(adt_def_id.to_def_id());
guar = Some(
struct_span_err!(
tcx.sess,
error.root_obligation.cause.span,
E0367,
"`Drop` impl requires `{root_predicate}` \
but the {self_descr} it is implemented for does not",
)
.span_note(item_span, "the implementor must specify the same requirement")
.emit(),
);
}
};
if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) {
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_descr(self_type_did.to_def_id());
let reported = struct_span_err!(
tcx.sess,
predicate_sp,
E0367,
"`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not",
)
.span_note(item_span, "the implementor must specify the same requirement")
.emit();
result = Err(reported);
}
return Err(guar.unwrap());
}
result
}
/// This is an implementation of the [`TypeRelation`] trait with the
/// aim of simply comparing for equality (without side-effects).
///
/// It is not intended to be used anywhere else other than here.
pub(crate) struct SimpleEqRelation<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'tcx> SimpleEqRelation<'tcx> {
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> {
SimpleEqRelation { tcx, param_env }
}
}
impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
fn tag(&self) -> &'static str {
"dropck::SimpleEqRelation"
}
fn a_is_expected(&self) -> bool {
true
}
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
// Here we ignore variance because we require drop impl's types
// to be *exactly* the same as to the ones in the struct definition.
self.relate(a, b)
}
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b);
ty::relate::super_relate_tys(self, a, b)
}
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b);
// We can just equate the regions because LBRs have been
// already anonymized.
if a == b {
Ok(a)
} else {
// I'm not sure is this `TypeError` is the right one, but
// it should not matter as it won't be checked (the dropck
// will emit its own, more informative and higher-level errors
// in case anything goes wrong).
Err(TypeError::RegionsPlaceholderMismatch)
let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env));
if !errors.is_empty() {
let mut guar = None;
for error in errors {
let item_span = tcx.def_span(adt_def_id);
let self_descr = tcx.def_descr(adt_def_id.to_def_id());
let outlives = match error {
RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"),
RegionResolutionError::GenericBoundFailure(_, generic, r) => {
format!("{generic}: {r}")
}
RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"),
RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
format!("{b}: {a}", a = tcx.mk_re_var(a))
}
};
guar = Some(
struct_span_err!(
tcx.sess,
error.origin().span(),
E0367,
"`Drop` impl requires `{outlives}` \
but the {self_descr} it is implemented for does not",
)
.span_note(item_span, "the implementor must specify the same requirement")
.emit(),
);
}
return Err(guar.unwrap());
}
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b);
ty::relate::super_relate_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
debug!("SimpleEqRelation::binders({:?}: {:?}", a, b);
// Anonymizing the LBRs is necessary to solve (Issue #59497).
// After we do so, it should be totally fine to skip the binders.
let anon_a = self.tcx.anonymize_bound_vars(a);
let anon_b = self.tcx.anonymize_bound_vars(b);
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
Ok(a)
}
Ok(())
}

View file

@ -102,6 +102,17 @@ pub enum RegionResolutionError<'tcx> {
),
}
impl<'tcx> RegionResolutionError<'tcx> {
pub fn origin(&self) -> &SubregionOrigin<'tcx> {
match self {
RegionResolutionError::ConcreteFailure(origin, _, _)
| RegionResolutionError::GenericBoundFailure(origin, _, _)
| RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
}
}
}
struct RegionAndOrigin<'tcx> {
region: Region<'tcx>,
origin: SubregionOrigin<'tcx>,

View file

@ -831,6 +831,28 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
return wrap(Sub);
}
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
const char *Name, size_t NameLen,
const char *LinkageName, size_t LinkageNameLen,
LLVMMetadataRef File, unsigned LineNo,
LLVMMetadataRef Ty, LLVMRustDIFlags Flags,
LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
DITemplateParameterArray TParams =
DITemplateParameterArray(unwrap<MDTuple>(TParam));
DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
DINode::DIFlags llvmFlags = fromRust(Flags);
DISubprogram *Sub = Builder->createMethod(
unwrapDI<DIScope>(Scope),
StringRef(Name, NameLen),
StringRef(LinkageName, LinkageNameLen),
unwrapDI<DIFile>(File), LineNo,
unwrapDI<DISubroutineType>(Ty),
0, 0, nullptr, // VTable params aren't used
llvmFlags, llvmSPFlags, TParams);
return wrap(Sub);
}
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
uint64_t SizeInBits, unsigned Encoding) {

View file

@ -444,6 +444,10 @@ pub enum ObligationCauseCode<'tcx> {
AscribeUserTypeProvePredicate(Span),
RustCall,
/// Obligations to prove that a `std::ops::Drop` impl is not stronger than
/// the ADT it's being implemented for.
DropImpl,
}
/// The 'location' at which we try to perform HIR-based wf checking.

View file

@ -385,7 +385,7 @@ impl<'tcx> Instance<'tcx> {
/// couldn't complete due to errors elsewhere - this is distinct
/// from `Ok(None)` to avoid misleading diagnostics when an error
/// has already been/will be emitted, for the original cause
#[instrument(level = "debug", skip(tcx))]
#[instrument(level = "debug", skip(tcx), ret)]
pub fn resolve(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,

View file

@ -1448,8 +1448,19 @@ impl<'a> Parser<'a> {
}
fn parse_expr_path_start(&mut self) -> PResult<'a, P<Expr>> {
let maybe_eq_tok = self.prev_token.clone();
let (qself, path) = if self.eat_lt() {
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
let lt_span = self.prev_token.span;
let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| {
// Suggests using '<=' if there is an error parsing qpath when the previous token
// is an '=' token. Only emits suggestion if the '<' token and '=' token are
// directly adjacent (i.e. '=<')
if maybe_eq_tok.kind == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {
let eq_lt = maybe_eq_tok.span.to(lt_span);
err.span_suggestion(eq_lt, "did you mean", "<=", Applicability::Unspecified);
}
err
})?;
(Some(qself), path)
} else {
(None, self.parse_path(PathStyle::Expr)?)

View file

@ -4,6 +4,7 @@ version = "0.0.0"
edition = "2021"
[dependencies]
rustc_hir = { path = "../rustc_hir" }
rustc_middle = { path = "../rustc_middle", optional = true }
rustc_span = { path = "../rustc_span", optional = true }
tracing = "0.1"

View file

@ -93,10 +93,10 @@ fn rustc_statement_to_statement(
}
}
fn rustc_rvalue_to_rvalue(rvalue: &rustc_middle::mir::Rvalue<'_>) -> stable_mir::mir::Operand {
fn rustc_rvalue_to_rvalue(rvalue: &rustc_middle::mir::Rvalue<'_>) -> stable_mir::mir::Rvalue {
use rustc_middle::mir::Rvalue::*;
match rvalue {
Use(op) => rustc_op_to_op(op),
Use(op) => stable_mir::mir::Rvalue::Use(rustc_op_to_op(op)),
Repeat(_, _) => todo!(),
Ref(_, _, _) => todo!(),
ThreadLocalRef(_) => todo!(),
@ -104,9 +104,15 @@ fn rustc_rvalue_to_rvalue(rvalue: &rustc_middle::mir::Rvalue<'_>) -> stable_mir:
Len(_) => todo!(),
Cast(_, _, _) => todo!(),
BinaryOp(_, _) => todo!(),
CheckedBinaryOp(_, _) => todo!(),
CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp(
rustc_bin_op_to_bin_op(bin_op),
rustc_op_to_op(&ops.0),
rustc_op_to_op(&ops.1),
),
NullaryOp(_, _) => todo!(),
UnaryOp(_, _) => todo!(),
UnaryOp(un_op, op) => {
stable_mir::mir::Rvalue::UnaryOp(rustc_un_op_to_un_op(un_op), rustc_op_to_op(op))
}
Discriminant(_) => todo!(),
Aggregate(_, _) => todo!(),
ShallowInitBox(_, _) => todo!(),
@ -124,8 +130,10 @@ fn rustc_op_to_op(op: &rustc_middle::mir::Operand<'_>) -> stable_mir::mir::Opera
}
fn rustc_place_to_place(place: &rustc_middle::mir::Place<'_>) -> stable_mir::mir::Place {
assert_eq!(&place.projection[..], &[]);
stable_mir::mir::Place { local: place.local.as_usize() }
stable_mir::mir::Place {
local: place.local.as_usize(),
projection: format!("{:?}", place.projection),
}
}
fn rustc_unwind_to_unwind(
@ -140,6 +148,96 @@ fn rustc_unwind_to_unwind(
}
}
fn rustc_assert_msg_to_msg<'tcx>(
assert_message: &rustc_middle::mir::AssertMessage<'tcx>,
) -> stable_mir::mir::AssertMessage {
use rustc_middle::mir::AssertKind;
match assert_message {
AssertKind::BoundsCheck { len, index } => stable_mir::mir::AssertMessage::BoundsCheck {
len: rustc_op_to_op(len),
index: rustc_op_to_op(index),
},
AssertKind::Overflow(bin_op, op1, op2) => stable_mir::mir::AssertMessage::Overflow(
rustc_bin_op_to_bin_op(bin_op),
rustc_op_to_op(op1),
rustc_op_to_op(op2),
),
AssertKind::OverflowNeg(op) => {
stable_mir::mir::AssertMessage::OverflowNeg(rustc_op_to_op(op))
}
AssertKind::DivisionByZero(op) => {
stable_mir::mir::AssertMessage::DivisionByZero(rustc_op_to_op(op))
}
AssertKind::RemainderByZero(op) => {
stable_mir::mir::AssertMessage::RemainderByZero(rustc_op_to_op(op))
}
AssertKind::ResumedAfterReturn(generator) => {
stable_mir::mir::AssertMessage::ResumedAfterReturn(rustc_generator_to_generator(
generator,
))
}
AssertKind::ResumedAfterPanic(generator) => {
stable_mir::mir::AssertMessage::ResumedAfterPanic(rustc_generator_to_generator(
generator,
))
}
AssertKind::MisalignedPointerDereference { required, found } => {
stable_mir::mir::AssertMessage::MisalignedPointerDereference {
required: rustc_op_to_op(required),
found: rustc_op_to_op(found),
}
}
}
}
fn rustc_bin_op_to_bin_op(bin_op: &rustc_middle::mir::BinOp) -> stable_mir::mir::BinOp {
use rustc_middle::mir::BinOp;
match bin_op {
BinOp::Add => stable_mir::mir::BinOp::Add,
BinOp::Sub => stable_mir::mir::BinOp::Sub,
BinOp::Mul => stable_mir::mir::BinOp::Mul,
BinOp::Div => stable_mir::mir::BinOp::Div,
BinOp::Rem => stable_mir::mir::BinOp::Rem,
BinOp::BitXor => stable_mir::mir::BinOp::BitXor,
BinOp::BitAnd => stable_mir::mir::BinOp::BitAnd,
BinOp::BitOr => stable_mir::mir::BinOp::BitOr,
BinOp::Shl => stable_mir::mir::BinOp::Shl,
BinOp::Shr => stable_mir::mir::BinOp::Shr,
BinOp::Eq => stable_mir::mir::BinOp::Eq,
BinOp::Lt => stable_mir::mir::BinOp::Lt,
BinOp::Le => stable_mir::mir::BinOp::Le,
BinOp::Ne => stable_mir::mir::BinOp::Ne,
BinOp::Ge => stable_mir::mir::BinOp::Ge,
BinOp::Gt => stable_mir::mir::BinOp::Gt,
BinOp::Offset => stable_mir::mir::BinOp::Offset,
}
}
fn rustc_un_op_to_un_op(unary_op: &rustc_middle::mir::UnOp) -> stable_mir::mir::UnOp {
use rustc_middle::mir::UnOp;
match unary_op {
UnOp::Not => stable_mir::mir::UnOp::Not,
UnOp::Neg => stable_mir::mir::UnOp::Neg,
}
}
fn rustc_generator_to_generator(
generator: &rustc_hir::GeneratorKind,
) -> stable_mir::mir::GeneratorKind {
use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
match generator {
GeneratorKind::Async(async_gen) => {
let async_gen = match async_gen {
AsyncGeneratorKind::Block => stable_mir::mir::AsyncGeneratorKind::Block,
AsyncGeneratorKind::Closure => stable_mir::mir::AsyncGeneratorKind::Closure,
AsyncGeneratorKind::Fn => stable_mir::mir::AsyncGeneratorKind::Fn,
};
stable_mir::mir::GeneratorKind::Async(async_gen)
}
GeneratorKind::Gen => stable_mir::mir::GeneratorKind::Gen,
}
}
fn rustc_terminator_to_terminator(
terminator: &rustc_middle::mir::Terminator<'_>,
) -> stable_mir::mir::Terminator {
@ -162,7 +260,11 @@ fn rustc_terminator_to_terminator(
Terminate => Terminator::Abort,
Return => Terminator::Return,
Unreachable => Terminator::Unreachable,
Drop { .. } => todo!(),
Drop { place, target, unwind } => Terminator::Drop {
place: rustc_place_to_place(place),
target: target.as_usize(),
unwind: rustc_unwind_to_unwind(unwind),
},
Call { func, args, destination, target, unwind, from_hir_call: _, fn_span: _ } => {
Terminator::Call {
func: rustc_op_to_op(func),
@ -172,9 +274,15 @@ fn rustc_terminator_to_terminator(
unwind: rustc_unwind_to_unwind(unwind),
}
}
Assert { .. } => todo!(),
Assert { cond, expected, msg, target, unwind } => Terminator::Assert {
cond: rustc_op_to_op(cond),
expected: *expected,
msg: rustc_assert_msg_to_msg(msg),
target: target.as_usize(),
unwind: rustc_unwind_to_unwind(unwind),
},
Yield { .. } => todo!(),
GeneratorDrop => todo!(),
GeneratorDrop => Terminator::GeneratorDrop,
FalseEdge { .. } => todo!(),
FalseUnwind { .. } => todo!(),
InlineAsm { .. } => todo!(),

View file

@ -26,7 +26,7 @@ pub enum Terminator {
Drop {
place: Place,
target: usize,
unwind: Option<usize>,
unwind: UnwindAction,
},
Call {
func: Operand,
@ -38,10 +38,11 @@ pub enum Terminator {
Assert {
cond: Operand,
expected: bool,
msg: String,
msg: AssertMessage,
target: usize,
cleanup: Option<usize>,
unwind: UnwindAction,
},
GeneratorDrop,
}
#[derive(Clone, Debug)]
@ -52,12 +53,72 @@ pub enum UnwindAction {
Cleanup(usize),
}
#[derive(Clone, Debug)]
pub enum AssertMessage {
BoundsCheck { len: Operand, index: Operand },
Overflow(BinOp, Operand, Operand),
OverflowNeg(Operand),
DivisionByZero(Operand),
RemainderByZero(Operand),
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
MisalignedPointerDereference { required: Operand, found: Operand },
}
#[derive(Clone, Debug)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
BitXor,
BitAnd,
BitOr,
Shl,
Shr,
Eq,
Lt,
Le,
Ne,
Ge,
Gt,
Offset,
}
#[derive(Clone, Debug)]
pub enum UnOp {
Not,
Neg,
}
#[derive(Clone, Debug)]
pub enum GeneratorKind {
Async(AsyncGeneratorKind),
Gen,
}
#[derive(Clone, Debug)]
pub enum AsyncGeneratorKind {
Block,
Closure,
Fn,
}
#[derive(Clone, Debug)]
pub enum Statement {
Assign(Place, Operand),
Assign(Place, Rvalue),
Nop,
}
// FIXME this is incomplete
#[derive(Clone, Debug)]
pub enum Rvalue {
Use(Operand),
CheckedBinaryOp(BinOp, Operand, Operand),
UnaryOp(UnOp, Operand),
}
#[derive(Clone, Debug)]
pub enum Operand {
Copy(Place),
@ -68,6 +129,7 @@ pub enum Operand {
#[derive(Clone, Debug)]
pub struct Place {
pub local: usize,
pub projection: String,
}
#[derive(Clone, Debug)]

View file

@ -1744,6 +1744,28 @@ impl SourceFile {
BytePos::from_u32(pos.0 - self.start_pos.0 + diff)
}
/// Calculates a normalized byte position from a byte offset relative to the
/// start of the file.
///
/// When we get an inline assembler error from LLVM during codegen, we
/// import the expanded assembly code as a new `SourceFile`, which can then
/// be used for error reporting with spans. However the byte offsets given
/// to us by LLVM are relative to the start of the original buffer, not the
/// normalized one. Hence we need to convert those offsets to the normalized
/// form when constructing spans.
pub fn normalized_byte_pos(&self, offset: u32) -> BytePos {
let diff = match self
.normalized_pos
.binary_search_by(|np| (np.pos.0 + np.diff).cmp(&(self.start_pos.0 + offset)))
{
Ok(i) => self.normalized_pos[i].diff,
Err(i) if i == 0 => 0,
Err(i) => self.normalized_pos[i - 1].diff,
};
BytePos::from_u32(self.start_pos.0 + offset - diff)
}
/// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`.
pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos {
// The number of extra bytes due to multibyte chars in the `SourceFile`.

View file

@ -1207,6 +1207,7 @@ symbols! {
require,
residual,
result,
resume,
return_position_impl_trait_in_trait,
return_type_notation,
rhs,

View file

@ -2793,7 +2793,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::LetElse
| ObligationCauseCode::BinOp { .. }
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
| ObligationCauseCode::RustCall => {}
| ObligationCauseCode::RustCall
| ObligationCauseCode::DropImpl => {}
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}

View file

@ -177,15 +177,55 @@ fn resolve_associated_item<'tcx>(
Some(ty::Instance::new(leaf_def.item.def_id, substs))
}
traits::ImplSource::Generator(generator_data) => Some(Instance {
def: ty::InstanceDef::Item(generator_data.generator_def_id),
substs: generator_data.substs,
}),
traits::ImplSource::Future(future_data) => Some(Instance {
def: ty::InstanceDef::Item(future_data.generator_def_id),
substs: future_data.substs,
}),
traits::ImplSource::Generator(generator_data) => {
if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::resume {
// For compiler developers who'd like to add new items to `Generator`,
// you either need to generate a shim body, or perhaps return
// `InstanceDef::Item` pointing to a trait default method body if
// it is given a default implementation by the trait.
span_bug!(
tcx.def_span(generator_data.generator_def_id),
"no definition for `{trait_ref}::{}` for built-in generator type",
tcx.item_name(trait_item_id)
)
}
Some(Instance {
def: ty::InstanceDef::Item(generator_data.generator_def_id),
substs: generator_data.substs,
})
}
traits::ImplSource::Future(future_data) => {
if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::poll {
// For compiler developers who'd like to add new items to `Future`,
// you either need to generate a shim body, or perhaps return
// `InstanceDef::Item` pointing to a trait default method body if
// it is given a default implementation by the trait.
span_bug!(
tcx.def_span(future_data.generator_def_id),
"no definition for `{trait_ref}::{}` for built-in async generator type",
tcx.item_name(trait_item_id)
)
}
Some(Instance {
def: ty::InstanceDef::Item(future_data.generator_def_id),
substs: future_data.substs,
})
}
traits::ImplSource::Closure(closure_data) => {
if cfg!(debug_assertions)
&& ![sym::call, sym::call_mut, sym::call_once]
.contains(&tcx.item_name(trait_item_id))
{
// For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
// you either need to generate a shim body, or perhaps return
// `InstanceDef::Item` pointing to a trait default method body if
// it is given a default implementation by the trait.
span_bug!(
tcx.def_span(closure_data.closure_def_id),
"no definition for `{trait_ref}::{}` for built-in closure type",
tcx.item_name(trait_item_id)
)
}
let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
Instance::resolve_closure(
tcx,
@ -195,11 +235,29 @@ fn resolve_associated_item<'tcx>(
)
}
traits::ImplSource::FnPointer(ref data) => match data.fn_ty.kind() {
ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty),
substs: rcvr_substs,
}),
_ => None,
ty::FnDef(..) | ty::FnPtr(..) => {
if cfg!(debug_assertions)
&& ![sym::call, sym::call_mut, sym::call_once]
.contains(&tcx.item_name(trait_item_id))
{
// For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
// you either need to generate a shim body, or perhaps return
// `InstanceDef::Item` pointing to a trait default method body if
// it is given a default implementation by the trait.
bug!(
"no definition for `{trait_ref}::{}` for built-in fn type",
tcx.item_name(trait_item_id)
)
}
Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty),
substs: rcvr_substs,
})
}
_ => bug!(
"no built-in definition for `{trait_ref}::{}` for non-fn type",
tcx.item_name(trait_item_id)
),
},
traits::ImplSource::Object(ref data) => {
traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| {

View file

@ -156,15 +156,19 @@ pub fn main() {
}
}
rustc_driver::install_ice_hook();
rustc_driver::install_ice_hook(
"https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
|_| (),
);
// When using CI artifacts (with `download_stage1 = true`), tracing is unconditionally built
// When using CI artifacts with `download-rustc`, tracing is unconditionally built
// with `--features=static_max_level_info`, which disables almost all rustdoc logging. To avoid
// this, compile our own version of `tracing` that logs all levels.
// NOTE: this compiles both versions of tracing unconditionally, because
// - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and
// - Otherwise, there's no warning that logging is being ignored when `download_stage1 = true`.
// NOTE: The reason this doesn't show double logging when `download_stage1 = false` and
// - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled
// NOTE: The reason this doesn't show double logging when `download-rustc = false` and
// `debug_logging = true` is because all rustc logging goes to its version of tracing (the one
// in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml).
init_logging();

View file

@ -11,7 +11,6 @@
// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_span;
@ -20,13 +19,10 @@ use rustc_interface::interface;
use rustc_session::parse::ParseSess;
use rustc_span::symbol::Symbol;
use std::borrow::Cow;
use std::env;
use std::ops::Deref;
use std::panic;
use std::path::Path;
use std::process::exit;
use std::sync::LazyLock;
/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
@ -198,66 +194,18 @@ You can use tool lints to allow or deny lints from your code, eg.:
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
type PanicCallback = dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static;
static ICE_HOOK: LazyLock<Box<PanicCallback>> = LazyLock::new(|| {
let hook = panic::take_hook();
panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
hook
});
fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
// Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
(*ICE_HOOK)(info);
// Separate the output with an empty line
eprintln!();
let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,
None,
fallback_bundle,
false,
false,
None,
false,
false,
rustc_errors::TerminalUrl::No,
));
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
// a .span_bug or .bug call has already printed what
// it wants to print.
if !info.payload().is::<rustc_errors::ExplicitBug>() {
let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
handler.emit_diagnostic(&mut d);
}
let version_info = rustc_tools_util::get_version_info!();
let xs: Vec<Cow<'static, str>> = vec![
"the compiler unexpectedly panicked. this is a bug.".into(),
format!("we would appreciate a bug report: {bug_report_url}").into(),
format!("Clippy version: {version_info}").into(),
];
for note in &xs {
handler.note_without_error(note.as_ref());
}
// If backtraces are enabled, also print the query stack
let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
let num_frames = if backtrace { None } else { Some(2) };
interface::try_print_query_stack(&handler, num_frames);
}
#[allow(clippy::too_many_lines)]
pub fn main() {
rustc_driver::init_rustc_env_logger();
LazyLock::force(&ICE_HOOK);
rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| {
// FIXME: this macro calls unwrap internally but is called in a panicking context! It's not
// as simple as moving the call from the hook to main, because `install_ice_hook` doesn't
// accept a generic closure.
let version_info = rustc_tools_util::get_version_info!();
handler.note_without_error(format!("Clippy version: {version_info}"));
});
exit(rustc_driver::catch_with_exit_code(move || {
let mut orig_args: Vec<String> = env::args().collect();
let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some();

View file

@ -286,11 +286,10 @@ fn main() {
// (`install_ice_hook` might change `RUST_BACKTRACE`.)
let env_snapshot = env::vars_os().collect::<Vec<_>>();
// Earliest rustc setup.
rustc_driver::install_ice_hook();
// If the environment asks us to actually be rustc, then do that.
if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
// Earliest rustc setup.
rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ());
rustc_driver::init_rustc_env_logger();
let target_crate = if crate_kind == "target" {
@ -309,6 +308,9 @@ fn main() {
)
}
// Add an ICE bug report hook.
rustc_driver::install_ice_hook("https://github.com/rust-lang/miri/issues/new", |_| ());
// Init loggers the Miri way.
init_early_loggers();

View file

@ -1,3 +1,5 @@
#![feature(rustc_private)]
use anyhow::{format_err, Result};
use io::Error as IoError;
@ -19,7 +21,14 @@ use crate::rustfmt::{
FormatReportFormatterBuilder, Input, Session, Verbosity,
};
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug";
// N.B. these crates are loaded from the sysroot, so they need extern crate.
extern crate rustc_driver;
fn main() {
rustc_driver::install_ice_hook(BUG_REPORT_URL, |_| ());
env_logger::Builder::from_env("RUSTFMT_LOG").init();
let opts = make_opts();

View file

@ -0,0 +1,12 @@
# ignore-cross-compile
include ../tools.mk
# With the upgrade to LLVM 16, this was getting:
#
# error: Cannot represent a difference across sections
#
# The error stemmed from DI function definitions under type scopes, fixed by
# only declaring in type scope and defining the subprogram elsewhere.
all:
$(RUSTC) lib.rs --test -C lto=fat -C debuginfo=2 -C incremental=$(TMPDIR)/inc-fat

View file

@ -0,0 +1,9 @@
extern crate alloc;
#[cfg(test)]
mod tests {
#[test]
fn something_alloc() {
assert_eq!(Vec::<u32>::new(), Vec::<u32>::new());
}
}

View file

@ -0,0 +1,14 @@
// compile-flags: -Ztreat-err-as-bug
// failure-status: 101
// error-pattern: aborting due to
// error-pattern: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md
// normalize-stderr-test "note: compiler flags.*\n\n" -> ""
// normalize-stderr-test "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}"
// normalize-stderr-test "thread.*panicked at .*, compiler.*" -> "thread panicked at 'aborting due to `-Z treat-err-as-bug`'"
// normalize-stderr-test "\s*\d{1,}: .*\n" -> ""
// normalize-stderr-test "\s at .*\n" -> ""
// normalize-stderr-test ".*note: Some details are omitted.*\n" -> ""
fn wrong()
//~^ ERROR expected one of

View file

@ -0,0 +1,16 @@
error: expected one of `->`, `where`, or `{`, found `<eof>`
--> $DIR/ice-bug-report-url.rs:13:10
|
LL | fn wrong()
| ^ expected one of `->`, `where`, or `{`
thread panicked at 'aborting due to `-Z treat-err-as-bug`'
stack backtrace:
error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md
note: rustc {version} running on {platform}
query stack during panic:
end of query stack

View file

@ -60,6 +60,24 @@ fn test_stable_mir(tcx: TyCtxt<'_>) {
stable_mir::mir::Terminator::Call { .. } => {}
other => panic!("{other:?}"),
}
let drop = get_item(tcx, &items, (DefKind::Fn, "drop")).unwrap();
let body = drop.body();
assert_eq!(body.blocks.len(), 2);
let block = &body.blocks[0];
match &block.terminator {
stable_mir::mir::Terminator::Drop { .. } => {}
other => panic!("{other:?}"),
}
let assert = get_item(tcx, &items, (DefKind::Fn, "assert")).unwrap();
let body = assert.body();
assert_eq!(body.blocks.len(), 2);
let block = &body.blocks[0];
match &block.terminator {
stable_mir::mir::Terminator::Assert { .. } => {}
other => panic!("{other:?}"),
}
}
// Use internal API to find a function in a crate.
@ -131,6 +149,12 @@ fn generate_input(path: &str) -> std::io::Result<()> {
let x_64 = foo::bar(x);
let y_64 = foo::bar(y);
x_64.wrapping_add(y_64)
}}
pub fn drop(_: String) {{}}
pub fn assert(x: i32) -> i32 {{
x + 1
}}"#
)?;
Ok(())

View file

@ -0,0 +1,35 @@
error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/explicit-drop-bounds.rs:27:18
|
LL | impl<T> Drop for DropMe<T>
| ^^^^^^^^^ the trait `Copy` is not implemented for `T`
|
note: required by a bound in `DropMe`
--> $DIR/explicit-drop-bounds.rs:7:18
|
LL | struct DropMe<T: Copy>(T);
| ^^^^ required by this bound in `DropMe`
help: consider further restricting type parameter `T`
|
LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy`
| ~~~~~~~~~~~~~~~~~~~~~~
error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/explicit-drop-bounds.rs:32:13
|
LL | fn drop(&mut self) {}
| ^^^^^^^^^ the trait `Copy` is not implemented for `T`
|
note: required by a bound in `DropMe`
--> $DIR/explicit-drop-bounds.rs:7:18
|
LL | struct DropMe<T: Copy>(T);
| ^^^^ required by this bound in `DropMe`
help: consider further restricting type parameter `T`
|
LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy`
| ~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,35 @@
error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/explicit-drop-bounds.rs:37:18
|
LL | impl<T> Drop for DropMe<T>
| ^^^^^^^^^ the trait `Copy` is not implemented for `T`
|
note: required by a bound in `DropMe`
--> $DIR/explicit-drop-bounds.rs:7:18
|
LL | struct DropMe<T: Copy>(T);
| ^^^^ required by this bound in `DropMe`
help: consider restricting type parameter `T`
|
LL | impl<T: std::marker::Copy> Drop for DropMe<T>
| +++++++++++++++++++
error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/explicit-drop-bounds.rs:40:13
|
LL | fn drop(&mut self) {}
| ^^^^^^^^^ the trait `Copy` is not implemented for `T`
|
note: required by a bound in `DropMe`
--> $DIR/explicit-drop-bounds.rs:7:18
|
LL | struct DropMe<T: Copy>(T);
| ^^^^ required by this bound in `DropMe`
help: consider restricting type parameter `T`
|
LL | impl<T: std::marker::Copy> Drop for DropMe<T>
| +++++++++++++++++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,44 @@
// revisions: good1 good2 bad1 bad2
//[good1] check-pass
//[good2] check-pass
use std::ops::Drop;
struct DropMe<T: Copy>(T);
#[cfg(good1)]
impl<T> Drop for DropMe<T>
where
T: Copy + Clone,
{
fn drop(&mut self) {}
}
#[cfg(good2)]
impl<T> Drop for DropMe<T>
where
T: Copy,
[T; 1]: Copy, // Trivial bound implied by `T: Copy`
{
fn drop(&mut self) {}
}
#[cfg(bad1)]
impl<T> Drop for DropMe<T>
//[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied
where
[T; 1]: Copy, // But `[T; 1]: Copy` does not imply `T: Copy`
{
fn drop(&mut self) {}
//[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied
}
#[cfg(bad2)]
impl<T> Drop for DropMe<T>
//[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied
{
fn drop(&mut self) {}
//[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied
}
fn main() {}

View file

@ -0,0 +1,15 @@
error[E0367]: `Drop` impl requires `T: 'static` but the struct it is implemented for does not
--> $DIR/explicit-implied-outlives.rs:28:8
|
LL | T: 'static,
| ^^^^^^^
|
note: the implementor must specify the same requirement
--> $DIR/explicit-implied-outlives.rs:7:1
|
LL | struct DropMe<'a, T>(&'a T);
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0367`.

View file

@ -0,0 +1,15 @@
error[E0367]: `Drop` impl requires `'a: 'static` but the struct it is implemented for does not
--> $DIR/explicit-implied-outlives.rs:37:9
|
LL | 'a: 'static,
| ^^^^^^^
|
note: the implementor must specify the same requirement
--> $DIR/explicit-implied-outlives.rs:7:1
|
LL | struct DropMe<'a, T>(&'a T);
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0367`.

View file

@ -0,0 +1,43 @@
// revisions: good1 good2 bad1 bad2
//[good1] check-pass
//[good2] check-pass
use std::ops::Drop;
struct DropMe<'a, T>(&'a T);
#[cfg(good1)]
impl<'a, T> Drop for DropMe<'a, T>
where
T: 'a, // Implied by struct, explicit on impl
{
fn drop(&mut self) {}
}
#[cfg(good2)]
impl<'a, T> Drop for DropMe<'a, T>
where
'static: 'a, // Trivial bound
{
fn drop(&mut self) {}
}
#[cfg(bad1)]
impl<'a, T> Drop for DropMe<'a, T>
where
T: 'static,
//[bad1]~^ ERROR `Drop` impl requires `T: 'static`
{
fn drop(&mut self) {}
}
#[cfg(bad2)]
impl<'a, T> Drop for DropMe<'a, T>
where
'a: 'static,
//[bad2]~^ ERROR `Drop` impl requires `'a: 'static`
{
fn drop(&mut self) {}
}
fn main() {}

View file

@ -0,0 +1,18 @@
// check-pass
use std::marker::PhantomData;
use std::ops::Drop;
// a >= b >= c >= a implies a = b = c
struct DropMe<'a: 'b, 'b: 'c, 'c: 'a>(
PhantomData<&'a ()>,
PhantomData<&'b ()>,
PhantomData<&'c ()>,
);
// a >= b, a >= c, b >= a, c >= a implies a = b = c
impl<'a: 'b + 'c, 'b: 'a, 'c: 'a> Drop for DropMe<'a, 'b, 'c> {
fn drop(&mut self) {}
}
fn main() {}

View file

@ -0,0 +1,15 @@
error[E0367]: `Drop` impl requires `'a: 'c` but the struct it is implemented for does not
--> $DIR/transitive-outlives.rs:20:9
|
LL | 'a: 'c,
| ^^
|
note: the implementor must specify the same requirement
--> $DIR/transitive-outlives.rs:7:1
|
LL | struct DropMe<'a, 'b: 'a, 'c: 'b>(PhantomData<&'a ()>, PhantomData<&'b ()>, PhantomData<&'c ()>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0367`.

View file

@ -0,0 +1,26 @@
// revisions: good bad
//[good] check-pass
use std::marker::PhantomData;
use std::ops::Drop;
struct DropMe<'a, 'b: 'a, 'c: 'b>(PhantomData<&'a ()>, PhantomData<&'b ()>, PhantomData<&'c ()>);
#[cfg(good)]
impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c>
where
'c: 'a,
{
fn drop(&mut self) {}
}
#[cfg(bad)]
impl<'a, 'b, 'c> Drop for DropMe<'a, 'b, 'c>
where
'a: 'c,
//[bad]~^ ERROR `Drop` impl requires `'a: 'c`
{
fn drop(&mut self) {}
}
fn main() {}

View file

@ -0,0 +1,34 @@
// revisions: good1 good2 good3
// check-pass
use std::ops::Drop;
struct Foo;
const X: usize = 1;
#[cfg(good1)]
impl Drop for Foo
where
[(); X]:, // Trivial WF bound
{
fn drop(&mut self) {}
}
#[cfg(good2)]
impl Drop for Foo
where
for<'a> &'a (): Copy, // Trivial trait bound
{
fn drop(&mut self) {}
}
#[cfg(good3)]
impl Drop for Foo
where
for<'a> &'a (): 'a, // Trivial outlives bound
{
fn drop(&mut self) {}
}
fn main() {}

View file

@ -0,0 +1,33 @@
fn foo() {
let a = 0;
let b = 4;
if a =< b { //~ERROR
println!("yay!");
}
}
fn bar() {
let a = 0;
let b = 4;
if a = <b { //~ERROR
println!("yay!");
}
}
fn baz() {
let a = 0;
let b = 4;
if a = < b { //~ERROR
println!("yay!");
}
}
fn qux() {
let a = 0;
let b = 4;
if a =< i32>::abs(-4) { //~ERROR: mismatched types
println!("yay!");
}
}
fn main() {}

View file

@ -0,0 +1,34 @@
error: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `{`
--> $DIR/eq-less-to-less-eq.rs:4:15
|
LL | if a =< b {
| -- ^ expected one of 7 possible tokens
| |
| help: did you mean: `<=`
error: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `{`
--> $DIR/eq-less-to-less-eq.rs:12:15
|
LL | if a = <b {
| ^ expected one of 7 possible tokens
error: expected one of `!`, `(`, `+`, `::`, `<`, `>`, or `as`, found `{`
--> $DIR/eq-less-to-less-eq.rs:20:16
|
LL | if a = < b {
| ^ expected one of 7 possible tokens
error[E0308]: mismatched types
--> $DIR/eq-less-to-less-eq.rs:28:8
|
LL | if a =< i32>::abs(-4) {
| ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
help: you might have meant to compare for equality
|
LL | if a ==< i32>::abs(-4) {
| +
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,24 @@
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/drop-impl-pred.rs:6:12
|
LL | #![feature(non_lifetime_binders)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0367]: `Drop` impl requires `H: Foo` but the struct it is implemented for does not
--> $DIR/drop-impl-pred.rs:19:15
|
LL | for<H> H: Foo,
| ^^^
|
note: the implementor must specify the same requirement
--> $DIR/drop-impl-pred.rs:12:1
|
LL | struct Bar<T>(T) where T: Foo;
| ^^^^^^^^^^^^^
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0367`.

View file

@ -0,0 +1,25 @@
// revisions: no yes
//[yes] check-pass
// Issue 110557
#![feature(non_lifetime_binders)]
//~^ WARN the feature `non_lifetime_binders` is incomplete
pub trait Foo {}
#[cfg(no)]
struct Bar<T>(T) where T: Foo;
#[cfg(yes)]
struct Bar<T>(T) where for<H> H: Foo;
impl<T> Drop for Bar<T>
where
for<H> H: Foo,
//[no]~^ ERROR `Drop` impl requires `H: Foo` but the struct it is implemented for does not
{
fn drop(&mut self) {}
}
fn main() {}

View file

@ -0,0 +1,11 @@
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/drop-impl-pred.rs:6:12
|
LL | #![feature(non_lifetime_binders)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted