Auto merge of #127982 - matthiaskrgr:rollup-nzyvphj, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #127295 (CFI: Support provided methods on traits)
 - #127814 (`C-cmse-nonsecure-call`: improved error messages)
 - #127949 (fix: explain E0120 better cover cases when its raised)
 - #127966 (Use structured suggestions for unconstrained generic parameters on impl blocks)
 - #127976 (Lazy type aliases: Diagostics: Detect bivariant ty params that are only used recursively)
 - #127978 (Avoid ref when using format! for perf)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-07-19 18:40:33 +00:00
commit ff4b39867e
28 changed files with 744 additions and 212 deletions

View File

@ -1,7 +1,7 @@
Drop was implemented on a trait, which is not allowed: only structs and
enums can implement Drop.
`Drop` was implemented on a trait object or reference, which is not allowed;
only structs, enums, and unions can implement Drop.
Erroneous code example:
Erroneous code examples:
```compile_fail,E0120
trait MyTrait {}
@ -11,8 +11,16 @@ impl Drop for MyTrait {
}
```
A workaround for this problem is to wrap the trait up in a struct, and implement
Drop on that:
```compile_fail,E0120
struct Concrete {}
impl Drop for &'_ mut Concrete {
fn drop(&mut self) {}
}
```
A workaround for traits is to create a wrapper struct with a generic type,
add a trait bound to the type, and implement `Drop` on the wrapper:
```
trait MyTrait {}
@ -24,13 +32,13 @@ impl <T: MyTrait> Drop for MyWrapper<T> {
```
Alternatively, wrapping trait objects requires something:
Alternatively, the `Drop` wrapper can contain the trait object:
```
trait MyTrait {}
//or Box<MyTrait>, if you wanted an owned trait object
struct MyWrapper<'a> { foo: &'a MyTrait }
// or Box<dyn MyTrait>, if you wanted an owned trait object
struct MyWrapper<'a> { foo: &'a dyn MyTrait }
impl <'a> Drop for MyWrapper<'a> {
fn drop(&mut self) {}

View File

@ -0,0 +1,39 @@
Functions marked as `C-cmse-nonsecure-call` place restrictions on their
inputs and outputs.
- inputs must fit in the 4 available 32-bit argument registers. Alignment
is relevant.
- outputs must either fit in 4 bytes, or be a foundational type of
size 8 (`i64`, `u64`, `f64`).
- no generics can be used in the signature
For more information,
see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases).
Erroneous code example:
```ignore (only fails on supported targets)
#![feature(abi_c_cmse_nonsecure_call)]
#[no_mangle]
pub fn test(
f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
) -> u32 {
f(1, 2, 3, 4, 5)
}
```
Arguments' alignment is respected. In the example below, padding is inserted
so that the `u64` argument is passed in registers r2 and r3. There is then no
room left for the final `f32` argument
```ignore (only fails on supported targets)
#![feature(abi_c_cmse_nonsecure_call)]
#[no_mangle]
pub fn test(
f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32,
) -> u32 {
f(1, 2, 3.0)
}
```

View File

@ -536,6 +536,7 @@ E0794: 0794,
E0795: 0795,
E0796: 0796,
E0797: 0797,
E0798: 0798,
);
)
}

View File

@ -58,6 +58,23 @@ hir_analysis_cannot_capture_late_bound_ty =
hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
.label = `for<...>` is here
hir_analysis_cmse_call_generic =
function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
hir_analysis_cmse_call_inputs_stack_spill =
arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
.label = {$plural ->
[false] this argument doesn't
*[true] these arguments don't
} fit in the available registers
.note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
hir_analysis_cmse_call_output_stack_spill =
return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
.label = this type doesn't fit in the available registers
.note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
@ -519,6 +536,11 @@ hir_analysis_typeof_reserved_keyword_used =
.suggestion = consider replacing `typeof(...)` with an actual type
.label = reserved keyword
hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates
.label = unconstrained {$param_def_kind}
.const_param_note = expressions using a const parameter must map each value to a distinct output value
.const_param_note2 = proving the result of expressions other than the parameter are unique is not supported
hir_analysis_unconstrained_opaque_type = unconstrained opaque type
.note = `{$name}` must be used in combination with a concrete type within the same {$what}

View File

@ -1922,31 +1922,24 @@ fn report_bivariance<'tcx>(
);
if !usage_spans.is_empty() {
// First, check if the ADT is (probably) cyclical. We say probably here, since
// we're not actually looking into substitutions, just walking through fields.
// And we only recurse into the fields of ADTs, and not the hidden types of
// opaques or anything else fancy.
// First, check if the ADT/LTA is (probably) cyclical. We say probably here, since we're
// not actually looking into substitutions, just walking through fields / the "RHS".
// We don't recurse into the hidden types of opaques or anything else fancy.
let item_def_id = item.owner_id.to_def_id();
let is_probably_cyclical = if matches!(
tcx.def_kind(item_def_id),
DefKind::Struct | DefKind::Union | DefKind::Enum
) {
IsProbablyCyclical { tcx, adt_def_id: item_def_id, seen: Default::default() }
.visit_all_fields(tcx.adt_def(item_def_id))
.is_break()
} else {
false
};
// If the ADT is cyclical, then if at least one usage of the type parameter or
// the `Self` alias is present in the, then it's probably a cyclical struct, and
// we should call those parameter usages recursive rather than just saying they're
// unused...
let is_probably_cyclical =
IsProbablyCyclical { tcx, item_def_id, seen: Default::default() }
.visit_def(item_def_id)
.is_break();
// If the ADT/LTA is cyclical, then if at least one usage of the type parameter or
// the `Self` alias is present in the, then it's probably a cyclical struct/ type
// alias, and we should call those parameter usages recursive rather than just saying
// they're unused...
//
// We currently report *all* of the parameter usages, since computing the exact
// subset is very involved, and the fact we're mentioning recursion at all is
// likely to guide the user in the right direction.
if is_probably_cyclical {
let diag = tcx.dcx().create_err(errors::RecursiveGenericParameter {
return tcx.dcx().emit_err(errors::RecursiveGenericParameter {
spans: usage_spans,
param_span: param.span,
param_name,
@ -1954,7 +1947,6 @@ fn report_bivariance<'tcx>(
help,
note: (),
});
return diag.emit();
}
}
@ -1974,42 +1966,51 @@ fn report_bivariance<'tcx>(
diag.emit()
}
/// Detects cases where an ADT is trivially cyclical -- we want to detect this so
/// /we only mention that its parameters are used cyclically if the ADT is truly
/// Detects cases where an ADT/LTA is trivially cyclical -- we want to detect this so
/// we only mention that its parameters are used cyclically if the ADT/LTA is truly
/// cyclical.
///
/// Notably, we don't consider substitutions here, so this may have false positives.
struct IsProbablyCyclical<'tcx> {
tcx: TyCtxt<'tcx>,
adt_def_id: DefId,
item_def_id: DefId,
seen: FxHashSet<DefId>,
}
impl<'tcx> IsProbablyCyclical<'tcx> {
fn visit_all_fields(&mut self, adt_def: ty::AdtDef<'tcx>) -> ControlFlow<(), ()> {
for field in adt_def.all_fields() {
self.tcx.type_of(field.did).instantiate_identity().visit_with(self)?;
fn visit_def(&mut self, def_id: DefId) -> ControlFlow<(), ()> {
match self.tcx.def_kind(def_id) {
DefKind::Struct | DefKind::Enum | DefKind::Union => {
self.tcx.adt_def(def_id).all_fields().try_for_each(|field| {
self.tcx.type_of(field.did).instantiate_identity().visit_with(self)
})
}
DefKind::TyAlias if self.tcx.type_alias_is_lazy(def_id) => {
self.tcx.type_of(def_id).instantiate_identity().visit_with(self)
}
_ => ControlFlow::Continue(()),
}
ControlFlow::Continue(())
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsProbablyCyclical<'tcx> {
type Result = ControlFlow<(), ()>;
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<(), ()> {
if let Some(adt_def) = t.ty_adt_def() {
if adt_def.did() == self.adt_def_id {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<(), ()> {
let def_id = match ty.kind() {
ty::Adt(adt_def, _) => Some(adt_def.did()),
ty::Alias(ty::Weak, alias_ty) => Some(alias_ty.def_id),
_ => None,
};
if let Some(def_id) = def_id {
if def_id == self.item_def_id {
return ControlFlow::Break(());
}
if self.seen.insert(adt_def.did()) {
self.visit_all_fields(adt_def)?;
if self.seen.insert(def_id) {
self.visit_def(def_id)?;
}
}
t.super_visit_with(self)
ty.super_visit_with(self)
}
}

View File

@ -1604,6 +1604,20 @@ pub(crate) enum UnusedGenericParameterHelp {
TyAlias { param_name: Ident },
}
#[derive(Diagnostic)]
#[diag(hir_analysis_unconstrained_generic_parameter)]
pub(crate) struct UnconstrainedGenericParameter {
#[primary_span]
#[label]
pub span: Span,
pub param_name: Symbol,
pub param_def_kind: &'static str,
#[note(hir_analysis_const_param_note)]
pub const_param_note: Option<()>,
#[note(hir_analysis_const_param_note2)]
pub const_param_note2: Option<()>,
}
#[derive(Diagnostic)]
pub enum UnnamedFieldsRepr<'a> {
#[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)]
@ -1673,3 +1687,30 @@ pub struct InvalidReceiverTy<'tcx> {
#[note]
#[help]
pub struct EffectsWithoutNextSolver;
#[derive(Diagnostic)]
#[diag(hir_analysis_cmse_call_inputs_stack_spill, code = E0798)]
#[note]
pub struct CmseCallInputsStackSpill {
#[primary_span]
#[label]
pub span: Span,
pub plural: bool,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_cmse_call_output_stack_spill, code = E0798)]
#[note(hir_analysis_note1)]
#[note(hir_analysis_note2)]
pub struct CmseCallOutputStackSpill {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_cmse_call_generic, code = E0798)]
pub struct CmseCallGeneric {
#[primary_span]
pub span: Span,
}

View File

@ -0,0 +1,156 @@
use rustc_errors::DiagCtxtHandle;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::{self, ParamEnv, TyCtxt};
use rustc_span::Span;
use rustc_target::spec::abi;
use crate::errors;
/// Check conditions on inputs and outputs that the cmse ABIs impose: arguments and results MUST be
/// returned via registers (i.e. MUST NOT spill to the stack). LLVM will also validate these
/// conditions, but by checking them here rustc can emit nicer error messages.
pub fn validate_cmse_abi<'tcx>(
tcx: TyCtxt<'tcx>,
dcx: DiagCtxtHandle<'_>,
hir_id: HirId,
abi: abi::Abi,
fn_sig: ty::PolyFnSig<'tcx>,
) {
if let abi::Abi::CCmseNonSecureCall = abi {
let hir_node = tcx.hir_node(hir_id);
let hir::Node::Ty(hir::Ty {
span: bare_fn_span,
kind: hir::TyKind::BareFn(bare_fn_ty),
..
}) = hir_node
else {
// might happen when this ABI is used incorrectly. That will be handled elsewhere
return;
};
match is_valid_cmse_inputs(tcx, fn_sig) {
Ok(Ok(())) => {}
Ok(Err(index)) => {
// fn(x: u32, u32, u32, u16, y: u16) -> u32,
// ^^^^^^
let span = bare_fn_ty.param_names[index]
.span
.to(bare_fn_ty.decl.inputs[index].span)
.to(bare_fn_ty.decl.inputs.last().unwrap().span);
let plural = bare_fn_ty.param_names.len() - index != 1;
dcx.emit_err(errors::CmseCallInputsStackSpill { span, plural });
}
Err(layout_err) => {
if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
dcx.emit_err(err);
}
}
}
match is_valid_cmse_output(tcx, fn_sig) {
Ok(true) => {}
Ok(false) => {
let span = bare_fn_ty.decl.output.span();
dcx.emit_err(errors::CmseCallOutputStackSpill { span });
}
Err(layout_err) => {
if let Some(err) = cmse_layout_err(layout_err, *bare_fn_span) {
dcx.emit_err(err);
}
}
};
}
}
/// Returns whether the inputs will fit into the available registers
fn is_valid_cmse_inputs<'tcx>(
tcx: TyCtxt<'tcx>,
fn_sig: ty::PolyFnSig<'tcx>,
) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> {
let mut span = None;
let mut accum = 0u64;
for (index, arg_def) in fn_sig.inputs().iter().enumerate() {
let layout = tcx.layout_of(ParamEnv::reveal_all().and(*arg_def.skip_binder()))?;
let align = layout.layout.align().abi.bytes();
let size = layout.layout.size().bytes();
accum += size;
accum = accum.next_multiple_of(Ord::max(4, align));
// i.e. exceeds 4 32-bit registers
if accum > 16 {
span = span.or(Some(index));
}
}
match span {
None => Ok(Ok(())),
Some(span) => Ok(Err(span)),
}
}
/// Returns whether the output will fit into the available registers
fn is_valid_cmse_output<'tcx>(
tcx: TyCtxt<'tcx>,
fn_sig: ty::PolyFnSig<'tcx>,
) -> Result<bool, &'tcx LayoutError<'tcx>> {
let mut ret_ty = fn_sig.output().skip_binder();
let layout = tcx.layout_of(ParamEnv::reveal_all().and(ret_ty))?;
let size = layout.layout.size().bytes();
if size <= 4 {
return Ok(true);
} else if size > 8 {
return Ok(false);
}
// next we need to peel any repr(transparent) layers off
'outer: loop {
let ty::Adt(adt_def, args) = ret_ty.kind() else {
break;
};
if !adt_def.repr().transparent() {
break;
}
// the first field with non-trivial size and alignment must be the data
for variant_def in adt_def.variants() {
for field_def in variant_def.fields.iter() {
let ty = field_def.ty(tcx, args);
let layout = tcx.layout_of(ParamEnv::reveal_all().and(ty))?;
if !layout.layout.is_1zst() {
ret_ty = ty;
continue 'outer;
}
}
}
}
Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
}
fn cmse_layout_err<'tcx>(
layout_err: &'tcx LayoutError<'tcx>,
span: Span,
) -> Option<crate::errors::CmseCallGeneric> {
use LayoutError::*;
match layout_err {
Unknown(ty) => {
if ty.is_impl_trait() {
None // prevent double reporting of this error
} else {
Some(errors::CmseCallGeneric { span })
}
}
SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => {
None // not our job to report these
}
}
}

View File

@ -14,6 +14,7 @@
//! trait references and bounds.
mod bounds;
mod cmse;
pub mod errors;
pub mod generics;
mod lint;
@ -2378,6 +2379,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
// reject function types that violate cmse ABI requirements
cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty);
// Find any late-bound regions declared in return type that do
// not appear in the arguments. These are not well-formed.
//

View File

@ -8,15 +8,15 @@
//! specialization errors. These things can (and probably should) be
//! fixed, but for the moment it's easier to do these checks early.
use crate::constrained_generic_params as cgp;
use crate::{constrained_generic_params as cgp, errors::UnconstrainedGenericParameter};
use min_specialization::check_min_specialization;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{codes::*, struct_span_code_err};
use rustc_errors::codes::*;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use rustc_span::ErrorGuaranteed;
mod min_specialization;
@ -117,43 +117,34 @@ fn enforce_impl_params_are_constrained(
let mut res = Ok(());
for param in &impl_generics.own_params {
match param.kind {
let err = match param.kind {
// Disallow ANY unconstrained type parameters.
ty::GenericParamDefKind::Type { .. } => {
let param_ty = ty::ParamTy::for_def(param);
if !input_parameters.contains(&cgp::Parameter::from(param_ty)) {
res = Err(report_unused_parameter(
tcx,
tcx.def_span(param.def_id),
"type",
param_ty.name,
));
}
!input_parameters.contains(&cgp::Parameter::from(param_ty))
}
ty::GenericParamDefKind::Lifetime => {
let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
if lifetimes_in_associated_types.contains(&param_lt) && // (*)
lifetimes_in_associated_types.contains(&param_lt) && // (*)
!input_parameters.contains(&param_lt)
{
res = Err(report_unused_parameter(
tcx,
tcx.def_span(param.def_id),
"lifetime",
param.name,
));
}
}
ty::GenericParamDefKind::Const { .. } => {
let param_ct = ty::ParamConst::for_def(param);
if !input_parameters.contains(&cgp::Parameter::from(param_ct)) {
res = Err(report_unused_parameter(
tcx,
tcx.def_span(param.def_id),
"const",
param_ct.name,
));
}
!input_parameters.contains(&cgp::Parameter::from(param_ct))
}
};
if err {
let const_param_note =
matches!(param.kind, ty::GenericParamDefKind::Const { .. }).then_some(());
let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
span: tcx.def_span(param.def_id),
param_name: param.name,
param_def_kind: tcx.def_descr(param.def_id),
const_param_note,
const_param_note2: const_param_note,
});
diag.code(E0207);
res = Err(diag.emit());
}
}
res
@ -177,30 +168,3 @@ fn enforce_impl_params_are_constrained(
// associated types. I believe this is sound, because lifetimes
// used elsewhere are not projected back out.
}
fn report_unused_parameter(
tcx: TyCtxt<'_>,
span: Span,
kind: &str,
name: Symbol,
) -> ErrorGuaranteed {
let mut err = struct_span_code_err!(
tcx.dcx(),
span,
E0207,
"the {} parameter `{}` is not constrained by the \
impl trait, self type, or predicates",
kind,
name
);
err.span_label(span, format!("unconstrained {kind} parameter"));
if kind == "const" {
err.note(
"expressions using a const parameter must map each value to a distinct output value",
);
err.note(
"proving the result of expressions other than the parameter are unique is not supported",
);
}
err.emit()
}

View File

@ -9,10 +9,10 @@ use rustc_hir::LangItem;
use rustc_middle::bug;
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::{
self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable,
TypeVisitableExt, UintTy,
self, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy, List, TraitRef, Ty,
TyCtxt, TypeFoldable, TypeVisitableExt, UintTy,
};
use rustc_span::sym;
use rustc_span::{def_id::DefId, sym};
use rustc_trait_selection::traits;
use std::iter;
use tracing::{debug, instrument};
@ -360,41 +360,29 @@ pub fn transform_instance<'tcx>(
if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
// Perform type erasure for calls on trait objects by transforming self into a trait object
// of the trait that defines the method.
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);
let trait_id = trait_ref.skip_binder().def_id;
if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
&& tcx.is_object_safe(trait_id)
{
// 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));
if let Some((trait_ref, method_id, ancestor)) = implemented_method(tcx, instance) {
// 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::InstanceKind::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);
}
// 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::InstanceKind::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, ancestor, abstract_trait_args);
} else if tcx.is_closure_like(instance.def_id()) {
// We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
// instantiate it, and take the type of its only method as our own.
@ -452,3 +440,36 @@ pub fn transform_instance<'tcx>(
instance
}
fn implemented_method<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
) -> Option<(ty::EarlyBinder<'tcx, TraitRef<'tcx>>, DefId, DefId)> {
let trait_ref;
let method_id;
let trait_id;
let trait_method;
let ancestor = if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) {
// Implementation in an `impl` block
trait_ref = tcx.impl_trait_ref(impl_id)?;
let impl_method = tcx.associated_item(instance.def_id());
method_id = impl_method.trait_item_def_id?;
trait_method = tcx.associated_item(method_id);
trait_id = trait_ref.skip_binder().def_id;
impl_id
} else if let InstanceKind::Item(def_id) = instance.def
&& let Some(trait_method_bound) = tcx.opt_associated_item(def_id)
{
// Provided method in a `trait` block
trait_method = trait_method_bound;
method_id = instance.def_id();
trait_id = tcx.trait_of_item(method_id)?;
trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args));
trait_id
} else {
return None;
};
let vtable_possible =
traits::is_vtable_safe_method(tcx, trait_id, trait_method) && tcx.is_object_safe(trait_id);
vtable_possible.then_some((trait_ref, method_id, ancestor))
}

View File

@ -1026,7 +1026,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
/// assert_eq!(format!("{}", value), "a");
/// assert_eq!(format!("{:?}", value), "'a'");
///
/// let wrapped = fmt::FormatterFn(|f| write!(f, "{:?}", &value));
/// let wrapped = fmt::FormatterFn(|f| write!(f, "{value:?}"));
/// assert_eq!(format!("{}", wrapped), "'a'");
/// assert_eq!(format!("{:?}", wrapped), "'a'");
/// ```

View File

@ -56,7 +56,7 @@ fn test_join() {
let y = String::new();
let x = join!(async {
println!("{}", &y);
println!("{y}");
1
})
.await;

View File

@ -1544,10 +1544,10 @@ impl fmt::Debug for Literal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Literal")
// format the kind on one line even in {:#?} mode
.field("kind", &format_args!("{:?}", &self.0.kind))
.field("kind", &format_args!("{:?}", self.0.kind))
.field("symbol", &self.0.symbol)
// format `Some("...")` on one line even in {:#?} mode
.field("suffix", &format_args!("{:?}", &self.0.suffix))
.field("suffix", &format_args!("{:?}", self.0.suffix))
.field("span", &self.0.span)
.finish()
}

View File

@ -683,7 +683,7 @@ fn recursive_rmdir_toctou() {
let drop_canary_arc = Arc::new(());
let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
eprintln!("x: {:?}", &victim_del_path);
eprintln!("x: {victim_del_path:?}");
// victim just continuously removes `victim_del`
thread::spawn(move || {

View File

@ -775,7 +775,7 @@ impl Error {
///
/// impl Display for MyError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "MyError: {}", &self.v)
/// write!(f, "MyError: {}", self.v)
/// }
/// }
///

View File

@ -0,0 +1,21 @@
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for u32 {}
#[repr(C)]
struct Wrapper<T>(T);
struct Test<T: Copy> {
f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64, //~ ERROR cannot find type `U` in this scope
//~^ ERROR function pointer types may not have generic parameters
f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
//~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters
f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798]
f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798]
}

View File

@ -0,0 +1,47 @@
error: function pointer types may not have generic parameters
--> $DIR/generics.rs:15:42
|
LL | f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
| ^^^^^^^^^
error[E0412]: cannot find type `U` in this scope
--> $DIR/generics.rs:15:52
|
LL | struct Test<T: Copy> {
| - similarly named type parameter `T` defined here
LL | f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
| ^
|
help: a type parameter with a similar name exists
|
LL | f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(T, u32, u32, u32) -> u64,
| ~
help: you might be missing a type parameter
|
LL | struct Test<T: Copy, U> {
| +++
error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters
--> $DIR/generics.rs:17:43
|
LL | f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
| ^^^^^^^^^
|
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
--> $DIR/generics.rs:19:9
|
LL | f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
--> $DIR/generics.rs:20:9
|
LL | f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0412, E0562, E0798.
For more information about an error, try `rustc --explain E0412`.

View File

@ -1,24 +0,0 @@
//@ build-pass
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
#![no_core]
#[lang="sized"]
pub trait Sized { }
#[lang="copy"]
pub trait Copy { }
impl Copy for u32 {}
extern "rust-intrinsic" {
pub fn transmute<T, U>(e: T) -> U;
}
#[no_mangle]
pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 {
let non_secure_function = unsafe {
transmute::<usize, extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32>(
0x10000004,
)
};
non_secure_function(a, b, c, d)
}

View File

@ -1,27 +0,0 @@
//@ build-fail
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
#![no_core]
#[lang="sized"]
pub trait Sized { }
#[lang="copy"]
pub trait Copy { }
impl Copy for u32 {}
extern "rust-intrinsic" {
pub fn transmute<T, U>(e: T) -> U;
}
#[no_mangle]
pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 {
let non_secure_function = unsafe {
transmute::<
usize,
extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32>
(
0x10000004,
)
};
non_secure_function(a, b, c, d, e)
}

View File

@ -1,4 +0,0 @@
error: <unknown>:0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack
error: aborting due to 1 previous error

View File

@ -0,0 +1,23 @@
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for u32 {}
#[repr(C, align(16))]
#[allow(unused)]
pub struct AlignRelevant(u32);
#[no_mangle]
pub fn test(
f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), //~ ERROR [E0798]
f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16), //~ ERROR [E0798]
f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32), //~ ERROR [E0798]
f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32), //~ ERROR [E0798]
f5: extern "C-cmse-nonsecure-call" fn([u32; 5]), //~ ERROR [E0798]
) {
}

View File

@ -0,0 +1,43 @@
error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/params-via-stack.rs:17:63
|
LL | f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32),
| ^^^^^^^^^^^^^^ these arguments don't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/params-via-stack.rs:18:63
|
LL | f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),
| ^^^ this argument doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/params-via-stack.rs:19:53
|
LL | f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32),
| ^^^ this argument doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/params-via-stack.rs:20:58
|
LL | f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32),
| ^^^ this argument doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/params-via-stack.rs:21:43
|
LL | f5: extern "C-cmse-nonsecure-call" fn([u32; 5]),
| ^^^^^^^^ this argument doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0798`.

View File

@ -0,0 +1,54 @@
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for u32 {}
#[repr(C)]
pub struct ReprCU64(u64);
#[repr(C)]
pub struct ReprCBytes(u8, u8, u8, u8, u8);
#[repr(C)]
pub struct U64Compound(u32, u32);
#[repr(C, align(16))]
pub struct ReprCAlign16(u16);
#[no_mangle]
pub fn test(
f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, //~ ERROR [E0798]
f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, //~ ERROR [E0798]
f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, //~ ERROR [E0798]
f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, //~ ERROR [E0798]
f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5], //~ ERROR [E0798]
) {
}
#[allow(improper_ctypes_definitions)]
struct Test {
u128: extern "C-cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798]
i128: extern "C-cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798]
}
#[repr(C)]
pub union ReprCUnionU64 {
_unused: u64,
}
#[repr(Rust)]
pub union ReprRustUnionU64 {
_unused: u64,
}
#[no_mangle]
pub fn test_union(
f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ ERROR [E0798]
f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64, //~ ERROR [E0798]
) {
}

View File

@ -0,0 +1,84 @@
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:35:50
|
LL | u128: extern "C-cmse-nonsecure-call" fn() -> u128,
| ^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:36:50
|
LL | i128: extern "C-cmse-nonsecure-call" fn() -> i128,
| ^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:25:48
|
LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64,
| ^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:26:48
|
LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes,
| ^^^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:27:48
|
LL | f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound,
| ^^^^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:28:48
|
LL | f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16,
| ^^^^^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:29:48
|
LL | f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5],
| ^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:51:48
|
LL | f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64,
| ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> $DIR/return-via-stack.rs:52:48
|
LL | f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64,
| ^^^^^^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0798`.

View File

@ -0,0 +1,53 @@
//@ build-pass
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
//@ needs-llvm-components: arm
#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for u32 {}
#[repr(transparent)]
pub struct ReprTransparentStruct<T> {
_marker1: (),
_marker2: (),
field: T,
_marker3: (),
}
#[repr(transparent)]
pub enum ReprTransparentEnumU64 {
A(u64),
}
#[repr(C)]
pub struct U32Compound(u16, u16);
#[no_mangle]
#[allow(improper_ctypes_definitions)]
pub fn params(
f1: extern "C-cmse-nonsecure-call" fn(),
f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32),
f3: extern "C-cmse-nonsecure-call" fn(u64, u64),
f4: extern "C-cmse-nonsecure-call" fn(u128),
f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32),
f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStruct<u64>, U32Compound),
f7: extern "C-cmse-nonsecure-call" fn([u32; 4]),
) {
}
#[no_mangle]
pub fn returns(
f1: extern "C-cmse-nonsecure-call" fn() -> u32,
f2: extern "C-cmse-nonsecure-call" fn() -> u64,
f3: extern "C-cmse-nonsecure-call" fn() -> i64,
f4: extern "C-cmse-nonsecure-call" fn() -> f64,
f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4],
f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct<u64>,
f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct<ReprTransparentStruct<u64>>,
f8: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64,
f9: extern "C-cmse-nonsecure-call" fn() -> U32Compound,
) {
}

View File

@ -4,27 +4,27 @@ error[E0275]: overflow evaluating the requirement `Loop == _`
LL | impl Loop {}
| ^^^^
error[E0392]: type parameter `T` is never used
--> $DIR/inherent-impls-overflow.rs:14:12
error: type parameter `T` is only used recursively
--> $DIR/inherent-impls-overflow.rs:14:24
|
LL | type Poly0<T> = Poly1<(T,)>;
| ^ - `T` is named here, but is likely unused in the containing type
| - ^
| |
| unused type parameter
| type parameter must be used non-recursively in the definition
|
= help: consider removing `T` or referring to it in the body of the type alias
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
= note: all type parameters must be used in a non-recursive way in order to constrain their variance
error[E0392]: type parameter `T` is never used
--> $DIR/inherent-impls-overflow.rs:17:12
error: type parameter `T` is only used recursively
--> $DIR/inherent-impls-overflow.rs:17:24
|
LL | type Poly1<T> = Poly0<(T,)>;
| ^ - `T` is named here, but is likely unused in the containing type
| - ^
| |
| unused type parameter
| type parameter must be used non-recursively in the definition
|
= help: consider removing `T` or referring to it in the body of the type alias
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
= note: all type parameters must be used in a non-recursive way in order to constrain their variance
error[E0275]: overflow evaluating the requirement `Poly0<()> == _`
--> $DIR/inherent-impls-overflow.rs:21:6
@ -36,5 +36,4 @@ LL | impl Poly0<()> {}
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0275, E0392.
For more information about an error, try `rustc --explain E0275`.
For more information about this error, try `rustc --explain E0275`.

View File

@ -13,10 +13,10 @@ impl Loop {}
type Poly0<T> = Poly1<(T,)>;
//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
//[next]~^^ ERROR type parameter `T` is never used
//[next]~^^ ERROR type parameter `T` is only used recursively
type Poly1<T> = Poly0<(T,)>;
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
//[next]~^^ ERROR type parameter `T` is never used
//[next]~^^ ERROR type parameter `T` is only used recursively
impl Poly0<()> {}
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`

View File

@ -16,6 +16,9 @@
trait Parent1 {
type P1;
fn p1(&self) -> Self::P1;
fn d(&self) -> i32 {
42
}
}
trait Parent2 {
@ -60,14 +63,17 @@ fn main() {
x.c();
x.p1();
x.p2();
x.d();
// Parents can be created and access their methods.
let y = &Foo as &dyn Parent1<P1=u16>;
y.p1();
y.d();
let z = &Foo as &dyn Parent2<P2=u32>;
z.p2();
// Trait upcasting works
let x1 = x as &dyn Parent1<P1=u16>;
x1.p1();
x1.d();
let x2 = x as &dyn Parent2<P2=u32>;
x2.p2();
}