Auto merge of #123029 - matthiaskrgr:rollup-6qsevhx, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #122858 (Tweak `parse_dot_suffix_expr`) - #122982 (Add more comments to the bootstrap code that handles `tests/coverage`) - #122990 (Clarify transmute example) - #122995 (Clean up unnecessary headers/flags in coverage mir-opt tests) - #123003 (CFI: Handle dyn with no principal) - #123005 (CFI: Support complex receivers) - #123020 (Temporarily remove nnethercote from the review rotation.) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
af98101ed8
@ -4634,6 +4634,7 @@ dependencies = [
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"rustc_trait_selection",
|
||||
"tracing",
|
||||
"twox-hash",
|
||||
]
|
||||
|
@ -16,7 +16,6 @@
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::Spacing;
|
||||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
|
||||
@ -999,13 +998,57 @@ fn parse_expr_dot_or_call_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<
|
||||
}
|
||||
|
||||
pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
|
||||
// At this point we've consumed something like `expr.` and `self.token` holds the token
|
||||
// after the dot.
|
||||
match self.token.uninterpolate().kind {
|
||||
token::Ident(..) => self.parse_dot_suffix(base, lo),
|
||||
token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
|
||||
Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None))
|
||||
let ident_span = self.token.span;
|
||||
self.bump();
|
||||
Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix))
|
||||
}
|
||||
token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => {
|
||||
Ok(self.parse_expr_tuple_field_access_float(lo, base, symbol, suffix))
|
||||
Ok(match self.break_up_float(symbol, self.token.span) {
|
||||
// 1e2
|
||||
DestructuredFloat::Single(sym, _sp) => {
|
||||
// `foo.1e2`: a single complete dot access, fully consumed. We end up with
|
||||
// the `1e2` token in `self.prev_token` and the following token in
|
||||
// `self.token`.
|
||||
let ident_span = self.token.span;
|
||||
self.bump();
|
||||
self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix)
|
||||
}
|
||||
// 1.
|
||||
DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => {
|
||||
// `foo.1.`: a single complete dot access and the start of another.
|
||||
// We end up with the `sym` (`1`) token in `self.prev_token` and a dot in
|
||||
// `self.token`.
|
||||
assert!(suffix.is_none());
|
||||
self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span);
|
||||
self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing));
|
||||
self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None)
|
||||
}
|
||||
// 1.2 | 1.2e3
|
||||
DestructuredFloat::MiddleDot(
|
||||
sym1,
|
||||
ident1_span,
|
||||
_dot_span,
|
||||
sym2,
|
||||
ident2_span,
|
||||
) => {
|
||||
// `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with
|
||||
// the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following
|
||||
// token in `self.token`.
|
||||
let next_token2 =
|
||||
Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span);
|
||||
self.bump_with((next_token2, self.token_spacing));
|
||||
self.bump();
|
||||
let base1 =
|
||||
self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None);
|
||||
self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix)
|
||||
}
|
||||
DestructuredFloat::Error => base,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.error_unexpected_after_dot();
|
||||
@ -1119,41 +1162,6 @@ enum FloatComponent {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expr_tuple_field_access_float(
|
||||
&mut self,
|
||||
lo: Span,
|
||||
base: P<Expr>,
|
||||
float: Symbol,
|
||||
suffix: Option<Symbol>,
|
||||
) -> P<Expr> {
|
||||
match self.break_up_float(float, self.token.span) {
|
||||
// 1e2
|
||||
DestructuredFloat::Single(sym, _sp) => {
|
||||
self.parse_expr_tuple_field_access(lo, base, sym, suffix, None)
|
||||
}
|
||||
// 1.
|
||||
DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => {
|
||||
assert!(suffix.is_none());
|
||||
self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span);
|
||||
let next_token = (Token::new(token::Dot, dot_span), self.token_spacing);
|
||||
self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token))
|
||||
}
|
||||
// 1.2 | 1.2e3
|
||||
DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => {
|
||||
self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span);
|
||||
// This needs to be `Spacing::Alone` to prevent regressions.
|
||||
// See issue #76399 and PR #76285 for more details
|
||||
let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone);
|
||||
let base1 =
|
||||
self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1));
|
||||
let next_token2 = Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span);
|
||||
self.bump_with((next_token2, self.token_spacing)); // `.`
|
||||
self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None)
|
||||
}
|
||||
DestructuredFloat::Error => base,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the field access used in offset_of, matched by `$(e:expr)+`.
|
||||
/// Currently returns a list of idents. However, it should be possible in
|
||||
/// future to also do array indices, which might be arbitrary expressions.
|
||||
@ -1255,24 +1263,18 @@ fn parse_floating_field_access(&mut self) -> PResult<'a, P<[Ident]>> {
|
||||
Ok(fields.into_iter().collect())
|
||||
}
|
||||
|
||||
fn parse_expr_tuple_field_access(
|
||||
fn mk_expr_tuple_field_access(
|
||||
&mut self,
|
||||
lo: Span,
|
||||
ident_span: Span,
|
||||
base: P<Expr>,
|
||||
field: Symbol,
|
||||
suffix: Option<Symbol>,
|
||||
next_token: Option<(Token, Spacing)>,
|
||||
) -> P<Expr> {
|
||||
match next_token {
|
||||
Some(next_token) => self.bump_with(next_token),
|
||||
None => self.bump(),
|
||||
}
|
||||
let span = self.prev_token.span;
|
||||
let field = ExprKind::Field(base, Ident::new(field, span));
|
||||
if let Some(suffix) = suffix {
|
||||
self.expect_no_tuple_index_suffix(span, suffix);
|
||||
self.expect_no_tuple_index_suffix(ident_span, suffix);
|
||||
}
|
||||
self.mk_expr(lo.to(span), field)
|
||||
self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span)))
|
||||
}
|
||||
|
||||
/// Parse a function call expression, `expr(...)`.
|
||||
|
@ -15,6 +15,7 @@ rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
tracing = "0.1"
|
||||
twox-hash = "1.6.3"
|
||||
# tidy-alphabetical-end
|
||||
|
@ -90,6 +90,7 @@
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(let_chains)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[macro_use]
|
||||
|
@ -11,6 +11,7 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{
|
||||
self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
|
||||
TermKind, Ty, TyCtxt, UintTy,
|
||||
@ -21,7 +22,9 @@
|
||||
use rustc_target::abi::call::{Conv, FnAbi, PassMode};
|
||||
use rustc_target::abi::Integer;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits;
|
||||
use std::fmt::Write as _;
|
||||
use std::iter;
|
||||
|
||||
use crate::typeid::TypeIdOptions;
|
||||
|
||||
@ -747,9 +750,8 @@ fn transform_predicates<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> &'tcx List<ty::PolyExistentialPredicate<'tcx>> {
|
||||
let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| match predicate.skip_binder() {
|
||||
tcx.mk_poly_existential_predicates_from_iter(predicates.iter().filter_map(|predicate| {
|
||||
match predicate.skip_binder() {
|
||||
ty::ExistentialPredicate::Trait(trait_ref) => {
|
||||
let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id);
|
||||
Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait(
|
||||
@ -758,9 +760,8 @@ fn transform_predicates<'tcx>(
|
||||
}
|
||||
ty::ExistentialPredicate::Projection(..) => None,
|
||||
ty::ExistentialPredicate::AutoTrait(..) => Some(predicate),
|
||||
})
|
||||
.collect();
|
||||
tcx.mk_poly_existential_predicates(&predicates)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/// Transforms args for being encoded and used in the substitution dictionary.
|
||||
@ -1115,51 +1116,46 @@ pub fn typeid_for_instance<'tcx>(
|
||||
instance.args = strip_receiver_auto(tcx, instance.args)
|
||||
}
|
||||
|
||||
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
||||
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
|
||||
{
|
||||
let impl_method = tcx.associated_item(instance.def_id());
|
||||
let method_id = impl_method
|
||||
.trait_item_def_id
|
||||
.expect("Part of a trait implementation, but not linked to the def_id?");
|
||||
let trait_method = tcx.associated_item(method_id);
|
||||
if traits::is_vtable_safe_method(tcx, trait_ref.skip_binder().def_id, trait_method) {
|
||||
// Trait methods will have a Self polymorphic parameter, where the concreteized
|
||||
// implementatation will not. We need to walk back to the more general trait method
|
||||
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
||||
instance.args,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
trait_ref,
|
||||
);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
|
||||
// At the call site, any call to this concrete function through a vtable will be
|
||||
// `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
|
||||
// original method id, and we've recovered the trait arguments, we can make the callee
|
||||
// instance we're computing the alias set for match the caller instance.
|
||||
//
|
||||
// Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
|
||||
// If we ever *do* start encoding the vtable index, we will need to generate an alias set
|
||||
// based on which vtables we are putting this method into, as there will be more than one
|
||||
// index value when supertraits are involved.
|
||||
instance.def = ty::InstanceDef::Virtual(method_id, 0);
|
||||
let abstract_trait_args =
|
||||
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
|
||||
}
|
||||
}
|
||||
|
||||
let fn_abi = tcx
|
||||
.fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty())))
|
||||
.unwrap_or_else(|instance| {
|
||||
bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance)
|
||||
});
|
||||
|
||||
// If this instance is a method and self is a reference, get the impl it belongs to
|
||||
let impl_def_id = tcx.impl_of_method(instance.def_id());
|
||||
if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() {
|
||||
// If this impl is not an inherent impl, get the trait it implements
|
||||
if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) {
|
||||
// Transform the concrete self into a reference to a trait object
|
||||
let existential_predicate = trait_ref.map_bound(|trait_ref| {
|
||||
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
|
||||
tcx, trait_ref,
|
||||
))
|
||||
});
|
||||
let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(
|
||||
existential_predicate.skip_binder(),
|
||||
)]);
|
||||
// Is the concrete self mutable?
|
||||
let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
|
||||
Ty::new_mut_ref(
|
||||
tcx,
|
||||
tcx.lifetimes.re_erased,
|
||||
Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
|
||||
)
|
||||
} else {
|
||||
Ty::new_imm_ref(
|
||||
tcx,
|
||||
tcx.lifetimes.re_erased,
|
||||
Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
|
||||
)
|
||||
};
|
||||
|
||||
// Replace the concrete self in an fn_abi clone by the reference to a trait object
|
||||
let mut fn_abi = fn_abi.clone();
|
||||
// HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the
|
||||
// other fields are never used.
|
||||
fn_abi.args[0].layout.ty = self_ty;
|
||||
|
||||
return typeid_for_fnabi(tcx, &fn_abi, options);
|
||||
}
|
||||
}
|
||||
|
||||
typeid_for_fnabi(tcx, fn_abi, options)
|
||||
}
|
||||
|
||||
@ -1171,14 +1167,50 @@ fn strip_receiver_auto<'tcx>(
|
||||
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
|
||||
bug!("Tried to strip auto traits from non-dynamic type {ty}");
|
||||
};
|
||||
let filtered_preds =
|
||||
if preds.principal().is_some() {
|
||||
let new_rcvr = if preds.principal().is_some() {
|
||||
let filtered_preds =
|
||||
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
|
||||
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
|
||||
}))
|
||||
} else {
|
||||
ty::List::empty()
|
||||
};
|
||||
let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind);
|
||||
}));
|
||||
Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind)
|
||||
} else {
|
||||
// If there's no principal type, re-encode it as a unit, since we don't know anything
|
||||
// about it. This technically discards the knowledge that it was a type that was made
|
||||
// into a trait object at some point, but that's not a lot.
|
||||
tcx.types.unit
|
||||
};
|
||||
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
|
||||
}
|
||||
|
||||
fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
|
||||
assert!(!poly_trait_ref.has_non_region_param());
|
||||
let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
|
||||
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
|
||||
});
|
||||
let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref)
|
||||
.flat_map(|super_poly_trait_ref| {
|
||||
tcx.associated_items(super_poly_trait_ref.def_id())
|
||||
.in_definition_order()
|
||||
.filter(|item| item.kind == ty::AssocKind::Type)
|
||||
.map(move |assoc_ty| {
|
||||
super_poly_trait_ref.map_bound(|super_trait_ref| {
|
||||
let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args);
|
||||
let resolved = tcx.normalize_erasing_regions(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
alias_ty.to_ty(tcx),
|
||||
);
|
||||
ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
|
||||
def_id: assoc_ty.def_id,
|
||||
args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args,
|
||||
term: resolved.into(),
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
|
||||
let preds = tcx.mk_poly_existential_predicates_from_iter(
|
||||
iter::once(principal_pred).chain(assoc_preds.into_iter()),
|
||||
);
|
||||
Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
|
||||
}
|
||||
|
@ -1356,7 +1356,7 @@ pub const fn unlikely(b: bool) -> bool {
|
||||
/// let v_clone = v_orig.clone();
|
||||
///
|
||||
/// // This is the suggested, safe way.
|
||||
/// // It does copy the entire vector, though, into a new array.
|
||||
/// // It may copy the entire vector into a new one though, but also may not.
|
||||
/// let v_collected = v_clone.into_iter()
|
||||
/// .map(Some)
|
||||
/// .collect::<Vec<Option<&i32>>>();
|
||||
|
@ -1289,9 +1289,9 @@ fn run(self, builder: &Builder<'_>) {
|
||||
/// Adapted from [`test_definitions`].
|
||||
macro_rules! coverage_test_alias {
|
||||
($name:ident {
|
||||
alias_and_mode: $alias_and_mode:expr,
|
||||
default: $default:expr,
|
||||
only_hosts: $only_hosts:expr $(,)?
|
||||
alias_and_mode: $alias_and_mode:expr, // &'static str
|
||||
default: $default:expr, // bool
|
||||
only_hosts: $only_hosts:expr $(,)? // bool
|
||||
}) => {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct $name {
|
||||
@ -1309,6 +1309,8 @@ impl Step for $name {
|
||||
const ONLY_HOSTS: bool = $only_hosts;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
// Register the mode name as a command-line alias.
|
||||
// This allows `x test coverage-map` and `x test coverage-run`.
|
||||
run.alias($alias_and_mode)
|
||||
}
|
||||
|
||||
@ -1319,8 +1321,7 @@ fn make_run(run: RunConfig<'_>) {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) {
|
||||
Coverage { compiler: self.compiler, target: self.target }
|
||||
.run_unified_suite(builder, Self::MODE)
|
||||
Coverage::run_coverage_tests(builder, self.compiler, self.target, Self::MODE);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1449,11 +1450,20 @@ fn run(self, builder: &Builder<'_>) {
|
||||
|
||||
default_test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly" });
|
||||
|
||||
/// Custom test step that is responsible for running the coverage tests
|
||||
/// in multiple different modes.
|
||||
/// Coverage tests are a bit more complicated than other test suites, because
|
||||
/// we want to run the same set of test files in multiple different modes,
|
||||
/// in a way that's convenient and flexible when invoked manually.
|
||||
///
|
||||
/// Each individual mode also has its own alias that will run the tests in
|
||||
/// just that mode.
|
||||
/// This combined step runs the specified tests (or all of `tests/coverage`)
|
||||
/// in both "coverage-map" and "coverage-run" modes.
|
||||
///
|
||||
/// Used by:
|
||||
/// - `x test coverage`
|
||||
/// - `x test tests/coverage`
|
||||
/// - `x test tests/coverage/trivial.rs` (etc)
|
||||
///
|
||||
/// (Each individual mode also has its own step that will run the tests in
|
||||
/// just that mode.)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Coverage {
|
||||
pub compiler: Compiler,
|
||||
@ -1464,24 +1474,41 @@ impl Coverage {
|
||||
const PATH: &'static str = "tests/coverage";
|
||||
const SUITE: &'static str = "coverage";
|
||||
|
||||
fn run_unified_suite(&self, builder: &Builder<'_>, mode: &'static str) {
|
||||
/// Runs the coverage test suite (or a user-specified subset) in one mode.
|
||||
///
|
||||
/// This same function is used by the multi-mode step ([`Coverage`]) and by
|
||||
/// the single-mode steps ([`CoverageMap`] and [`CoverageRun`]), to help
|
||||
/// ensure that they all behave consistently with each other, regardless of
|
||||
/// how the coverage tests have been invoked.
|
||||
fn run_coverage_tests(
|
||||
builder: &Builder<'_>,
|
||||
compiler: Compiler,
|
||||
target: TargetSelection,
|
||||
mode: &'static str,
|
||||
) {
|
||||
// Like many other test steps, we delegate to a `Compiletest` step to
|
||||
// actually run the tests. (See `test_definitions!`.)
|
||||
builder.ensure(Compiletest {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
compiler,
|
||||
target,
|
||||
mode,
|
||||
suite: Self::SUITE,
|
||||
path: Self::PATH,
|
||||
compare_mode: None,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for Coverage {
|
||||
type Output = ();
|
||||
// We rely on the individual CoverageMap/CoverageRun steps to run themselves.
|
||||
const DEFAULT: bool = false;
|
||||
// When manually invoked, try to run as much as possible.
|
||||
// Compiletest will automatically skip the "coverage-run" tests if necessary.
|
||||
const ONLY_HOSTS: bool = false;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
// Take responsibility for command-line paths within `tests/coverage`.
|
||||
run.suite_path(Self::PATH)
|
||||
}
|
||||
|
||||
@ -1492,20 +1519,26 @@ fn make_run(run: RunConfig<'_>) {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) {
|
||||
self.run_unified_suite(builder, CoverageMap::MODE);
|
||||
self.run_unified_suite(builder, CoverageRun::MODE);
|
||||
// Run the specified coverage tests (possibly all of them) in both modes.
|
||||
Self::run_coverage_tests(builder, self.compiler, self.target, CoverageMap::MODE);
|
||||
Self::run_coverage_tests(builder, self.compiler, self.target, CoverageRun::MODE);
|
||||
}
|
||||
}
|
||||
|
||||
// Aliases for running the coverage tests in only one mode.
|
||||
// Runs `tests/coverage` in "coverage-map" mode only.
|
||||
// Used by `x test` and `x test coverage-map`.
|
||||
coverage_test_alias!(CoverageMap {
|
||||
alias_and_mode: "coverage-map",
|
||||
default: true,
|
||||
only_hosts: false,
|
||||
});
|
||||
// Runs `tests/coverage` in "coverage-run" mode only.
|
||||
// Used by `x test` and `x test coverage-run`.
|
||||
coverage_test_alias!(CoverageRun {
|
||||
alias_and_mode: "coverage-run",
|
||||
default: true,
|
||||
// Compiletest knows how to automatically skip these tests when cross-compiling,
|
||||
// but skipping the whole step here makes it clearer that they haven't run at all.
|
||||
only_hosts: true,
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
fn bar() -> bool {
|
||||
let mut _0: bool;
|
||||
|
||||
+ coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:21:1 - 23:2;
|
||||
+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:19:1 - 21:2;
|
||||
+
|
||||
bb0: {
|
||||
+ Coverage::CounterIncrement(0);
|
||||
|
@ -9,11 +9,11 @@
|
||||
|
||||
+ coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Add, rhs: Counter(1) };
|
||||
+ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Subtract, rhs: Counter(1) };
|
||||
+ coverage Code(Counter(0)) => /the/src/instrument_coverage.rs:12:1 - 12:11;
|
||||
+ coverage Code(Expression(0)) => /the/src/instrument_coverage.rs:13:5 - 14:17;
|
||||
+ coverage Code(Expression(1)) => /the/src/instrument_coverage.rs:15:13 - 15:18;
|
||||
+ coverage Code(Counter(1)) => /the/src/instrument_coverage.rs:16:10 - 16:11;
|
||||
+ coverage Code(Expression(1)) => /the/src/instrument_coverage.rs:18:1 - 18:2;
|
||||
+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:10:1 - 10:11;
|
||||
+ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:11:5 - 12:17;
|
||||
+ coverage Code(Expression(1)) => $DIR/instrument_coverage.rs:13:13 - 13:18;
|
||||
+ coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:14:10 - 14:11;
|
||||
+ coverage Code(Expression(1)) => $DIR/instrument_coverage.rs:16:1 - 16:2;
|
||||
+
|
||||
bb0: {
|
||||
+ Coverage::CounterIncrement(0);
|
||||
|
@ -1,11 +1,9 @@
|
||||
// skip-filecheck
|
||||
// Test that `-C instrument-coverage` injects Coverage statements. The Coverage Counter statements
|
||||
// are later converted into LLVM instrprof.increment intrinsics, during codegen.
|
||||
// Test that `-C instrument-coverage` injects Coverage statements.
|
||||
// The Coverage::CounterIncrement statements are later converted into LLVM
|
||||
// instrprof.increment intrinsics, during codegen.
|
||||
|
||||
//@ unit-test: InstrumentCoverage
|
||||
//@ needs-profiler-support
|
||||
//@ ignore-windows
|
||||
//@ compile-flags: -C instrument-coverage --remap-path-prefix={{src-base}}=/the/src
|
||||
//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime
|
||||
|
||||
// EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
|
||||
// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
|
||||
@ -22,17 +20,9 @@ fn bar() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// Note that the MIR with injected coverage intrinsics includes references to source locations,
|
||||
// including the source file absolute path. Typically, MIR pretty print output with file
|
||||
// references are safe because the file prefixes are substituted with `$DIR`, but in this case
|
||||
// the file references are encoded as function arguments, with an `Operand` type representation
|
||||
// (`Slice` `Allocation` interned byte array) that cannot be normalized by simple substitution.
|
||||
//
|
||||
// The first workaround is to use the `SourceMap`-supported `--remap-path-prefix` option; however,
|
||||
// the implementation of the `--remap-path-prefix` option currently joins the new prefix and the
|
||||
// remaining source path with an OS-specific path separator (`\` on Windows). This difference still
|
||||
// shows up in the byte array representation of the path, causing Windows tests to fail to match
|
||||
// blessed results baselined with a `/` path separator.
|
||||
//
|
||||
// Since this `mir-opt` test does not have any significant platform dependencies, other than the
|
||||
// path separator differences, the final workaround is to disable testing on Windows.
|
||||
// CHECK: coverage ExpressionId({{[0-9]+}}) =>
|
||||
// CHECK-DAG: coverage Code(Counter({{[0-9]+}})) =>
|
||||
// CHECK-DAG: coverage Code(Expression({{[0-9]+}})) =>
|
||||
// CHECK: bb0:
|
||||
// CHECK-DAG: Coverage::ExpressionUsed({{[0-9]+}})
|
||||
// CHECK-DAG: Coverage::CounterIncrement({{[0-9]+}})
|
||||
|
@ -5,15 +5,15 @@
|
||||
let mut _0: ();
|
||||
let mut _1: bool;
|
||||
|
||||
coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0)
|
||||
coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0)
|
||||
|
||||
coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) };
|
||||
coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) };
|
||||
coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36;
|
||||
coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39;
|
||||
coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40;
|
||||
coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2;
|
||||
coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36;
|
||||
coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36;
|
||||
coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39;
|
||||
coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40;
|
||||
coverage Code(Expression(1)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2;
|
||||
coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36;
|
||||
|
||||
bb0: {
|
||||
Coverage::CounterIncrement(0);
|
||||
|
@ -5,15 +5,15 @@
|
||||
let mut _0: ();
|
||||
let mut _1: bool;
|
||||
|
||||
coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0)
|
||||
coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0)
|
||||
|
||||
+ coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) };
|
||||
+ coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) };
|
||||
+ coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36;
|
||||
+ coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39;
|
||||
+ coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40;
|
||||
+ coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2;
|
||||
+ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36;
|
||||
+ coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36;
|
||||
+ coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39;
|
||||
+ coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40;
|
||||
+ coverage Code(Expression(1)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2;
|
||||
+ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36;
|
||||
+
|
||||
bb0: {
|
||||
+ Coverage::CounterIncrement(0);
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
//@ unit-test: InstrumentCoverage
|
||||
//@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime
|
||||
//@ compile-flags: --remap-path-prefix={{src-base}}=/the/src
|
||||
|
||||
// EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff
|
||||
// EMIT_MIR instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
|
||||
|
42
tests/ui/sanitizer/cfi-complex-receiver.rs
Normal file
42
tests/ui/sanitizer/cfi-complex-receiver.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Check that more complex receivers work:
|
||||
// * Arc<dyn Foo> as for custom receivers
|
||||
// * &dyn Bar<T=Baz> for type constraints
|
||||
|
||||
//@ needs-sanitizer-cfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
|
||||
//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0
|
||||
//@ run-pass
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
trait Foo {
|
||||
fn foo(self: Arc<Self>);
|
||||
}
|
||||
|
||||
struct FooImpl;
|
||||
|
||||
impl Foo for FooImpl {
|
||||
fn foo(self: Arc<Self>) {}
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
type T;
|
||||
fn bar(&self) -> Self::T;
|
||||
}
|
||||
|
||||
struct BarImpl;
|
||||
|
||||
impl Bar for BarImpl {
|
||||
type T = i32;
|
||||
fn bar(&self) -> Self::T { 7 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo: Arc<dyn Foo> = Arc::new(FooImpl);
|
||||
foo.foo();
|
||||
|
||||
let bar: &dyn Bar<T=i32> = &BarImpl;
|
||||
assert_eq!(bar.bar(), 7);
|
||||
}
|
21
tests/ui/sanitizer/cfi-drop-no-principal.rs
Normal file
21
tests/ui/sanitizer/cfi-drop-no-principal.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// Check that dropping a trait object without a principal trait succeeds
|
||||
|
||||
//@ needs-sanitizer-cfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries works
|
||||
//@ only-linux
|
||||
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
|
||||
//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0
|
||||
// FIXME(#118761) Should be run-pass once the labels on drop are compatible.
|
||||
// This test is being landed ahead of that to test that the compiler doesn't ICE while labeling the
|
||||
// callsite for a drop, but the vtable doesn't have the correct label yet.
|
||||
//@ build-pass
|
||||
|
||||
struct CustomDrop;
|
||||
|
||||
impl Drop for CustomDrop {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = Box::new(CustomDrop) as Box<dyn Send>;
|
||||
}
|
@ -775,7 +775,6 @@ compiler-team = [
|
||||
compiler-team-contributors = [
|
||||
"@TaKO8Ki",
|
||||
"@Nadrieril",
|
||||
"@nnethercote",
|
||||
"@fmease",
|
||||
"@fee1-dead",
|
||||
]
|
||||
@ -831,17 +830,14 @@ parser = [
|
||||
"@compiler-errors",
|
||||
"@davidtwco",
|
||||
"@estebank",
|
||||
"@nnethercote",
|
||||
"@petrochenkov",
|
||||
"@spastorino",
|
||||
]
|
||||
lexer = [
|
||||
"@nnethercote",
|
||||
"@petrochenkov",
|
||||
"@estebank",
|
||||
]
|
||||
arena = [
|
||||
"@nnethercote",
|
||||
"@spastorino",
|
||||
]
|
||||
mir = [
|
||||
|
Loading…
Reference in New Issue
Block a user