Skip to content

Commit 329cb5e

Browse files
committed
Merge branch 'master' of github.com:EtomicBomb/rust
2 parents 42201fa + a4e559d commit 329cb5e

File tree

52 files changed

+1068
-525
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1068
-525
lines changed

compiler/rustc_const_eval/src/interpret/cast.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
401401
}
402402
(ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => {
403403
let val = self.read_immediate(src)?;
404-
if data_a.principal() == data_b.principal() {
405-
// A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables.
406-
// (But currently mismatching vtables violate the validity invariant so UB is triggered anyway.)
407-
return self.write_immediate(*val, dest);
408-
}
404+
// Take apart the old pointer, and find the dynamic type.
409405
let (old_data, old_vptr) = val.to_scalar_pair();
410406
let old_data = old_data.to_pointer(self)?;
411407
let old_vptr = old_vptr.to_pointer(self)?;
412408
let ty = self.get_ptr_vtable_ty(old_vptr, Some(data_a))?;
409+
410+
// Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces
411+
// our destination trait.
412+
if cfg!(debug_assertions) {
413+
let vptr_entry_idx =
414+
self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
415+
let vtable_entries = self.vtable_entries(data_a.principal(), ty);
416+
if let Some(entry_idx) = vptr_entry_idx {
417+
let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
418+
vtable_entries.get(entry_idx)
419+
else {
420+
span_bug!(
421+
self.cur_span(),
422+
"invalid vtable entry index in {} -> {} upcast",
423+
src_pointee_ty,
424+
dest_pointee_ty
425+
);
426+
};
427+
let erased_trait_ref = upcast_trait_ref
428+
.map_bound(|r| ty::ExistentialTraitRef::erase_self_ty(*self.tcx, r));
429+
assert!(
430+
data_b
431+
.principal()
432+
.is_some_and(|b| self.eq_in_param_env(erased_trait_ref, b))
433+
);
434+
} else {
435+
// In this case codegen would keep using the old vtable. We don't want to do
436+
// that as it has the wrong trait. The reason codegen can do this is that
437+
// one vtable is a prefix of the other, so we double-check that.
438+
let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
439+
assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
440+
};
441+
}
442+
443+
// Get the destination trait vtable and return that.
413444
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
414445
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
415446
}

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ use std::cell::Cell;
22
use std::{fmt, mem};
33

44
use either::{Either, Left, Right};
5+
use rustc_infer::infer::at::ToTrace;
6+
use rustc_infer::traits::ObligationCause;
7+
use rustc_trait_selection::traits::ObligationCtxt;
58
use tracing::{debug, info, info_span, instrument, trace};
69

710
use rustc_errors::DiagCtxtHandle;
811
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
912
use rustc_index::IndexVec;
13+
use rustc_infer::infer::TyCtxtInferExt;
1014
use rustc_middle::mir;
1115
use rustc_middle::mir::interpret::{
1216
CtfeProvenance, ErrorHandled, InvalidMetaKind, ReportedErrorInfo,
@@ -640,6 +644,32 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
640644
}
641645
}
642646

647+
/// Check if the two things are equal in the current param_env, using an infctx to get proper
648+
/// equality checks.
649+
pub(super) fn eq_in_param_env<T>(&self, a: T, b: T) -> bool
650+
where
651+
T: PartialEq + TypeFoldable<TyCtxt<'tcx>> + ToTrace<'tcx>,
652+
{
653+
// Fast path: compare directly.
654+
if a == b {
655+
return true;
656+
}
657+
// Slow path: spin up an inference context to check if these traits are sufficiently equal.
658+
let infcx = self.tcx.infer_ctxt().build();
659+
let ocx = ObligationCtxt::new(&infcx);
660+
let cause = ObligationCause::dummy_with_span(self.cur_span());
661+
// equate the two trait refs after normalization
662+
let a = ocx.normalize(&cause, self.param_env, a);
663+
let b = ocx.normalize(&cause, self.param_env, b);
664+
if ocx.eq(&cause, self.param_env, a, b).is_ok() {
665+
if ocx.select_all_or_error().is_empty() {
666+
// All good.
667+
return true;
668+
}
669+
}
670+
return false;
671+
}
672+
643673
/// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
644674
/// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic,
645675
/// and is primarily intended for the panic machinery.

compiler/rustc_const_eval/src/interpret/terminator.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::borrow::Cow;
22

33
use either::Either;
4-
use rustc_middle::ty::TyCtxt;
54
use tracing::trace;
65

76
use rustc_middle::{
@@ -867,7 +866,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
867866
};
868867

869868
// Obtain the underlying trait we are working on, and the adjusted receiver argument.
870-
let (dyn_trait, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) =
869+
let (trait_, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) =
871870
receiver_place.layout.ty.kind()
872871
{
873872
let recv = self.unpack_dyn_star(&receiver_place, data)?;
@@ -898,20 +897,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
898897
(receiver_trait.principal(), dyn_ty, receiver_place.ptr())
899898
};
900899

901-
// Now determine the actual method to call. We can do that in two different ways and
902-
// compare them to ensure everything fits.
903-
let vtable_entries = if let Some(dyn_trait) = dyn_trait {
904-
let trait_ref = dyn_trait.with_self_ty(*self.tcx, dyn_ty);
905-
let trait_ref = self.tcx.erase_regions(trait_ref);
906-
self.tcx.vtable_entries(trait_ref)
907-
} else {
908-
TyCtxt::COMMON_VTABLE_ENTRIES
909-
};
900+
// Now determine the actual method to call. Usually we use the easy way of just
901+
// looking up the method at index `idx`.
902+
let vtable_entries = self.vtable_entries(trait_, dyn_ty);
910903
let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else {
911904
// FIXME(fee1-dead) these could be variants of the UB info enum instead of this
912905
throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method);
913906
};
914907
trace!("Virtual call dispatches to {fn_inst:#?}");
908+
// We can also do the lookup based on `def_id` and `dyn_ty`, and check that that
909+
// produces the same result.
915910
if cfg!(debug_assertions) {
916911
let tcx = *self.tcx;
917912

compiler/rustc_const_eval/src/interpret/traits.rs

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
use rustc_infer::infer::TyCtxtInferExt;
2-
use rustc_infer::traits::ObligationCause;
31
use rustc_middle::mir::interpret::{InterpResult, Pointer};
42
use rustc_middle::ty::layout::LayoutOf;
5-
use rustc_middle::ty::{self, Ty};
3+
use rustc_middle::ty::{self, Ty, TyCtxt, VtblEntry};
64
use rustc_target::abi::{Align, Size};
7-
use rustc_trait_selection::traits::ObligationCtxt;
85
use tracing::trace;
96

107
use super::util::ensure_monomorphic_enough;
@@ -47,35 +44,36 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
4744
Ok((layout.size, layout.align.abi))
4845
}
4946

47+
pub(super) fn vtable_entries(
48+
&self,
49+
trait_: Option<ty::PolyExistentialTraitRef<'tcx>>,
50+
dyn_ty: Ty<'tcx>,
51+
) -> &'tcx [VtblEntry<'tcx>] {
52+
if let Some(trait_) = trait_ {
53+
let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty);
54+
let trait_ref = self.tcx.erase_regions(trait_ref);
55+
self.tcx.vtable_entries(trait_ref)
56+
} else {
57+
TyCtxt::COMMON_VTABLE_ENTRIES
58+
}
59+
}
60+
5061
/// Check that the given vtable trait is valid for a pointer/reference/place with the given
5162
/// expected trait type.
5263
pub(super) fn check_vtable_for_type(
5364
&self,
5465
vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>,
5566
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
5667
) -> InterpResult<'tcx> {
57-
// Fast path: if they are equal, it's all fine.
58-
if expected_trait.principal() == vtable_trait {
59-
return Ok(());
60-
}
61-
if let (Some(expected_trait), Some(vtable_trait)) =
62-
(expected_trait.principal(), vtable_trait)
63-
{
64-
// Slow path: spin up an inference context to check if these traits are sufficiently equal.
65-
let infcx = self.tcx.infer_ctxt().build();
66-
let ocx = ObligationCtxt::new(&infcx);
67-
let cause = ObligationCause::dummy_with_span(self.cur_span());
68-
// equate the two trait refs after normalization
69-
let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait);
70-
let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait);
71-
if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() {
72-
if ocx.select_all_or_error().is_empty() {
73-
// All good.
74-
return Ok(());
75-
}
76-
}
68+
let eq = match (expected_trait.principal(), vtable_trait) {
69+
(Some(a), Some(b)) => self.eq_in_param_env(a, b),
70+
(None, None) => true,
71+
_ => false,
72+
};
73+
if !eq {
74+
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
7775
}
78-
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
76+
Ok(())
7977
}
8078

8179
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.

compiler/rustc_error_codes/src/error_codes/E0120.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
Drop was implemented on a trait, which is not allowed: only structs and
2-
enums can implement Drop.
1+
`Drop` was implemented on a trait object or reference, which is not allowed;
2+
only structs, enums, and unions can implement Drop.
33

4-
Erroneous code example:
4+
Erroneous code examples:
55

66
```compile_fail,E0120
77
trait MyTrait {}
@@ -11,8 +11,16 @@ impl Drop for MyTrait {
1111
}
1212
```
1313

14-
A workaround for this problem is to wrap the trait up in a struct, and implement
15-
Drop on that:
14+
```compile_fail,E0120
15+
struct Concrete {}
16+
17+
impl Drop for &'_ mut Concrete {
18+
fn drop(&mut self) {}
19+
}
20+
```
21+
22+
A workaround for traits is to create a wrapper struct with a generic type,
23+
add a trait bound to the type, and implement `Drop` on the wrapper:
1624

1725
```
1826
trait MyTrait {}
@@ -24,13 +32,13 @@ impl <T: MyTrait> Drop for MyWrapper<T> {
2432
2533
```
2634

27-
Alternatively, wrapping trait objects requires something:
35+
Alternatively, the `Drop` wrapper can contain the trait object:
2836

2937
```
3038
trait MyTrait {}
3139
32-
//or Box<MyTrait>, if you wanted an owned trait object
33-
struct MyWrapper<'a> { foo: &'a MyTrait }
40+
// or Box<dyn MyTrait>, if you wanted an owned trait object
41+
struct MyWrapper<'a> { foo: &'a dyn MyTrait }
3442
3543
impl <'a> Drop for MyWrapper<'a> {
3644
fn drop(&mut self) {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Functions marked as `C-cmse-nonsecure-call` place restrictions on their
2+
inputs and outputs.
3+
4+
- inputs must fit in the 4 available 32-bit argument registers. Alignment
5+
is relevant.
6+
- outputs must either fit in 4 bytes, or be a foundational type of
7+
size 8 (`i64`, `u64`, `f64`).
8+
- no generics can be used in the signature
9+
10+
For more information,
11+
see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases).
12+
13+
Erroneous code example:
14+
15+
```ignore (only fails on supported targets)
16+
#![feature(abi_c_cmse_nonsecure_call)]
17+
18+
#[no_mangle]
19+
pub fn test(
20+
f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
21+
) -> u32 {
22+
f(1, 2, 3, 4, 5)
23+
}
24+
```
25+
26+
Arguments' alignment is respected. In the example below, padding is inserted
27+
so that the `u64` argument is passed in registers r2 and r3. There is then no
28+
room left for the final `f32` argument
29+
30+
```ignore (only fails on supported targets)
31+
#![feature(abi_c_cmse_nonsecure_call)]
32+
33+
#[no_mangle]
34+
pub fn test(
35+
f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32,
36+
) -> u32 {
37+
f(1, 2, 3.0)
38+
}
39+
```

compiler/rustc_error_codes/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ E0794: 0794,
536536
E0795: 0795,
537537
E0796: 0796,
538538
E0797: 0797,
539+
E0798: 0798,
539540
);
540541
)
541542
}

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ hir_analysis_cannot_capture_late_bound_ty =
5858
hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
5959
.label = `for<...>` is here
6060
61+
hir_analysis_cmse_call_generic =
62+
function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
63+
64+
hir_analysis_cmse_call_inputs_stack_spill =
65+
arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
66+
.label = {$plural ->
67+
[false] this argument doesn't
68+
*[true] these arguments don't
69+
} fit in the available registers
70+
.note = functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
71+
72+
hir_analysis_cmse_call_output_stack_spill =
73+
return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
74+
.label = this type doesn't fit in the available registers
75+
.note1 = functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
76+
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
77+
6178
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
6279
6380
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
@@ -519,6 +536,11 @@ hir_analysis_typeof_reserved_keyword_used =
519536
.suggestion = consider replacing `typeof(...)` with an actual type
520537
.label = reserved keyword
521538
539+
hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates
540+
.label = unconstrained {$param_def_kind}
541+
.const_param_note = expressions using a const parameter must map each value to a distinct output value
542+
.const_param_note2 = proving the result of expressions other than the parameter are unique is not supported
543+
522544
hir_analysis_unconstrained_opaque_type = unconstrained opaque type
523545
.note = `{$name}` must be used in combination with a concrete type within the same {$what}
524546

0 commit comments

Comments
 (0)