Skip to content

Fix deduplication mismatches in vtables leading to upcasting unsoundness #135318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove print_vtable_sizes
  • Loading branch information
compiler-errors committed Jan 30, 2025
commit 37a430e6ea0a674287b53a017497b3414e44b93d
91 changes: 0 additions & 91 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use rustc_parse::{
};
use rustc_passes::{abi_test, input_stats, layout_test};
use rustc_resolve::Resolver;
use rustc_session::code_stats::VTableSizeInfo;
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
use rustc_session::cstore::Untracked;
use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
Expand Down Expand Up @@ -989,90 +988,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) {
// we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally.
let _ = tcx.all_diagnostic_items(());
});

if sess.opts.unstable_opts.print_vtable_sizes {
let traits = tcx.traits(LOCAL_CRATE);

for &tr in traits {
if !tcx.is_dyn_compatible(tr) {
continue;
}

let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr));

let mut first_dsa = true;

// Number of vtable entries, if we didn't have upcasting
let mut entries_ignoring_upcasting = 0;
// Number of vtable entries needed solely for upcasting
let mut entries_for_upcasting = 0;

let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr));

// A slightly edited version of the code in
// `rustc_trait_selection::traits::vtable::vtable_entries`, that works without self
// type and just counts number of entries.
//
// Note that this is technically wrong, for traits which have associated types in
// supertraits:
//
// trait A: AsRef<Self::T> + AsRef<()> { type T; }
//
// Without self type we can't normalize `Self::T`, so we can't know if `AsRef<Self::T>`
// and `AsRef<()>` are the same trait, thus we assume that those are different, and
// potentially over-estimate how many vtable entries there are.
//
// Similarly this is wrong for traits that have methods with possibly-impossible bounds.
// For example:
//
// trait B<T> { fn f(&self) where T: Copy; }
//
// Here `dyn B<u8>` will have 4 entries, while `dyn B<String>` will only have 3.
// However, since we don't know `T`, we can't know if `T: Copy` holds or not,
// thus we lean on the bigger side and say it has 4 entries.
traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| {
match segment {
traits::vtable::VtblSegment::MetadataDSA => {
// If this is the first dsa, it would be included either way,
// otherwise it's needed for upcasting
if std::mem::take(&mut first_dsa) {
entries_ignoring_upcasting += 3;
} else {
entries_for_upcasting += 3;
}
}

traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
// Lookup the shape of vtable for the trait.
let own_existential_entries =
tcx.own_existential_vtable_entries(trait_ref.def_id());

// The original code here ignores the method if its predicates are
// impossible. We can't really do that as, for example, all not trivial
// bounds on generic parameters are impossible (since we don't know the
// parameters...), see the comment above.
entries_ignoring_upcasting += own_existential_entries.len();

if emit_vptr {
entries_for_upcasting += 1;
}
}
}

std::ops::ControlFlow::Continue::<std::convert::Infallible>(())
});

sess.code_stats.record_vtable_size(tr, &name, VTableSizeInfo {
trait_name: name.clone(),
entries: entries_ignoring_upcasting + entries_for_upcasting,
entries_ignoring_upcasting,
entries_for_upcasting,
upcasting_cost_percent: entries_for_upcasting as f64
/ entries_ignoring_upcasting as f64
* 100.,
})
}
}
}

/// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
Expand Down Expand Up @@ -1153,12 +1068,6 @@ pub(crate) fn start_codegen<'tcx>(
tcx.sess.code_stats.print_type_sizes();
}

if tcx.sess.opts.unstable_opts.print_vtable_sizes {
let crate_name = tcx.crate_name(LOCAL_CRATE);

tcx.sess.code_stats.print_vtable_sizes(crate_name);
}

codegen
}

Expand Down
60 changes: 1 addition & 59 deletions compiler/rustc_session/src/code_stats.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::cmp;

use rustc_abi::{Align, Size};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lock;
use rustc_span::Symbol;
use rustc_span::def_id::DefId;

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantInfo {
Expand Down Expand Up @@ -71,29 +70,9 @@ pub struct TypeSizeInfo {
pub variants: Vec<VariantInfo>,
}

pub struct VTableSizeInfo {
pub trait_name: String,

/// Number of entries in a vtable with the current algorithm
/// (i.e. with upcasting).
pub entries: usize,

/// Number of entries in a vtable, as-if we did not have trait upcasting.
pub entries_ignoring_upcasting: usize,

/// Number of entries in a vtable needed solely for upcasting
/// (i.e. `entries - entries_ignoring_upcasting`).
pub entries_for_upcasting: usize,

/// Cost of having upcasting in % relative to the number of entries without
/// upcasting (i.e. `entries_for_upcasting / entries_ignoring_upcasting * 100%`).
pub upcasting_cost_percent: f64,
}

#[derive(Default)]
pub struct CodeStats {
type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
vtable_sizes: Lock<FxHashMap<DefId, VTableSizeInfo>>,
}

impl CodeStats {
Expand Down Expand Up @@ -127,14 +106,6 @@ impl CodeStats {
self.type_sizes.borrow_mut().insert(info);
}

pub fn record_vtable_size(&self, trait_did: DefId, trait_name: &str, info: VTableSizeInfo) {
let prev = self.vtable_sizes.lock().insert(trait_did, info);
assert!(
prev.is_none(),
"size of vtable for `{trait_name}` ({trait_did:?}) is already recorded"
);
}

pub fn print_type_sizes(&self) {
let type_sizes = self.type_sizes.borrow();
// We will soon sort, so the initial order does not matter.
Expand Down Expand Up @@ -238,33 +209,4 @@ impl CodeStats {
}
}
}

pub fn print_vtable_sizes(&self, crate_name: Symbol) {
// We will soon sort, so the initial order does not matter.
#[allow(rustc::potential_query_instability)]
let mut infos =
std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>();

// Primary sort: cost % in reverse order (from largest to smallest)
// Secondary sort: trait_name
infos.sort_by(|a, b| {
a.upcasting_cost_percent
.total_cmp(&b.upcasting_cost_percent)
.reverse()
.then_with(|| a.trait_name.cmp(&b.trait_name))
});

for VTableSizeInfo {
trait_name,
entries,
entries_ignoring_upcasting,
entries_for_upcasting,
upcasting_cost_percent,
} in infos
{
println!(
r#"print-vtable-sizes {{ "crate_name": "{crate_name}", "trait_name": "{trait_name}", "entries": "{entries}", "entries_ignoring_upcasting": "{entries_ignoring_upcasting}", "entries_for_upcasting": "{entries_for_upcasting}", "upcasting_cost_percent": "{upcasting_cost_percent}" }}"#
);
}
}
}
2 changes: 0 additions & 2 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2033,8 +2033,6 @@ options! {
Note that this overwrites the effect `-Clink-dead-code` has on collection!"),
print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
"print layout information for each type encountered (default: no)"),
print_vtable_sizes: bool = (false, parse_bool, [UNTRACKED],
"print size comparison between old and new vtable layouts (default: no)"),
proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
"show backtraces for panics during proc-macro execution (default: no)"),
proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread,
Expand Down
62 changes: 0 additions & 62 deletions tests/ui/traits/object/print_vtable_sizes.rs

This file was deleted.

11 changes: 0 additions & 11 deletions tests/ui/traits/object/print_vtable_sizes.stdout

This file was deleted.