Skip to content

Commit cf71cd5

Browse files
authored
Merge pull request #240 from GaloisInc/T237-trait-object-constants
Support constant trait objects
2 parents 886c15a + 318f8c4 commit cf71cd5

File tree

8 files changed

+170
-37
lines changed

8 files changed

+170
-37
lines changed

SCHEMA_CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ The following document describes the changes to the JSON schema that
33
as a changelog for the code in the `mir-json` tools themselves, which are
44
versioned separately.)
55

6+
## 9
7+
8+
Add `trait_object`, which represents constant trait objects such as `const X:
9+
&dyn Trait = &0u32`. These include three fields:
10+
11+
* `def_id`: the `DefId` for the constant reference backing the trait object.
12+
This is the same as in `"static_ref"` and `"slice"` constant values.
13+
14+
* `trait_id`: the `DefId` for the principal trait bound in the trait object.
15+
This plays a similar role as the `trait_id` field in `Dynamic` types.
16+
17+
* `vtable`: the `DefId` for the trait object's vtable. This plays a similar
18+
role as the `vtable` field in `UnsizeVtable` cast kinds.
19+
620
## 8
721

822
Add details to the `InlineType` case for `TyKind::Coroutine` and to the

doc/mir-json-schema.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ type BaseSize = {
164164
kind: "Usize" | "U8" | "U16" | "U32" | "U64" | "U128" |
165165
"Isize" | "I8" | "I16" | "I32" | "I64" | "I128"
166166
}
167-
167+
168168
type FloatKind = { kind: "F32" | "F64" }
169169

170170
type Layout = {
@@ -286,6 +286,7 @@ type ConstVal =
286286
| { kind: "closure", upvars: ConstVal[] }
287287
| { kind: "coroutine_closure", upvars: ConstVal[] }
288288
| { kind: "fn_ptr", "def_id": DefId }
289+
| { kind: "trait_object", def_id: DefId, trait_id: DefId, vtable: DefId }
289290

290291

291292

@@ -302,7 +303,7 @@ type Lvalue = {
302303
type PlaceElem =
303304
{ kind: "Deref" }
304305
| { kind: "Field", field: number, ty: Ty }
305-
| { kind: "Index", op: Var }
306+
| { kind: "Index", op: Var }
306307
| { kind: "ConstantIndex", offset: number, min_length: number, from_end: boolean }
307308
| { kind: "Subslice", from: number, to: number, from_end: boolean }
308309
| { kind: "Downcast", variant: number }
@@ -357,8 +358,8 @@ type Operand =
357358
{ kind: "Move", data: Lvalue }
358359
| { kind: "Constant", data: Constant }
359360
| { kind: "Copy", data: Lvalue }
360-
361-
type AggregateKind =
361+
362+
type AggregateKind =
362363
{ kind: "Array", ty: Ty }
363364
| { kind: "Tuple" | "Closure" | "CoroutineClosure" }
364365
| { kind: "Coroutine", discr_ty: Ty, upvar_tys: Ty[], saved_tys: Ty[], field_map: number[][] }

src/analyz/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,13 +1195,6 @@ fn emit_vtable<'tcx>(
11951195
emit_new_defs(ms, out)
11961196
}
11971197

1198-
fn vtable_name<'tcx>(
1199-
mir: &mut MirState<'_, 'tcx>,
1200-
trait_ref: ty::PolyTraitRef<'tcx>,
1201-
) -> String {
1202-
ext_def_id_str(mir.tcx, trait_ref.def_id(), "_vtbl", trait_ref)
1203-
}
1204-
12051198
fn build_vtable_items<'tcx>(
12061199
mir: &mut MirState<'_, 'tcx>,
12071200
trait_ref: ty::PolyTraitRef<'tcx>,

src/analyz/ty_json.rs

Lines changed: 127 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use rustc_index::{IndexVec, Idx};
77
use rustc_middle::mir;
88
use rustc_middle::ty::CoroutineArgsExt;
99
use rustc_const_eval::interpret::{
10-
self, InterpCx, InterpResult, MPlaceTy, OffsetMode, Projectable,
10+
self, InterpCx, InterpResult, MemPlaceMeta, MPlaceTy, OffsetMode,
11+
Projectable,
1112
};
1213
use rustc_middle::ty;
1314
use rustc_middle::ty::{AdtKind, DynKind, TyCtxt, TypeVisitableExt};
@@ -244,6 +245,13 @@ pub fn trait_inst_id_str<'tcx>(
244245
}
245246
}
246247

248+
pub fn vtable_name<'tcx>(
249+
mir: &mut MirState<'_, 'tcx>,
250+
trait_ref: ty::PolyTraitRef<'tcx>,
251+
) -> String {
252+
ext_def_id_str(mir.tcx, trait_ref.def_id(), "_vtbl", trait_ref)
253+
}
254+
247255
/// Get the mangled name of a monomorphic function. As a side effect, this marks the function as
248256
/// "used", so its body will be emitted too.
249257
pub fn get_fn_def_name<'tcx>(
@@ -1464,6 +1472,24 @@ fn make_allocation_body<'tcx>(
14641472
) -> serde_json::Value {
14651473
let tcx = mir.tcx;
14661474

1475+
fn do_default<'tcx>(
1476+
mir: &mut MirState<'_, 'tcx>,
1477+
icx: &mut interpret::InterpCx<'tcx, RenderConstMachine<'tcx>>,
1478+
rty: ty::Ty<'tcx>,
1479+
d: &MPlaceTy<'tcx>,
1480+
) -> serde_json::Value {
1481+
let rlayout = mir.tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(rty)).unwrap();
1482+
let mpty: MPlaceTy = d.offset_with_meta(Size::ZERO, OffsetMode::Inbounds, d.meta(), rlayout, icx).unwrap();
1483+
let rendered = try_render_opty(mir, icx, &mpty.into());
1484+
1485+
json!({
1486+
"kind": "constant",
1487+
"mutable": false,
1488+
"ty": rty.to_json(mir),
1489+
"rendered": rendered,
1490+
})
1491+
}
1492+
14671493
if !is_mut {
14681494
/// Common logic for emitting `"kind": "strbody"` constants, shared by the `str` and `CStr`
14691495
/// cases.
@@ -1494,10 +1520,15 @@ fn make_allocation_body<'tcx>(
14941520
}
14951521

14961522
match *rty.kind() {
1497-
// Special cases for &str, &CStr, and &[T]
1523+
// Special cases for references to unsized types. Currently, the
1524+
// following are supported:
1525+
//
1526+
// * String slices (&str and &CStr)
1527+
// * Array slices (&[T])
1528+
// * Trait objects (&dyn Trait)
14981529
//
1499-
// These and the ones in try_render_ref_opty below should be
1500-
// kept in sync.
1530+
// These special cases and the ones in try_render_ref_opty below
1531+
// should be kept in sync.
15011532
ty::TyKind::Str => {
15021533
let len = d.len(icx).unwrap();
15031534
return do_strbody(mir, icx, d, len);
@@ -1531,21 +1562,16 @@ fn make_allocation_body<'tcx>(
15311562
"rendered": rendered,
15321563
});
15331564
},
1565+
ty::TyKind::Dynamic(ref preds, _, _) => {
1566+
let unpacked_d = unpack_dyn_place(icx, d, preds).unwrap();
1567+
return do_default(mir, icx, unpacked_d.layout.ty, &unpacked_d);
1568+
},
15341569
_ => ()
15351570
}
15361571
}
15371572

15381573
// Default case
1539-
let rlayout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(rty)).unwrap();
1540-
let mpty: MPlaceTy = d.offset_with_meta(Size::ZERO, OffsetMode::Inbounds, d.meta(), rlayout, icx).unwrap();
1541-
let rendered = try_render_opty(mir, icx, &mpty.into());
1542-
1543-
return json!({
1544-
"kind": "constant",
1545-
"mutable": false,
1546-
"ty": rty.to_json(mir),
1547-
"rendered": rendered,
1548-
});
1574+
return do_default(mir, icx, rty, d);
15491575
}
15501576

15511577
fn try_render_ref_opty<'tcx>(
@@ -1602,27 +1628,64 @@ fn try_render_ref_opty<'tcx>(
16021628
return None
16031629
};
16041630

1631+
// TODO(#241): Lift this restriction.
16051632
assert!(d_offset == Size::ZERO, "cannot handle nonzero reference offsets");
16061633

16071634
if !is_mut {
1608-
// Special cases for &str, &CStr, and &[T]
1609-
//
1610-
// These and the ones in make_allocation_body above should be kept in sync.
1611-
let do_slice_special_case = match *rty.kind() {
1612-
ty::TyKind::Str | ty::TyKind::Slice(_) => true,
1613-
ty::TyKind::Adt(adt_def, _) if tcx.is_lang_item(adt_def.did(), LangItem::CStr) => true,
1614-
_ => false,
1615-
};
1616-
if do_slice_special_case {
1635+
fn do_slice<'tcx>(
1636+
icx: &mut interpret::InterpCx<'tcx, RenderConstMachine<'tcx>>,
1637+
d: &MPlaceTy<'tcx>,
1638+
def_id_json: serde_json::Value,
1639+
) -> serde_json::Value {
16171640
// `<MPlaceTy as Projectable>::len` asserts that the input must have `Slice` or
16181641
// `Str` type. However, the implementation it uses works fine on `CStr` too, so we
16191642
// copy-paste the code here.
16201643
let len = d.meta().unwrap_meta().to_target_usize(icx).unwrap();
1621-
return Some(json!({
1644+
json!({
16221645
"kind": "slice",
16231646
"def_id": def_id_json,
16241647
"len": len
1625-
}))
1648+
})
1649+
}
1650+
1651+
// Special cases for references to unsized types. Currently, the
1652+
// following are supported:
1653+
//
1654+
// * String slices (&str and &CStr)
1655+
// * Array slices (&[T])
1656+
// * Trait objects (&dyn Trait)
1657+
//
1658+
// These special cases and the ones in make_allocation_body above should be kept in sync.
1659+
match *rty.kind() {
1660+
ty::TyKind::Str | ty::TyKind::Slice(_) =>
1661+
return Some(do_slice(icx, &d, def_id_json)),
1662+
ty::TyKind::Adt(adt_def, _) if tcx.is_lang_item(adt_def.did(), LangItem::CStr) =>
1663+
return Some(do_slice(icx, &d, def_id_json)),
1664+
ty::TyKind::Dynamic(ref preds, _, _) => {
1665+
let self_ty = unpack_dyn_ty(icx, &d, preds).unwrap();
1666+
let vtable_desc = preds.principal().map(|pred| pred.with_self_ty(tcx, self_ty));
1667+
match vtable_desc {
1668+
Some(vtable_desc) => {
1669+
mir.used.vtables.insert(vtable_desc);
1670+
let ti = TraitInst::from_dynamic_predicates(tcx, preds);
1671+
return Some(json!({
1672+
"kind": "trait_object",
1673+
"def_id": def_id_json,
1674+
"trait_id": trait_inst_id_str(tcx, &ti),
1675+
"vtable": vtable_name(mir, vtable_desc),
1676+
}))
1677+
},
1678+
None =>
1679+
// If there is no principal trait bound, then all of
1680+
// the trait bounds are auto traits. We do not
1681+
// currently support computing vtables for these sorts
1682+
// of trait objects (see #239).
1683+
return Some(json!({
1684+
"kind": "unsupported",
1685+
}))
1686+
}
1687+
},
1688+
_ => (),
16261689
}
16271690
}
16281691

@@ -1856,3 +1919,42 @@ pub fn eval_mir_constant<'tcx>(
18561919
.eval(tcx, ty::TypingEnv::fully_monomorphized(), constant.span)
18571920
.unwrap()
18581921
}
1922+
1923+
// Turn a place with a `dyn Trait` type into the actual dynamic type.
1924+
//
1925+
// This is based on the internals of
1926+
// `rustc_const_eval::interpret::InterpCx::unpack_dyn_trait`.
1927+
fn unpack_dyn_ty<'tcx>(
1928+
icx: &InterpCx<'tcx, RenderConstMachine<'tcx>>,
1929+
mplace: &MPlaceTy<'tcx>,
1930+
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
1931+
) -> InterpResult<'tcx, ty::Ty<'tcx>> {
1932+
assert!(
1933+
matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)),
1934+
"`unpack_dyn_ty` only makes sense on `dyn*` types"
1935+
);
1936+
let vtable = mplace.meta().unwrap_meta().to_pointer(icx)?;
1937+
icx.get_ptr_vtable_ty(vtable, Some(expected_trait))
1938+
}
1939+
1940+
// Turn a place with a `dyn Trait` type into a place with the actual dynamic
1941+
// type.
1942+
//
1943+
// This is based on `rustc_const_eval::interpret::InterpCx::unpack_dyn_trait`.
1944+
fn unpack_dyn_place<'tcx>(
1945+
icx: &InterpCx<'tcx, RenderConstMachine<'tcx>>,
1946+
mplace: &MPlaceTy<'tcx>,
1947+
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
1948+
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
1949+
let ty = unpack_dyn_ty(icx, mplace, expected_trait)?;
1950+
// This is a kind of transmute, from a place with unsized type and metadata to
1951+
// a place with sized type and no metadata.
1952+
let layout = icx.layout_of(ty)?;
1953+
mplace.offset_with_meta(
1954+
Size::ZERO,
1955+
OffsetMode::Wrapping,
1956+
MemPlaceMeta::None,
1957+
layout,
1958+
icx,
1959+
)
1960+
}

src/schema_ver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
/// Each version of the schema is assumed to be backwards-incompatible with
77
/// previous versions of the schema. As such, any time this version number is
88
/// bumped, it should be treated as a major version bump.
9-
pub const SCHEMA_VER: u64 = 8;
9+
pub const SCHEMA_VER: u64 = 9;

tests/issues/test0237/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
A regression test for [issue
2+
#237](https://github.com/GaloisInc/mir-json/issues/237). This tests
3+
`mir-json`'s ability to compile constant trait objects.

tests/issues/test0237/test.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub trait Trait {
2+
fn method(&self) -> u32;
3+
}
4+
5+
impl Trait for u32 {
6+
fn method(&self) -> u32 { *self }
7+
}
8+
9+
const X: &dyn Trait = &0u32;
10+
11+
pub fn f() -> u32 {
12+
X.method()
13+
}

tests/issues/test0237/test.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
source "$(dirname "$0")/../../common.sh"
4+
5+
expect_no_panic \
6+
saw-rustc test.rs \
7+
--target "$(rustc --print host-tuple)"

0 commit comments

Comments
 (0)