Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 68c087c

Browse files
committedAug 18, 2024·
safe transmute: forbid reference lifetime extension
Modifies `BikeshedIntrinsicFrom` to forbid lifetime extensions on references. This static check can be opted out of with the `Assume::lifetimes` flag. Fixes #129097
1 parent 0f442e2 commit 68c087c

File tree

9 files changed

+535
-132
lines changed

9 files changed

+535
-132
lines changed
 

‎compiler/rustc_trait_selection/src/traits/select/confirmation.rs

Lines changed: 103 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
1717
use rustc_infer::traits::ObligationCauseCode;
1818
use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
1919
use rustc_middle::ty::{
20-
self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TraitPredicate, Ty,
21-
TyCtxt, Upcast,
20+
self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast,
2221
};
2322
use rustc_middle::{bug, span_bug};
2423
use rustc_span::def_id::DefId;
@@ -292,90 +291,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
292291
&mut self,
293292
obligation: &PolyTraitObligation<'tcx>,
294293
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
295-
use rustc_transmute::{Answer, Condition};
296-
#[instrument(level = "debug", skip(tcx, obligation, predicate))]
294+
use rustc_transmute::{Answer, Assume, Condition};
295+
296+
/// Generate sub-obligations for reference-to-reference transmutations.
297+
fn reference_obligations<'tcx>(
298+
tcx: TyCtxt<'tcx>,
299+
obligation: &PolyTraitObligation<'tcx>,
300+
(src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
301+
(dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
302+
assume: Assume,
303+
) -> Vec<PredicateObligation<'tcx>> {
304+
let make_transmute_obl = |src, dst| {
305+
let transmute_trait = obligation.predicate.def_id();
306+
let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
307+
let trait_ref = ty::TraitRef::new(
308+
tcx,
309+
transmute_trait,
310+
[
311+
ty::GenericArg::from(dst),
312+
ty::GenericArg::from(src),
313+
ty::GenericArg::from(assume),
314+
],
315+
);
316+
Obligation::with_depth(
317+
tcx,
318+
obligation.cause.clone(),
319+
obligation.recursion_depth + 1,
320+
obligation.param_env,
321+
obligation.predicate.rebind(trait_ref),
322+
)
323+
};
324+
325+
let make_freeze_obl = |ty| {
326+
let trait_ref = ty::TraitRef::new(
327+
tcx,
328+
tcx.require_lang_item(LangItem::Freeze, None),
329+
[ty::GenericArg::from(ty)],
330+
);
331+
Obligation::with_depth(
332+
tcx,
333+
obligation.cause.clone(),
334+
obligation.recursion_depth + 1,
335+
obligation.param_env,
336+
trait_ref,
337+
)
338+
};
339+
340+
let make_outlives_obl = |target, region| {
341+
let outlives = ty::OutlivesPredicate(target, region);
342+
Obligation::with_depth(
343+
tcx,
344+
obligation.cause.clone(),
345+
obligation.recursion_depth + 1,
346+
obligation.param_env,
347+
obligation.predicate.rebind(outlives),
348+
)
349+
};
350+
351+
// Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
352+
// it is always the case that `Src` must be transmutable into `Dst`,
353+
// and that that `'src` must outlive `'dst`.
354+
let mut obls = vec![make_transmute_obl(src_ty, dst_ty)];
355+
if !assume.lifetimes {
356+
obls.push(make_outlives_obl(src_lifetime, dst_lifetime));
357+
}
358+
359+
// Given a transmutation from `&Src`, both `Src` and `Dst` must be
360+
// `Freeze`, otherwise, using the transmuted value could lead to
361+
// data races.
362+
if src_mut == Mutability::Not {
363+
obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)])
364+
}
365+
366+
// Given a transmutation into `&'dst mut Dst`, it also must be the
367+
// case that `Dst` is transmutable into `Src`. For example,
368+
// transmuting bool -> u8 is OK as long as you can't update that u8
369+
// to be > 1, because you could later transmute the u8 back to a
370+
// bool and get undefined behavior. It also must be the case that
371+
// `'dst` lives exactly as long as `'src`.
372+
if dst_mut == Mutability::Mut {
373+
obls.push(make_transmute_obl(dst_ty, src_ty));
374+
if !assume.lifetimes {
375+
obls.push(make_outlives_obl(dst_lifetime, src_lifetime));
376+
}
377+
}
378+
379+
obls
380+
}
381+
382+
/// Flatten the `Condition` tree into a conjunction of obligations.
383+
#[instrument(level = "debug", skip(tcx, obligation))]
297384
fn flatten_answer_tree<'tcx>(
298385
tcx: TyCtxt<'tcx>,
299386
obligation: &PolyTraitObligation<'tcx>,
300-
predicate: TraitPredicate<'tcx>,
301387
cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
388+
assume: Assume,
302389
) -> Vec<PredicateObligation<'tcx>> {
303390
match cond {
304391
// FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
305392
// Not possible until the trait solver supports disjunctions of obligations
306393
Condition::IfAll(conds) | Condition::IfAny(conds) => conds
307394
.into_iter()
308-
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
395+
.flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume))
309396
.collect(),
310-
Condition::IfTransmutable { src, dst } => {
311-
let transmute_trait = obligation.predicate.def_id();
312-
let assume_const = predicate.trait_ref.args.const_at(2);
313-
let make_transmute_obl = |from_ty, to_ty| {
314-
let trait_ref = ty::TraitRef::new(
315-
tcx,
316-
transmute_trait,
317-
[
318-
ty::GenericArg::from(to_ty),
319-
ty::GenericArg::from(from_ty),
320-
ty::GenericArg::from(assume_const),
321-
],
322-
);
323-
Obligation::with_depth(
324-
tcx,
325-
obligation.cause.clone(),
326-
obligation.recursion_depth + 1,
327-
obligation.param_env,
328-
trait_ref,
329-
)
330-
};
331-
332-
let make_freeze_obl = |ty| {
333-
let trait_ref = ty::TraitRef::new(
334-
tcx,
335-
tcx.require_lang_item(LangItem::Freeze, None),
336-
[ty::GenericArg::from(ty)],
337-
);
338-
Obligation::with_depth(
339-
tcx,
340-
obligation.cause.clone(),
341-
obligation.recursion_depth + 1,
342-
obligation.param_env,
343-
trait_ref,
344-
)
345-
};
346-
347-
let mut obls = vec![];
348-
349-
// If the source is a shared reference, it must be `Freeze`;
350-
// otherwise, transmuting could lead to data races.
351-
if src.mutability == Mutability::Not {
352-
obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
353-
}
354-
355-
// If Dst is mutable, check bidirectionally.
356-
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
357-
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
358-
match dst.mutability {
359-
Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
360-
Mutability::Mut => obls.extend([
361-
make_transmute_obl(src.ty, dst.ty),
362-
make_transmute_obl(dst.ty, src.ty),
363-
]),
364-
}
365-
366-
obls
367-
}
397+
Condition::IfTransmutable { src, dst } => reference_obligations(
398+
tcx,
399+
obligation,
400+
(src.lifetime, src.ty, src.mutability),
401+
(dst.lifetime, dst.ty, dst.mutability),
402+
assume,
403+
),
368404
}
369405
}
370406

371-
// We erase regions here because transmutability calls layout queries,
372-
// which does not handle inference regions and doesn't particularly
373-
// care about other regions. Erasing late-bound regions is equivalent
374-
// to instantiating the binder with placeholders then erasing those
375-
// placeholder regions.
376-
let predicate = self
377-
.tcx()
378-
.erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate));
407+
let predicate = obligation.predicate.skip_binder();
379408

380409
let Some(assume) = rustc_transmute::Assume::from_const(
381410
self.infcx.tcx,
@@ -387,6 +416,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
387416

388417
let dst = predicate.trait_ref.args.type_at(0);
389418
let src = predicate.trait_ref.args.type_at(1);
419+
390420
debug!(?src, ?dst);
391421
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
392422
let maybe_transmutable = transmute_env.is_transmutable(
@@ -397,7 +427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
397427

398428
let fully_flattened = match maybe_transmutable {
399429
Answer::No(_) => Err(Unimplemented)?,
400-
Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
430+
Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
401431
Answer::Yes => vec![],
402432
};
403433

‎compiler/rustc_transmute/src/layout/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ pub mod rustc {
6363
use std::fmt::{self, Write};
6464

6565
use rustc_middle::mir::Mutability;
66-
use rustc_middle::ty::{self, Ty};
66+
use rustc_middle::ty::layout::{LayoutCx, LayoutError};
67+
use rustc_middle::ty::{self, Ty, TyCtxt};
68+
use rustc_target::abi::Layout;
6769

6870
/// A reference in the layout.
6971
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
@@ -120,4 +122,13 @@ pub mod rustc {
120122
self != &Self::Primitive
121123
}
122124
}
125+
126+
pub(crate) fn layout_of<'tcx>(
127+
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
128+
ty: Ty<'tcx>,
129+
) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
130+
use rustc_middle::ty::layout::LayoutOf;
131+
let ty = cx.tcx.erase_regions(ty);
132+
cx.layout_of(ty).map(|tl| tl.layout)
133+
}
123134
}

‎compiler/rustc_transmute/src/layout/tree.rs

Lines changed: 88 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,12 @@ where
171171

172172
#[cfg(feature = "rustc")]
173173
pub(crate) mod rustc {
174-
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf};
174+
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
175175
use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
176176
use rustc_span::ErrorGuaranteed;
177-
use rustc_target::abi::{FieldsShape, Size, TyAndLayout, Variants};
177+
use rustc_target::abi::{
178+
FieldIdx, FieldsShape, Layout, Size, TyAndLayout, VariantIdx, Variants,
179+
};
178180

179181
use super::Tree;
180182
use crate::layout::rustc::{Def, Ref};
@@ -202,20 +204,18 @@ pub(crate) mod rustc {
202204
}
203205

204206
impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
205-
pub fn from_ty(
206-
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
207-
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
208-
) -> Result<Self, Err> {
207+
pub fn from_ty(ty: Ty<'tcx>, cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, Err> {
209208
use rustc_target::abi::HasDataLayout;
209+
let layout = ty_layout(cx, ty);
210210

211-
if let Err(e) = ty_and_layout.ty.error_reported() {
211+
if let Err(e) = ty.error_reported() {
212212
return Err(Err::TypeError(e));
213213
}
214214

215215
let target = cx.tcx.data_layout();
216216
let pointer_size = target.pointer_size;
217217

218-
match ty_and_layout.ty.kind() {
218+
match ty.kind() {
219219
ty::Bool => Ok(Self::bool()),
220220

221221
ty::Float(nty) => {
@@ -233,32 +233,30 @@ pub(crate) mod rustc {
233233
Ok(Self::number(width as _))
234234
}
235235

236-
ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
236+
ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx),
237237

238238
ty::Array(inner_ty, len) => {
239-
let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
239+
let FieldsShape::Array { stride, count } = &layout.fields else {
240240
return Err(Err::NotYetSupported);
241241
};
242-
let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
243-
assert_eq!(*stride, inner_ty_and_layout.size);
244-
let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
242+
let inner_layout = ty_layout(cx, *inner_ty);
243+
assert_eq!(*stride, inner_layout.size);
244+
let elt = Tree::from_ty(*inner_ty, cx)?;
245245
Ok(std::iter::repeat(elt)
246246
.take(*count as usize)
247247
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
248248
}
249249

250-
ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
251-
match adt_def.adt_kind() {
252-
AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
253-
AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
254-
AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
255-
}
256-
}
250+
ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() {
251+
AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx),
252+
AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx),
253+
AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx),
254+
},
257255

258256
ty::Ref(lifetime, ty, mutability) => {
259-
let ty_and_layout = cx.layout_of(*ty)?;
260-
let align = ty_and_layout.align.abi.bytes_usize();
261-
let size = ty_and_layout.size.bytes_usize();
257+
let layout = ty_layout(cx, *ty);
258+
let align = layout.align.abi.bytes_usize();
259+
let size = layout.size.bytes_usize();
262260
Ok(Tree::Ref(Ref {
263261
lifetime: *lifetime,
264262
ty: *ty,
@@ -274,21 +272,20 @@ pub(crate) mod rustc {
274272

275273
/// Constructs a `Tree` from a tuple.
276274
fn from_tuple(
277-
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
275+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
278276
members: &'tcx List<Ty<'tcx>>,
279277
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
280278
) -> Result<Self, Err> {
281-
match &ty_and_layout.fields {
279+
match &layout.fields {
282280
FieldsShape::Primitive => {
283281
assert_eq!(members.len(), 1);
284282
let inner_ty = members[0];
285-
let inner_ty_and_layout = cx.layout_of(inner_ty)?;
286-
assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
287-
Self::from_ty(inner_ty_and_layout, cx)
283+
let inner_layout = ty_layout(cx, inner_ty);
284+
Self::from_ty(inner_ty, cx)
288285
}
289286
FieldsShape::Arbitrary { offsets, .. } => {
290287
assert_eq!(offsets.len(), members.len());
291-
Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
288+
Self::from_variant(Def::Primitive, None, (ty, layout), layout.size, cx)
292289
}
293290
FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
294291
}
@@ -300,13 +297,13 @@ pub(crate) mod rustc {
300297
///
301298
/// Panics if `def` is not a struct definition.
302299
fn from_struct(
303-
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
300+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
304301
def: AdtDef<'tcx>,
305302
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
306303
) -> Result<Self, Err> {
307304
assert!(def.is_struct());
308305
let def = Def::Adt(def);
309-
Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
306+
Self::from_variant(def, None, (ty, layout), layout.size, cx)
310307
}
311308

312309
/// Constructs a `Tree` from an enum.
@@ -315,19 +312,18 @@ pub(crate) mod rustc {
315312
///
316313
/// Panics if `def` is not an enum definition.
317314
fn from_enum(
318-
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
315+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
319316
def: AdtDef<'tcx>,
320317
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
321318
) -> Result<Self, Err> {
322319
assert!(def.is_enum());
323-
let layout = ty_and_layout.layout;
324320

325321
// Computes the variant of a given index.
326322
let layout_of_variant = |index| {
327-
let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, index));
323+
let tag = cx.tcx.tag_for_variant((ty, index));
328324
let variant_def = Def::Variant(def.variant(index));
329-
let variant_ty_and_layout = ty_and_layout.for_variant(&cx, index);
330-
Self::from_variant(variant_def, tag, variant_ty_and_layout, layout.size, cx)
325+
let variant_layout = ty_variant(cx, (ty, layout), index);
326+
Self::from_variant(variant_def, tag, (ty, variant_layout), layout.size, cx)
331327
};
332328

333329
// We consider three kinds of enums, each demanding a different
@@ -385,21 +381,20 @@ pub(crate) mod rustc {
385381
fn from_variant(
386382
def: Def<'tcx>,
387383
tag: Option<ScalarInt>,
388-
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
384+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
389385
total_size: Size,
390386
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
391387
) -> Result<Self, Err> {
392388
// This constructor does not support non-`FieldsShape::Arbitrary`
393389
// layouts.
394-
let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
395-
else {
390+
let FieldsShape::Arbitrary { offsets, memory_index } = layout.fields() else {
396391
return Err(Err::NotYetSupported);
397392
};
398393

399394
// When this function is invoked with enum variants,
400395
// `ty_and_layout.size` does not encompass the entire size of the
401396
// enum. We rely on `total_size` for this.
402-
assert!(ty_and_layout.size <= total_size);
397+
assert!(layout.size <= total_size);
403398

404399
let mut size = Size::ZERO;
405400
let mut struct_tree = Self::def(def);
@@ -412,17 +407,18 @@ pub(crate) mod rustc {
412407

413408
// Append the fields, in memory order, to the layout.
414409
let inverse_memory_index = memory_index.invert_bijective_mapping();
415-
for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
410+
for (memory_idx, &field_idx) in inverse_memory_index.iter_enumerated() {
416411
// Add interfield padding.
417-
let padding_needed = offsets[*field_idx] - size;
412+
let padding_needed = offsets[field_idx] - size;
418413
let padding = Self::padding(padding_needed.bytes_usize());
419414

420-
let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
421-
let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
415+
let field_ty = ty_field(cx, (ty, layout), field_idx);
416+
let field_layout = ty_layout(cx, field_ty);
417+
let field_tree = Self::from_ty(field_ty, cx)?;
422418

423419
struct_tree = struct_tree.then(padding).then(field_tree);
424420

425-
size += padding_needed + field_ty_and_layout.size;
421+
size += padding_needed + field_layout.size;
426422
}
427423

428424
// Add trailing padding.
@@ -457,28 +453,27 @@ pub(crate) mod rustc {
457453
///
458454
/// Panics if `def` is not a union definition.
459455
fn from_union(
460-
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
456+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
461457
def: AdtDef<'tcx>,
462458
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
463459
) -> Result<Self, Err> {
464460
assert!(def.is_union());
465461

466-
let union_layout = ty_and_layout.layout;
467-
468462
// This constructor does not support non-`FieldsShape::Union`
469463
// layouts. Fields of this shape are all placed at offset 0.
470-
let FieldsShape::Union(fields) = union_layout.fields() else {
464+
let FieldsShape::Union(fields) = layout.fields() else {
471465
return Err(Err::NotYetSupported);
472466
};
473467

474468
let fields = &def.non_enum_variant().fields;
475469
let fields = fields.iter_enumerated().try_fold(
476470
Self::uninhabited(),
477-
|fields, (idx, ref field_def)| {
471+
|fields, (idx, field_def)| {
478472
let field_def = Def::Field(field_def);
479-
let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
480-
let field = Self::from_ty(field_ty_and_layout, cx)?;
481-
let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
473+
let field_ty = ty_field(cx, (ty, layout), idx);
474+
let field_layout = ty_layout(cx, field_ty);
475+
let field = Self::from_ty(field_ty, cx)?;
476+
let trailing_padding_needed = layout.size - field_layout.size;
482477
let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
483478
let field_and_padding = field.then(trailing_padding);
484479
Result::<Self, Err>::Ok(fields.or(field_and_padding))
@@ -488,4 +483,44 @@ pub(crate) mod rustc {
488483
Ok(Self::def(Def::Adt(def)).then(fields))
489484
}
490485
}
486+
487+
pub(crate) fn ty_layout<'tcx>(cx: LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>) -> Layout<'tcx> {
488+
crate::layout::rustc::layout_of(cx, ty).unwrap()
489+
}
490+
491+
fn ty_field<'tcx>(
492+
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
493+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
494+
i: FieldIdx,
495+
) -> Ty<'tcx> {
496+
match ty.kind() {
497+
ty::Adt(def, args) => {
498+
match layout.variants {
499+
Variants::Single { index } => {
500+
let field = &def.variant(index).fields[i];
501+
field.ty(cx.tcx, args)
502+
}
503+
// Discriminant field for enums (where applicable).
504+
Variants::Multiple { tag, .. } => {
505+
assert_eq!(i.as_usize(), 0);
506+
ty::layout::PrimitiveExt::to_ty(&tag.primitive(), cx.tcx)
507+
}
508+
}
509+
}
510+
ty::Tuple(fields) => fields[i.as_usize()],
511+
kind @ _ => unimplemented!(
512+
"only a subset of `Ty::ty_and_layout_field`'s functionality is implemented. implementation needed for {:?}",
513+
kind
514+
),
515+
}
516+
}
517+
518+
fn ty_variant<'tcx>(
519+
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
520+
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
521+
i: VariantIdx,
522+
) -> Layout<'tcx> {
523+
let ty = cx.tcx.erase_regions(ty);
524+
TyAndLayout { ty, layout }.for_variant(&cx, i).layout
525+
}
491526
}

‎compiler/rustc_transmute/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}
99
pub mod layout;
1010
mod maybe_transmutable;
1111

12-
#[derive(Default)]
12+
#[derive(Copy, Clone, Debug, Default)]
1313
pub struct Assume {
1414
pub alignment: bool,
1515
pub lifetimes: bool,

‎compiler/rustc_transmute/src/maybe_transmutable/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ where
3030
// FIXME: Nix this cfg, so we can write unit tests independently of rustc
3131
#[cfg(feature = "rustc")]
3232
mod rustc {
33-
use rustc_middle::ty::layout::{LayoutCx, LayoutOf};
33+
use rustc_middle::ty::layout::LayoutCx;
3434
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
3535

3636
use super::*;
@@ -45,10 +45,9 @@ mod rustc {
4545

4646
let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
4747
let layout_of = |ty| {
48-
layout_cx
49-
.layout_of(ty)
48+
crate::layout::rustc::layout_of(layout_cx, ty)
5049
.map_err(|_| Err::NotYetSupported)
51-
.and_then(|tl| Tree::from_ty(tl, layout_cx))
50+
.and_then(|_| Tree::from_ty(ty, layout_cx))
5251
};
5352

5453
// Convert `src` and `dst` from their rustc representations, to `Tree`-based
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//@ check-pass
2+
3+
//! Accept lifetime extensions with `Assume::LIFETIMES`.
4+
5+
#![feature(transmutability, core_intrinsics)]
6+
7+
use std::mem::{Assume, BikeshedIntrinsicFrom};
8+
9+
unsafe fn transmute<Src, Dst>(src: Src) -> Dst
10+
where
11+
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY.and(Assume::LIFETIMES) }>,
12+
{
13+
core::intrinsics::transmute_unchecked(src)
14+
}
15+
16+
mod bare {
17+
use super::*;
18+
19+
fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
20+
unsafe { transmute(src) }
21+
}
22+
}
23+
24+
mod nested {
25+
use super::*;
26+
27+
fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
28+
unsafe { transmute(src) }
29+
}
30+
}
31+
32+
mod tuple {
33+
use super::*;
34+
35+
fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
36+
unsafe { transmute(src) }
37+
}
38+
39+
fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
40+
unsafe { transmute(src) }
41+
}
42+
}
43+
44+
mod r#struct {
45+
use super::*;
46+
47+
struct Struct<'a>(&'a u8);
48+
49+
fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
50+
unsafe { transmute(src) }
51+
}
52+
}
53+
54+
mod r#enum {
55+
use super::*;
56+
57+
enum Single<'a> {
58+
A(&'a u8),
59+
}
60+
61+
fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
62+
unsafe { transmute(src) }
63+
}
64+
65+
enum Multi<'a> {
66+
A(&'a u8),
67+
B,
68+
C,
69+
}
70+
71+
fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
72+
unsafe { transmute(src) }
73+
}
74+
}
75+
76+
mod hrtb {
77+
use super::*;
78+
79+
fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
80+
unsafe { extend_hrtb(src) }
81+
}
82+
83+
unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8
84+
where
85+
for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8, { Assume::LIFETIMES }>,
86+
{
87+
core::intrinsics::transmute_unchecked(src)
88+
}
89+
}
90+
91+
fn main() {}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//@ check-pass
2+
3+
//! Accept lifetime extensions of un-exercised lifetimes.
4+
5+
#![feature(transmutability, core_intrinsics)]
6+
7+
use std::mem::{Assume, BikeshedIntrinsicFrom};
8+
9+
unsafe fn transmute<Src, Dst>(src: Src) -> Dst
10+
where
11+
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>,
12+
{
13+
core::intrinsics::transmute_unchecked(src)
14+
}
15+
16+
enum Void {}
17+
18+
mod phantom {
19+
use super::*;
20+
use std::marker::PhantomData;
21+
22+
fn extend_bare<'a>(src: PhantomData<&'a u8>) -> PhantomData<&'static u8> {
23+
unsafe { transmute(src) }
24+
}
25+
}
26+
27+
28+
mod tuple {
29+
use super::*;
30+
31+
fn extend_pair<'a>(src: (&'a u8, Void)) -> (&'static u8, Void) {
32+
unsafe { transmute(src) }
33+
}
34+
}
35+
36+
mod r#struct {
37+
use super::*;
38+
39+
struct Struct<'a>(&'a u8, Void);
40+
41+
fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
42+
unsafe { transmute(src) }
43+
}
44+
}
45+
46+
mod r#enum {
47+
use super::*;
48+
49+
enum Single<'a> {
50+
A(&'a u8, Void),
51+
}
52+
53+
fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
54+
unsafe { transmute(src) }
55+
}
56+
57+
enum Multi<'a> {
58+
A(&'a u8, Void),
59+
B,
60+
C,
61+
}
62+
63+
fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
64+
unsafe { transmute(src) }
65+
}
66+
}
67+
68+
fn main() {}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//@ check-fail
2+
3+
//! Reject lifetime extensions.
4+
5+
#![feature(transmutability, core_intrinsics)]
6+
7+
use std::mem::{Assume, BikeshedIntrinsicFrom};
8+
9+
unsafe fn transmute<Src, Dst>(src: Src) -> Dst
10+
where
11+
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>,
12+
{
13+
core::intrinsics::transmute_unchecked(src)
14+
}
15+
16+
mod bare {
17+
use super::*;
18+
19+
fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
20+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
21+
}
22+
}
23+
24+
mod nested {
25+
use super::*;
26+
27+
fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
28+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
29+
}
30+
}
31+
32+
mod tuple {
33+
use super::*;
34+
35+
fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
36+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
37+
}
38+
39+
fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
40+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
41+
}
42+
}
43+
44+
mod r#struct {
45+
use super::*;
46+
47+
struct Struct<'a>(&'a u8);
48+
49+
fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
50+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
51+
}
52+
}
53+
54+
mod r#enum {
55+
use super::*;
56+
57+
enum Single<'a> {
58+
A(&'a u8),
59+
}
60+
61+
fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
62+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
63+
}
64+
65+
enum Multi<'a> {
66+
A(&'a u8),
67+
B,
68+
C,
69+
}
70+
71+
fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
72+
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
73+
}
74+
}
75+
76+
mod hrtb {
77+
use super::*;
78+
79+
fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
80+
unsafe { extend_hrtb(src) } //~ ERROR borrowed data escapes outside of function
81+
}
82+
83+
unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8
84+
where
85+
for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8>,
86+
{
87+
core::intrinsics::transmute_unchecked(src)
88+
}
89+
}
90+
91+
fn main() {}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/reject_lifetime_extension.rs:20:18
3+
|
4+
LL | fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
5+
| -- lifetime `'a` defined here
6+
LL | unsafe { transmute(src) }
7+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
8+
9+
error: lifetime may not live long enough
10+
--> $DIR/reject_lifetime_extension.rs:28:18
11+
|
12+
LL | fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
13+
| -- lifetime `'a` defined here
14+
LL | unsafe { transmute(src) }
15+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
16+
17+
error: lifetime may not live long enough
18+
--> $DIR/reject_lifetime_extension.rs:36:18
19+
|
20+
LL | fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
21+
| -- lifetime `'a` defined here
22+
LL | unsafe { transmute(src) }
23+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
24+
25+
error: lifetime may not live long enough
26+
--> $DIR/reject_lifetime_extension.rs:40:18
27+
|
28+
LL | fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
29+
| -- lifetime `'a` defined here
30+
LL | unsafe { transmute(src) }
31+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
32+
33+
error: lifetime may not live long enough
34+
--> $DIR/reject_lifetime_extension.rs:50:18
35+
|
36+
LL | fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
37+
| -- lifetime `'a` defined here
38+
LL | unsafe { transmute(src) }
39+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
40+
41+
error: lifetime may not live long enough
42+
--> $DIR/reject_lifetime_extension.rs:62:18
43+
|
44+
LL | fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
45+
| -- lifetime `'a` defined here
46+
LL | unsafe { transmute(src) }
47+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
48+
49+
error: lifetime may not live long enough
50+
--> $DIR/reject_lifetime_extension.rs:72:18
51+
|
52+
LL | fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
53+
| -- lifetime `'a` defined here
54+
LL | unsafe { transmute(src) }
55+
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
56+
57+
error[E0521]: borrowed data escapes outside of function
58+
--> $DIR/reject_lifetime_extension.rs:80:18
59+
|
60+
LL | fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
61+
| -- --- `src` is a reference that is only valid in the function body
62+
| |
63+
| lifetime `'a` defined here
64+
LL | unsafe { extend_hrtb(src) }
65+
| ^^^^^^^^^^^^^^^^
66+
| |
67+
| `src` escapes the function body here
68+
| argument requires that `'a` must outlive `'static`
69+
|
70+
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
71+
--> $DIR/reject_lifetime_extension.rs:85:25
72+
|
73+
LL | for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8>,
74+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
75+
76+
error: aborting due to 8 previous errors
77+
78+
For more information about this error, try `rustc --explain E0521`.

0 commit comments

Comments
 (0)
Please sign in to comment.