Skip to content

Commit 0c78bc2

Browse files
assert call args are assignable
1 parent ee9c7c9 commit 0c78bc2

File tree

1 file changed

+180
-1
lines changed
  • compiler/rustc_codegen_ssa/src/mir

1 file changed

+180
-1
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+180-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_hir::lang_items::LangItem;
1515
use rustc_middle::mir::{self, AssertKind, SwitchTargets, UnwindTerminateReason};
1616
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
1717
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
18-
use rustc_middle::ty::{self, Instance, Ty};
18+
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
1919
use rustc_session::config::OptLevel;
2020
use rustc_span::{source_map::Spanned, sym, Span, Symbol};
2121
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
@@ -986,6 +986,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
986986
}
987987
}
988988

989+
#[cfg(debug_assertions)]
990+
assert_assignable(bx.tcx(), instance, op.layout.ty, fn_abi.args[i].layout.ty, 16);
991+
989992
// The callee needs to own the argument memory if we pass it
990993
// by-ref, so make a local copy of non-immediate constants.
991994
match (&arg.node, op.val) {
@@ -1003,6 +1006,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10031006
self.codegen_argument(bx, op, &mut llargs, &fn_abi.args[i]);
10041007
}
10051008
let num_untupled = untuple.map(|tup| {
1009+
#[cfg(debug_assertions)]
1010+
for (param, arg) in std::iter::zip(
1011+
&fn_abi.args[first_args.len()..],
1012+
self.monomorphize(tup.node.ty(self.mir, bx.tcx())).tuple_fields(),
1013+
) {
1014+
assert_assignable(bx.tcx(), instance, arg, param.layout.ty, 16);
1015+
}
1016+
10061017
self.codegen_arguments_untupled(
10071018
bx,
10081019
&tup.node,
@@ -1734,3 +1745,171 @@ enum ReturnDest<'tcx, V> {
17341745
// Store a direct return value to an operand local place.
17351746
DirectOperand(mir::Local),
17361747
}
1748+
1749+
#[track_caller]
1750+
pub(crate) fn assert_assignable<'tcx>(
1751+
tcx: TyCtxt<'tcx>,
1752+
instance: Option<Instance<'tcx>>,
1753+
from_ty: Ty<'tcx>,
1754+
to_ty: Ty<'tcx>,
1755+
limit: usize,
1756+
) {
1757+
use ty::TypeAndMut;
1758+
if limit == 0 {
1759+
// assert_assignable exists solely to catch bugs in cg_clif. it isn't necessary for
1760+
// soundness. don't attempt to check deep types to avoid exponential behavior in certain
1761+
// cases.
1762+
return;
1763+
}
1764+
match (from_ty.kind(), to_ty.kind()) {
1765+
(ty::Ref(_, a, _), ty::Ref(_, b, _))
1766+
| (
1767+
ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }),
1768+
ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }),
1769+
) => {
1770+
assert_assignable(tcx, instance, *a, *b, limit - 1);
1771+
}
1772+
(ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }))
1773+
| (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => {
1774+
assert_assignable(tcx, instance, *a, *b, limit - 1);
1775+
}
1776+
(ty::FnPtr(_), ty::FnPtr(_)) => {
1777+
let from_sig = tcx.normalize_erasing_late_bound_regions(
1778+
ty::ParamEnv::reveal_all(),
1779+
from_ty.fn_sig(tcx),
1780+
);
1781+
let ty::FnSig {
1782+
inputs_and_output: types_from,
1783+
c_variadic: c_variadic_from,
1784+
unsafety: unsafety_from,
1785+
abi: abi_from,
1786+
} = from_sig;
1787+
let to_sig = tcx.normalize_erasing_late_bound_regions(
1788+
ty::ParamEnv::reveal_all(),
1789+
to_ty.fn_sig(tcx),
1790+
);
1791+
let ty::FnSig {
1792+
inputs_and_output: types_to,
1793+
c_variadic: c_variadic_to,
1794+
unsafety: unsafety_to,
1795+
abi: abi_to,
1796+
} = to_sig;
1797+
let mut types_from = types_from.iter();
1798+
let mut types_to = types_to.iter();
1799+
loop {
1800+
match (types_from.next(), types_to.next()) {
1801+
(Some(a), Some(b)) => assert_assignable(tcx, instance, a, b, limit - 1),
1802+
(None, None) => break,
1803+
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
1804+
}
1805+
}
1806+
assert_eq!(
1807+
c_variadic_from, c_variadic_to,
1808+
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n",
1809+
from_sig, to_sig,
1810+
);
1811+
assert_eq!(
1812+
unsafety_from, unsafety_to,
1813+
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n",
1814+
from_sig, to_sig,
1815+
);
1816+
assert_eq!(
1817+
abi_from, abi_to,
1818+
"Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n",
1819+
from_sig, to_sig,
1820+
);
1821+
// fn(&T) -> for<'l> fn(&'l T) is allowed
1822+
}
1823+
(&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => {
1824+
// FIXME(dyn-star): Do the right thing with DynKinds
1825+
for (from, to) in from_traits.iter().zip(to_traits) {
1826+
let from =
1827+
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), from);
1828+
let to = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), to);
1829+
assert_eq!(
1830+
from, to,
1831+
"Can't write trait object of incompatible traits {:?} to place with traits {:?}\n",
1832+
from_traits, to_traits
1833+
);
1834+
}
1835+
// dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed
1836+
}
1837+
(&ty::Tuple(types_a), &ty::Tuple(types_b)) => {
1838+
let mut types_a = types_a.iter();
1839+
let mut types_b = types_b.iter();
1840+
loop {
1841+
match (types_a.next(), types_b.next()) {
1842+
(Some(a), Some(b)) => assert_assignable(tcx, instance, a, b, limit - 1),
1843+
(None, None) => return,
1844+
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
1845+
}
1846+
}
1847+
}
1848+
(&ty::Adt(adt_def_a, args_a), &ty::Adt(adt_def_b, args_b))
1849+
if adt_def_a.did() == adt_def_b.did() =>
1850+
{
1851+
let mut types_a = args_a.types();
1852+
let mut types_b = args_b.types();
1853+
loop {
1854+
match (types_a.next(), types_b.next()) {
1855+
(Some(a), Some(b)) => assert_assignable(tcx, instance, a, b, limit - 1),
1856+
(None, None) => return,
1857+
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
1858+
}
1859+
}
1860+
}
1861+
(ty::Array(a, _), ty::Array(b, _)) => assert_assignable(tcx, instance, *a, *b, limit - 1),
1862+
(&ty::Closure(def_id_a, args_a), &ty::Closure(def_id_b, args_b))
1863+
if def_id_a == def_id_b =>
1864+
{
1865+
let mut types_a = args_a.types();
1866+
let mut types_b = args_b.types();
1867+
loop {
1868+
match (types_a.next(), types_b.next()) {
1869+
(Some(a), Some(b)) => assert_assignable(tcx, instance, a, b, limit - 1),
1870+
(None, None) => return,
1871+
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
1872+
}
1873+
}
1874+
}
1875+
(&ty::Coroutine(def_id_a, args_a), &ty::Coroutine(def_id_b, args_b))
1876+
if def_id_a == def_id_b =>
1877+
{
1878+
let mut types_a = args_a.types();
1879+
let mut types_b = args_b.types();
1880+
loop {
1881+
match (types_a.next(), types_b.next()) {
1882+
(Some(a), Some(b)) => assert_assignable(tcx, instance, a, b, limit - 1),
1883+
(None, None) => return,
1884+
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
1885+
}
1886+
}
1887+
}
1888+
(&ty::CoroutineWitness(def_id_a, args_a), &ty::CoroutineWitness(def_id_b, args_b))
1889+
if def_id_a == def_id_b =>
1890+
{
1891+
let mut types_a = args_a.types();
1892+
let mut types_b = args_b.types();
1893+
loop {
1894+
match (types_a.next(), types_b.next()) {
1895+
(Some(a), Some(b)) => assert_assignable(tcx, instance, a, b, limit - 1),
1896+
(None, None) => return,
1897+
(Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty),
1898+
}
1899+
}
1900+
}
1901+
(ty::Param(_), _) | (_, ty::Param(_)) if tcx.sess.opts.unstable_opts.polymorphize => {
1902+
// No way to check if it is correct or not with polymorphization enabled
1903+
}
1904+
_ => {
1905+
assert_eq!(
1906+
from_ty,
1907+
to_ty,
1908+
"Can't write value with incompatible type {:?} to place with type {:?} in {:?}\n",
1909+
from_ty.kind(),
1910+
to_ty.kind(),
1911+
instance,
1912+
);
1913+
}
1914+
}
1915+
}

0 commit comments

Comments
 (0)