mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #129275 - matthiaskrgr:rollup-qv64hg6, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #129194 (Fix bootstrap test `detect_src_and_out` on Windows) - #129217 (safe transmute: check lifetimes) - #129223 ( Fix wrong argument for `get_fn_decl`) - #129235 (Check that `#[may_dangle]` is properly applied) - #129245 (Fix a typo in `rustc_hir` doc comment) - #129271 (Prevent double panic in query system, improve diagnostics) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
636d7ff91b
@ -1395,7 +1395,7 @@ pub struct LetExpr<'hir> {
|
||||
pub pat: &'hir Pat<'hir>,
|
||||
pub ty: Option<&'hir Ty<'hir>>,
|
||||
pub init: &'hir Expr<'hir>,
|
||||
/// `Recovered::Yes` when this let expressions is not in a syntanctically valid location.
|
||||
/// `Recovered::Yes` when this let expressions is not in a syntactically valid location.
|
||||
/// Used to prevent building MIR in such situations.
|
||||
pub recovered: ast::Recovered,
|
||||
}
|
||||
|
@ -1859,7 +1859,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
||||
};
|
||||
|
||||
// If this is due to an explicit `return`, suggest adding a return type.
|
||||
if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id)
|
||||
if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(block_or_return_id)
|
||||
&& !due_to_block
|
||||
{
|
||||
fcx.suggest_missing_return_type(&mut err, fn_decl, expected, found, can_suggest, fn_id);
|
||||
|
@ -444,6 +444,9 @@ passes_macro_export_on_decl_macro =
|
||||
passes_macro_use =
|
||||
`#[{$name}]` only has an effect on `extern crate` and modules
|
||||
|
||||
passes_may_dangle =
|
||||
`#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
|
||||
passes_maybe_string_interpolation = you might have meant to use string interpolation in this string literal
|
||||
passes_missing_const_err =
|
||||
attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const`
|
||||
@ -475,8 +478,8 @@ passes_multiple_start_functions =
|
||||
.previous = previous `#[start]` function here
|
||||
|
||||
passes_must_not_suspend =
|
||||
`must_not_suspend` attribute should be applied to a struct, enum, or trait
|
||||
.label = is not a struct, enum, or trait
|
||||
`must_not_suspend` attribute should be applied to a struct, enum, union, or trait
|
||||
.label = is not a struct, enum, union, or trait
|
||||
|
||||
passes_must_use_async =
|
||||
`must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
|
||||
|
@ -189,6 +189,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
[sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
|
||||
[sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
|
||||
[sym::must_use, ..] => self.check_must_use(hir_id, attr, target),
|
||||
[sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr),
|
||||
[sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
|
||||
[sym::rustc_allow_incoherent_impl, ..] => {
|
||||
self.check_allow_incoherent_impl(attr, span, target)
|
||||
@ -255,7 +256,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
| sym::cfg_attr
|
||||
// need to be fixed
|
||||
| sym::cfi_encoding // FIXME(cfi_encoding)
|
||||
| sym::may_dangle // FIXME(dropck_eyepatch)
|
||||
| sym::pointee // FIXME(derive_smart_pointer)
|
||||
| sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section)
|
||||
| sym::used // handled elsewhere to restrict to static items
|
||||
@ -1363,7 +1363,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `#[must_not_suspend]` is applied to a function.
|
||||
/// Checks if `#[must_not_suspend]` is applied to a struct, enum, union, or trait.
|
||||
fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
|
||||
match target {
|
||||
Target::Struct | Target::Enum | Target::Union | Target::Trait => {}
|
||||
@ -1373,6 +1373,27 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.
|
||||
fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
|
||||
if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)
|
||||
&& matches!(
|
||||
param.kind,
|
||||
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }
|
||||
)
|
||||
&& matches!(param.source, hir::GenericParamSource::Generics)
|
||||
&& let parent_hir_id = self.tcx.parent_hir_id(hir_id)
|
||||
&& let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)
|
||||
&& let hir::ItemKind::Impl(impl_) = item.kind
|
||||
&& let Some(trait_) = impl_.of_trait
|
||||
&& let Some(def_id) = trait_.trait_def_id()
|
||||
&& self.tcx.is_lang_item(def_id, hir::LangItem::Drop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span });
|
||||
}
|
||||
|
||||
/// Checks if `#[cold]` is applied to a non-function.
|
||||
fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
|
||||
match target {
|
||||
|
@ -737,6 +737,13 @@ pub struct NonExportedMacroInvalidAttrs {
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_may_dangle)]
|
||||
pub struct InvalidMayDangle {
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_unused_duplicate)]
|
||||
pub struct UnusedDuplicate {
|
||||
|
@ -702,11 +702,17 @@ macro_rules! define_queries {
|
||||
let name = stringify!($name);
|
||||
$crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name)
|
||||
};
|
||||
tcx.query_system.states.$name.try_collect_active_jobs(
|
||||
let res = tcx.query_system.states.$name.try_collect_active_jobs(
|
||||
tcx,
|
||||
make_query,
|
||||
qmap,
|
||||
).unwrap();
|
||||
);
|
||||
// this can be called during unwinding, and the function has a `try_`-prefix, so
|
||||
// don't `unwrap()` here, just manually check for `None` and do best-effort error
|
||||
// reporting.
|
||||
if res.is_none() {
|
||||
tracing::warn!("Failed to collect active jobs for query with name `{}`!", stringify!($name));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc_self_profile_query_strings<'tcx>(tcx: TyCtxt<'tcx>, string_cache: &mut QueryKeyStringCache) {
|
||||
|
@ -181,8 +181,15 @@ where
|
||||
cache.complete(key, result, dep_node_index);
|
||||
|
||||
let job = {
|
||||
let mut lock = state.active.lock_shard_by_value(&key);
|
||||
lock.remove(&key).unwrap().expect_job()
|
||||
let val = {
|
||||
// don't keep the lock during the `unwrap()` of the retrieved value, or we taint the
|
||||
// underlying shard.
|
||||
// since unwinding also wants to look at this map, this can also prevent a double
|
||||
// panic.
|
||||
let mut lock = state.active.lock_shard_by_value(&key);
|
||||
lock.remove(&key)
|
||||
};
|
||||
val.unwrap().expect_job()
|
||||
};
|
||||
|
||||
job.signal_complete();
|
||||
|
@ -17,8 +17,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
|
||||
use rustc_infer::traits::ObligationCauseCode;
|
||||
use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TraitPredicate, Ty,
|
||||
TyCtxt, Upcast,
|
||||
self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
@ -292,90 +291,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
&mut self,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
|
||||
use rustc_transmute::{Answer, Condition};
|
||||
#[instrument(level = "debug", skip(tcx, obligation, predicate))]
|
||||
use rustc_transmute::{Answer, Assume, Condition};
|
||||
|
||||
/// Generate sub-obligations for reference-to-reference transmutations.
|
||||
fn reference_obligations<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
(src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
|
||||
(dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
|
||||
assume: Assume,
|
||||
) -> Vec<PredicateObligation<'tcx>> {
|
||||
let make_transmute_obl = |src, dst| {
|
||||
let transmute_trait = obligation.predicate.def_id();
|
||||
let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
transmute_trait,
|
||||
[
|
||||
ty::GenericArg::from(dst),
|
||||
ty::GenericArg::from(src),
|
||||
ty::GenericArg::from(assume),
|
||||
],
|
||||
);
|
||||
Obligation::with_depth(
|
||||
tcx,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.param_env,
|
||||
obligation.predicate.rebind(trait_ref),
|
||||
)
|
||||
};
|
||||
|
||||
let make_freeze_obl = |ty| {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Freeze, None),
|
||||
[ty::GenericArg::from(ty)],
|
||||
);
|
||||
Obligation::with_depth(
|
||||
tcx,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.param_env,
|
||||
trait_ref,
|
||||
)
|
||||
};
|
||||
|
||||
let make_outlives_obl = |target, region| {
|
||||
let outlives = ty::OutlivesPredicate(target, region);
|
||||
Obligation::with_depth(
|
||||
tcx,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.param_env,
|
||||
obligation.predicate.rebind(outlives),
|
||||
)
|
||||
};
|
||||
|
||||
// Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
|
||||
// it is always the case that `Src` must be transmutable into `Dst`,
|
||||
// and that that `'src` must outlive `'dst`.
|
||||
let mut obls = vec![make_transmute_obl(src_ty, dst_ty)];
|
||||
if !assume.lifetimes {
|
||||
obls.push(make_outlives_obl(src_lifetime, dst_lifetime));
|
||||
}
|
||||
|
||||
// Given a transmutation from `&Src`, both `Src` and `Dst` must be
|
||||
// `Freeze`, otherwise, using the transmuted value could lead to
|
||||
// data races.
|
||||
if src_mut == Mutability::Not {
|
||||
obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)])
|
||||
}
|
||||
|
||||
// Given a transmutation into `&'dst mut Dst`, it also must be the
|
||||
// case that `Dst` is transmutable into `Src`. For example,
|
||||
// transmuting bool -> u8 is OK as long as you can't update that u8
|
||||
// to be > 1, because you could later transmute the u8 back to a
|
||||
// bool and get undefined behavior. It also must be the case that
|
||||
// `'dst` lives exactly as long as `'src`.
|
||||
if dst_mut == Mutability::Mut {
|
||||
obls.push(make_transmute_obl(dst_ty, src_ty));
|
||||
if !assume.lifetimes {
|
||||
obls.push(make_outlives_obl(dst_lifetime, src_lifetime));
|
||||
}
|
||||
}
|
||||
|
||||
obls
|
||||
}
|
||||
|
||||
/// Flatten the `Condition` tree into a conjunction of obligations.
|
||||
#[instrument(level = "debug", skip(tcx, obligation))]
|
||||
fn flatten_answer_tree<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
predicate: TraitPredicate<'tcx>,
|
||||
cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
|
||||
assume: Assume,
|
||||
) -> Vec<PredicateObligation<'tcx>> {
|
||||
match cond {
|
||||
// FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
|
||||
// Not possible until the trait solver supports disjunctions of obligations
|
||||
Condition::IfAll(conds) | Condition::IfAny(conds) => conds
|
||||
.into_iter()
|
||||
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
|
||||
.flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume))
|
||||
.collect(),
|
||||
Condition::IfTransmutable { src, dst } => {
|
||||
let transmute_trait = obligation.predicate.def_id();
|
||||
let assume_const = predicate.trait_ref.args.const_at(2);
|
||||
let make_transmute_obl = |from_ty, to_ty| {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
transmute_trait,
|
||||
[
|
||||
ty::GenericArg::from(to_ty),
|
||||
ty::GenericArg::from(from_ty),
|
||||
ty::GenericArg::from(assume_const),
|
||||
],
|
||||
);
|
||||
Obligation::with_depth(
|
||||
tcx,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.param_env,
|
||||
trait_ref,
|
||||
)
|
||||
};
|
||||
|
||||
let make_freeze_obl = |ty| {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Freeze, None),
|
||||
[ty::GenericArg::from(ty)],
|
||||
);
|
||||
Obligation::with_depth(
|
||||
tcx,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.param_env,
|
||||
trait_ref,
|
||||
)
|
||||
};
|
||||
|
||||
let mut obls = vec![];
|
||||
|
||||
// If the source is a shared reference, it must be `Freeze`;
|
||||
// otherwise, transmuting could lead to data races.
|
||||
if src.mutability == Mutability::Not {
|
||||
obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
|
||||
}
|
||||
|
||||
// If Dst is mutable, check bidirectionally.
|
||||
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
|
||||
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
|
||||
match dst.mutability {
|
||||
Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
|
||||
Mutability::Mut => obls.extend([
|
||||
make_transmute_obl(src.ty, dst.ty),
|
||||
make_transmute_obl(dst.ty, src.ty),
|
||||
]),
|
||||
}
|
||||
|
||||
obls
|
||||
}
|
||||
Condition::IfTransmutable { src, dst } => reference_obligations(
|
||||
tcx,
|
||||
obligation,
|
||||
(src.lifetime, src.ty, src.mutability),
|
||||
(dst.lifetime, dst.ty, dst.mutability),
|
||||
assume,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// We erase regions here because transmutability calls layout queries,
|
||||
// which does not handle inference regions and doesn't particularly
|
||||
// care about other regions. Erasing late-bound regions is equivalent
|
||||
// to instantiating the binder with placeholders then erasing those
|
||||
// placeholder regions.
|
||||
let predicate = self
|
||||
.tcx()
|
||||
.erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate));
|
||||
let predicate = obligation.predicate.skip_binder();
|
||||
|
||||
let Some(assume) = rustc_transmute::Assume::from_const(
|
||||
self.infcx.tcx,
|
||||
@ -387,6 +416,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
|
||||
let dst = predicate.trait_ref.args.type_at(0);
|
||||
let src = predicate.trait_ref.args.type_at(1);
|
||||
|
||||
debug!(?src, ?dst);
|
||||
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
|
||||
let maybe_transmutable = transmute_env.is_transmutable(
|
||||
@ -397,7 +427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
|
||||
let fully_flattened = match maybe_transmutable {
|
||||
Answer::No(_) => Err(Unimplemented)?,
|
||||
Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
|
||||
Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
|
||||
Answer::Yes => vec![],
|
||||
};
|
||||
|
||||
|
@ -63,7 +63,9 @@ pub mod rustc {
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutError};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_target::abi::Layout;
|
||||
|
||||
/// A reference in the layout.
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
|
||||
@ -120,4 +122,13 @@ pub mod rustc {
|
||||
self != &Self::Primitive
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn layout_of<'tcx>(
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
let ty = cx.tcx.erase_regions(ty);
|
||||
cx.layout_of(ty).map(|tl| tl.layout)
|
||||
}
|
||||
}
|
||||
|
@ -171,10 +171,12 @@ where
|
||||
|
||||
#[cfg(feature = "rustc")]
|
||||
pub(crate) mod rustc {
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
|
||||
use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use rustc_target::abi::{FieldsShape, Size, TyAndLayout, Variants};
|
||||
use rustc_target::abi::{
|
||||
FieldIdx, FieldsShape, Layout, Size, TyAndLayout, VariantIdx, Variants,
|
||||
};
|
||||
|
||||
use super::Tree;
|
||||
use crate::layout::rustc::{Def, Ref};
|
||||
@ -202,20 +204,18 @@ pub(crate) mod rustc {
|
||||
}
|
||||
|
||||
impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
|
||||
pub fn from_ty(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
pub fn from_ty(ty: Ty<'tcx>, cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, Err> {
|
||||
use rustc_target::abi::HasDataLayout;
|
||||
let layout = ty_layout(cx, ty);
|
||||
|
||||
if let Err(e) = ty_and_layout.ty.error_reported() {
|
||||
if let Err(e) = ty.error_reported() {
|
||||
return Err(Err::TypeError(e));
|
||||
}
|
||||
|
||||
let target = cx.tcx.data_layout();
|
||||
let pointer_size = target.pointer_size;
|
||||
|
||||
match ty_and_layout.ty.kind() {
|
||||
match ty.kind() {
|
||||
ty::Bool => Ok(Self::bool()),
|
||||
|
||||
ty::Float(nty) => {
|
||||
@ -233,32 +233,30 @@ pub(crate) mod rustc {
|
||||
Ok(Self::number(width as _))
|
||||
}
|
||||
|
||||
ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
|
||||
ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx),
|
||||
|
||||
ty::Array(inner_ty, len) => {
|
||||
let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
|
||||
let FieldsShape::Array { stride, count } = &layout.fields else {
|
||||
return Err(Err::NotYetSupported);
|
||||
};
|
||||
let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
|
||||
assert_eq!(*stride, inner_ty_and_layout.size);
|
||||
let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
|
||||
let inner_layout = ty_layout(cx, *inner_ty);
|
||||
assert_eq!(*stride, inner_layout.size);
|
||||
let elt = Tree::from_ty(*inner_ty, cx)?;
|
||||
Ok(std::iter::repeat(elt)
|
||||
.take(*count as usize)
|
||||
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
|
||||
}
|
||||
|
||||
ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
|
||||
match adt_def.adt_kind() {
|
||||
AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
|
||||
AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
|
||||
AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
|
||||
}
|
||||
}
|
||||
ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() {
|
||||
AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx),
|
||||
AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx),
|
||||
AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx),
|
||||
},
|
||||
|
||||
ty::Ref(lifetime, ty, mutability) => {
|
||||
let ty_and_layout = cx.layout_of(*ty)?;
|
||||
let align = ty_and_layout.align.abi.bytes_usize();
|
||||
let size = ty_and_layout.size.bytes_usize();
|
||||
let layout = ty_layout(cx, *ty);
|
||||
let align = layout.align.abi.bytes_usize();
|
||||
let size = layout.size.bytes_usize();
|
||||
Ok(Tree::Ref(Ref {
|
||||
lifetime: *lifetime,
|
||||
ty: *ty,
|
||||
@ -274,21 +272,20 @@ pub(crate) mod rustc {
|
||||
|
||||
/// Constructs a `Tree` from a tuple.
|
||||
fn from_tuple(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
members: &'tcx List<Ty<'tcx>>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
match &ty_and_layout.fields {
|
||||
match &layout.fields {
|
||||
FieldsShape::Primitive => {
|
||||
assert_eq!(members.len(), 1);
|
||||
let inner_ty = members[0];
|
||||
let inner_ty_and_layout = cx.layout_of(inner_ty)?;
|
||||
assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
|
||||
Self::from_ty(inner_ty_and_layout, cx)
|
||||
let inner_layout = ty_layout(cx, inner_ty);
|
||||
Self::from_ty(inner_ty, cx)
|
||||
}
|
||||
FieldsShape::Arbitrary { offsets, .. } => {
|
||||
assert_eq!(offsets.len(), members.len());
|
||||
Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
|
||||
Self::from_variant(Def::Primitive, None, (ty, layout), layout.size, cx)
|
||||
}
|
||||
FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
|
||||
}
|
||||
@ -300,13 +297,13 @@ pub(crate) mod rustc {
|
||||
///
|
||||
/// Panics if `def` is not a struct definition.
|
||||
fn from_struct(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
def: AdtDef<'tcx>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
assert!(def.is_struct());
|
||||
let def = Def::Adt(def);
|
||||
Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
|
||||
Self::from_variant(def, None, (ty, layout), layout.size, cx)
|
||||
}
|
||||
|
||||
/// Constructs a `Tree` from an enum.
|
||||
@ -315,19 +312,18 @@ pub(crate) mod rustc {
|
||||
///
|
||||
/// Panics if `def` is not an enum definition.
|
||||
fn from_enum(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
def: AdtDef<'tcx>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
assert!(def.is_enum());
|
||||
let layout = ty_and_layout.layout;
|
||||
|
||||
// Computes the variant of a given index.
|
||||
let layout_of_variant = |index| {
|
||||
let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, index));
|
||||
let tag = cx.tcx.tag_for_variant((cx.tcx.erase_regions(ty), index));
|
||||
let variant_def = Def::Variant(def.variant(index));
|
||||
let variant_ty_and_layout = ty_and_layout.for_variant(&cx, index);
|
||||
Self::from_variant(variant_def, tag, variant_ty_and_layout, layout.size, cx)
|
||||
let variant_layout = ty_variant(cx, (ty, layout), index);
|
||||
Self::from_variant(variant_def, tag, (ty, variant_layout), layout.size, cx)
|
||||
};
|
||||
|
||||
// We consider three kinds of enums, each demanding a different
|
||||
@ -385,21 +381,20 @@ pub(crate) mod rustc {
|
||||
fn from_variant(
|
||||
def: Def<'tcx>,
|
||||
tag: Option<ScalarInt>,
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
total_size: Size,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
// This constructor does not support non-`FieldsShape::Arbitrary`
|
||||
// layouts.
|
||||
let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
|
||||
else {
|
||||
let FieldsShape::Arbitrary { offsets, memory_index } = layout.fields() else {
|
||||
return Err(Err::NotYetSupported);
|
||||
};
|
||||
|
||||
// When this function is invoked with enum variants,
|
||||
// `ty_and_layout.size` does not encompass the entire size of the
|
||||
// enum. We rely on `total_size` for this.
|
||||
assert!(ty_and_layout.size <= total_size);
|
||||
assert!(layout.size <= total_size);
|
||||
|
||||
let mut size = Size::ZERO;
|
||||
let mut struct_tree = Self::def(def);
|
||||
@ -412,17 +407,18 @@ pub(crate) mod rustc {
|
||||
|
||||
// Append the fields, in memory order, to the layout.
|
||||
let inverse_memory_index = memory_index.invert_bijective_mapping();
|
||||
for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
|
||||
for (memory_idx, &field_idx) in inverse_memory_index.iter_enumerated() {
|
||||
// Add interfield padding.
|
||||
let padding_needed = offsets[*field_idx] - size;
|
||||
let padding_needed = offsets[field_idx] - size;
|
||||
let padding = Self::padding(padding_needed.bytes_usize());
|
||||
|
||||
let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
|
||||
let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
|
||||
let field_ty = ty_field(cx, (ty, layout), field_idx);
|
||||
let field_layout = ty_layout(cx, field_ty);
|
||||
let field_tree = Self::from_ty(field_ty, cx)?;
|
||||
|
||||
struct_tree = struct_tree.then(padding).then(field_tree);
|
||||
|
||||
size += padding_needed + field_ty_and_layout.size;
|
||||
size += padding_needed + field_layout.size;
|
||||
}
|
||||
|
||||
// Add trailing padding.
|
||||
@ -457,28 +453,27 @@ pub(crate) mod rustc {
|
||||
///
|
||||
/// Panics if `def` is not a union definition.
|
||||
fn from_union(
|
||||
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
def: AdtDef<'tcx>,
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Result<Self, Err> {
|
||||
assert!(def.is_union());
|
||||
|
||||
let union_layout = ty_and_layout.layout;
|
||||
|
||||
// This constructor does not support non-`FieldsShape::Union`
|
||||
// layouts. Fields of this shape are all placed at offset 0.
|
||||
let FieldsShape::Union(fields) = union_layout.fields() else {
|
||||
let FieldsShape::Union(fields) = layout.fields() else {
|
||||
return Err(Err::NotYetSupported);
|
||||
};
|
||||
|
||||
let fields = &def.non_enum_variant().fields;
|
||||
let fields = fields.iter_enumerated().try_fold(
|
||||
Self::uninhabited(),
|
||||
|fields, (idx, ref field_def)| {
|
||||
|fields, (idx, field_def)| {
|
||||
let field_def = Def::Field(field_def);
|
||||
let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
|
||||
let field = Self::from_ty(field_ty_and_layout, cx)?;
|
||||
let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
|
||||
let field_ty = ty_field(cx, (ty, layout), idx);
|
||||
let field_layout = ty_layout(cx, field_ty);
|
||||
let field = Self::from_ty(field_ty, cx)?;
|
||||
let trailing_padding_needed = layout.size - field_layout.size;
|
||||
let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
|
||||
let field_and_padding = field.then(trailing_padding);
|
||||
Result::<Self, Err>::Ok(fields.or(field_and_padding))
|
||||
@ -488,4 +483,44 @@ pub(crate) mod rustc {
|
||||
Ok(Self::def(Def::Adt(def)).then(fields))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ty_layout<'tcx>(cx: LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>) -> Layout<'tcx> {
|
||||
crate::layout::rustc::layout_of(cx, ty).unwrap()
|
||||
}
|
||||
|
||||
fn ty_field<'tcx>(
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
i: FieldIdx,
|
||||
) -> Ty<'tcx> {
|
||||
match ty.kind() {
|
||||
ty::Adt(def, args) => {
|
||||
match layout.variants {
|
||||
Variants::Single { index } => {
|
||||
let field = &def.variant(index).fields[i];
|
||||
field.ty(cx.tcx, args)
|
||||
}
|
||||
// Discriminant field for enums (where applicable).
|
||||
Variants::Multiple { tag, .. } => {
|
||||
assert_eq!(i.as_usize(), 0);
|
||||
ty::layout::PrimitiveExt::to_ty(&tag.primitive(), cx.tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Tuple(fields) => fields[i.as_usize()],
|
||||
kind @ _ => unimplemented!(
|
||||
"only a subset of `Ty::ty_and_layout_field`'s functionality is implemented. implementation needed for {:?}",
|
||||
kind
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_variant<'tcx>(
|
||||
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||
i: VariantIdx,
|
||||
) -> Layout<'tcx> {
|
||||
let ty = cx.tcx.erase_regions(ty);
|
||||
TyAndLayout { ty, layout }.for_variant(&cx, i).layout
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}
|
||||
pub mod layout;
|
||||
mod maybe_transmutable;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Assume {
|
||||
pub alignment: bool,
|
||||
pub lifetimes: bool,
|
||||
|
@ -30,7 +30,7 @@ where
|
||||
// FIXME: Nix this cfg, so we can write unit tests independently of rustc
|
||||
#[cfg(feature = "rustc")]
|
||||
mod rustc {
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutOf};
|
||||
use rustc_middle::ty::layout::LayoutCx;
|
||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||
|
||||
use super::*;
|
||||
@ -45,10 +45,9 @@ mod rustc {
|
||||
|
||||
let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
|
||||
let layout_of = |ty| {
|
||||
layout_cx
|
||||
.layout_of(ty)
|
||||
crate::layout::rustc::layout_of(layout_cx, ty)
|
||||
.map_err(|_| Err::NotYetSupported)
|
||||
.and_then(|tl| Tree::from_ty(tl, layout_cx))
|
||||
.and_then(|_| Tree::from_ty(ty, layout_cx))
|
||||
};
|
||||
|
||||
// Convert `src` and `dst` from their rustc representations, to `Tree`-based
|
||||
|
@ -96,8 +96,8 @@ fn detect_src_and_out() {
|
||||
test(parse(""), None);
|
||||
|
||||
{
|
||||
let build_dir = if cfg!(windows) { Some("C:\\tmp") } else { Some("/tmp") };
|
||||
test(parse("build.build-dir = \"/tmp\""), build_dir);
|
||||
let build_dir = if cfg!(windows) { "C:\\tmp" } else { "/tmp" };
|
||||
test(parse(&format!("build.build-dir = '{build_dir}'")), Some(build_dir));
|
||||
}
|
||||
}
|
||||
|
||||
|
9
tests/ui/async-await/async-fn/recurse-ice-129215.rs
Normal file
9
tests/ui/async-await/async-fn/recurse-ice-129215.rs
Normal file
@ -0,0 +1,9 @@
|
||||
//@ edition: 2021
|
||||
|
||||
async fn a() {
|
||||
//~^ ERROR `()` is not a future
|
||||
//~| ERROR mismatched types
|
||||
a() //~ ERROR `()` is not a future
|
||||
}
|
||||
|
||||
fn main() {}
|
34
tests/ui/async-await/async-fn/recurse-ice-129215.stderr
Normal file
34
tests/ui/async-await/async-fn/recurse-ice-129215.stderr
Normal file
@ -0,0 +1,34 @@
|
||||
error[E0277]: `()` is not a future
|
||||
--> $DIR/recurse-ice-129215.rs:6:5
|
||||
|
|
||||
LL | a()
|
||||
| ^^^ `()` is not a future
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `()`
|
||||
|
||||
error[E0277]: `()` is not a future
|
||||
--> $DIR/recurse-ice-129215.rs:3:1
|
||||
|
|
||||
LL | async fn a() {
|
||||
| ^^^^^^^^^^^^ `()` is not a future
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/recurse-ice-129215.rs:3:14
|
||||
|
|
||||
LL | async fn a() {
|
||||
| ______________^
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | a()
|
||||
LL | | }
|
||||
| |_^ expected `()`, found `async` fn body
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found `async` fn body `{async fn body of a()}`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0308.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
53
tests/ui/attributes/may_dangle.rs
Normal file
53
tests/ui/attributes/may_dangle.rs
Normal file
@ -0,0 +1,53 @@
|
||||
#![feature(dropck_eyepatch)]
|
||||
|
||||
struct Implee1<'a, T, const N: usize>(&'a T);
|
||||
struct Implee2<'a, T, const N: usize>(&'a T);
|
||||
struct Implee3<'a, T, const N: usize>(&'a T);
|
||||
trait NotDrop {}
|
||||
|
||||
unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {}
|
||||
//~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
|
||||
unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {}
|
||||
//~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
|
||||
unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> {
|
||||
//~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
// Ok, lifetime param in a `Drop` impl.
|
||||
unsafe impl<#[may_dangle] 'a, T, const N: usize> Drop for Implee2<'a, T, N> {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
// Ok, type param in a `Drop` impl.
|
||||
unsafe impl<'a, #[may_dangle] T, const N: usize> Drop for Implee3<'a, T, N> {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
// Check that this check is not textual.
|
||||
mod fake {
|
||||
trait Drop {
|
||||
fn drop(&mut self);
|
||||
}
|
||||
struct Implee<T>(T);
|
||||
|
||||
unsafe impl<#[may_dangle] T> Drop for Implee<T> {
|
||||
//~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
}
|
||||
|
||||
#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
struct Dangling;
|
||||
|
||||
#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
impl NotDrop for () {
|
||||
}
|
||||
|
||||
#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
fn main() {
|
||||
#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
let () = ();
|
||||
}
|
50
tests/ui/attributes/may_dangle.stderr
Normal file
50
tests/ui/attributes/may_dangle.stderr
Normal file
@ -0,0 +1,50 @@
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:8:13
|
||||
|
|
||||
LL | unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:11:17
|
||||
|
|
||||
LL | unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:14:20
|
||||
|
|
||||
LL | unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:42:1
|
||||
|
|
||||
LL | #[may_dangle]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:45:1
|
||||
|
|
||||
LL | #[may_dangle]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:49:1
|
||||
|
|
||||
LL | #[may_dangle]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:51:5
|
||||
|
|
||||
LL | #[may_dangle]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
|
||||
--> $DIR/may_dangle.rs:36:17
|
||||
|
|
||||
LL | unsafe impl<#[may_dangle] T> Drop for Implee<T> {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
@ -8,4 +8,5 @@ fn main() {
|
||||
foo(|| bar())
|
||||
//~^ ERROR mismatched types [E0308]
|
||||
//~| HELP consider using a semicolon here
|
||||
//~| HELP try adding a return type
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/add_semicolon_non_block_closure.rs:8:12
|
||||
|
|
||||
LL | fn main() {
|
||||
| - expected `()` because of default return type
|
||||
LL | foo(|| bar())
|
||||
| ^^^^^ expected `()`, found `i32`
|
||||
|
|
||||
@ -10,6 +8,10 @@ help: consider using a semicolon here
|
||||
|
|
||||
LL | foo(|| { bar(); })
|
||||
| + +++
|
||||
help: try adding a return type
|
||||
|
|
||||
LL | foo(|| -> i32 bar())
|
||||
| ++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
error: `must_not_suspend` attribute should be applied to a struct, enum, or trait
|
||||
error: `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
|
||||
--> $DIR/other_items.rs:5:1
|
||||
|
|
||||
LL | #[must_not_suspend]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
LL | mod inner {}
|
||||
| ------------ is not a struct, enum, or trait
|
||||
| ------------ is not a struct, enum, union, or trait
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: `must_not_suspend` attribute should be applied to a struct, enum, or trait
|
||||
error: `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
|
||||
--> $DIR/return.rs:5:1
|
||||
|
|
||||
LL | #[must_not_suspend]
|
||||
@ -6,7 +6,7 @@ LL | #[must_not_suspend]
|
||||
LL | / fn foo() -> i32 {
|
||||
LL | | 0
|
||||
LL | | }
|
||||
| |_- is not a struct, enum, or trait
|
||||
| |_- is not a struct, enum, union, or trait
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -0,0 +1,91 @@
|
||||
//@ check-pass
|
||||
|
||||
//! Accept lifetime extensions with `Assume::LIFETIMES`.
|
||||
|
||||
#![feature(transmutability, core_intrinsics)]
|
||||
|
||||
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||
|
||||
unsafe fn transmute<Src, Dst>(src: Src) -> Dst
|
||||
where
|
||||
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY.and(Assume::LIFETIMES) }>,
|
||||
{
|
||||
core::intrinsics::transmute_unchecked(src)
|
||||
}
|
||||
|
||||
mod bare {
|
||||
use super::*;
|
||||
|
||||
fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod nested {
|
||||
use super::*;
|
||||
|
||||
fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod tuple {
|
||||
use super::*;
|
||||
|
||||
fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
|
||||
fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod r#struct {
|
||||
use super::*;
|
||||
|
||||
struct Struct<'a>(&'a u8);
|
||||
|
||||
fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod r#enum {
|
||||
use super::*;
|
||||
|
||||
enum Single<'a> {
|
||||
A(&'a u8),
|
||||
}
|
||||
|
||||
fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
|
||||
enum Multi<'a> {
|
||||
A(&'a u8),
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod hrtb {
|
||||
use super::*;
|
||||
|
||||
fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
|
||||
unsafe { extend_hrtb(src) }
|
||||
}
|
||||
|
||||
unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8
|
||||
where
|
||||
for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8, { Assume::LIFETIMES }>,
|
||||
{
|
||||
core::intrinsics::transmute_unchecked(src)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,68 @@
|
||||
//@ check-pass
|
||||
|
||||
//! Accept lifetime extensions of un-exercised lifetimes.
|
||||
|
||||
#![feature(transmutability, core_intrinsics)]
|
||||
|
||||
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||
|
||||
unsafe fn transmute<Src, Dst>(src: Src) -> Dst
|
||||
where
|
||||
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>,
|
||||
{
|
||||
core::intrinsics::transmute_unchecked(src)
|
||||
}
|
||||
|
||||
enum Void {}
|
||||
|
||||
mod phantom {
|
||||
use super::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
fn extend_bare<'a>(src: PhantomData<&'a u8>) -> PhantomData<&'static u8> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mod tuple {
|
||||
use super::*;
|
||||
|
||||
fn extend_pair<'a>(src: (&'a u8, Void)) -> (&'static u8, Void) {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod r#struct {
|
||||
use super::*;
|
||||
|
||||
struct Struct<'a>(&'a u8, Void);
|
||||
|
||||
fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
mod r#enum {
|
||||
use super::*;
|
||||
|
||||
enum Single<'a> {
|
||||
A(&'a u8, Void),
|
||||
}
|
||||
|
||||
fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
|
||||
enum Multi<'a> {
|
||||
A(&'a u8, Void),
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
|
||||
unsafe { transmute(src) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,91 @@
|
||||
//@ check-fail
|
||||
|
||||
//! Reject lifetime extensions.
|
||||
|
||||
#![feature(transmutability, core_intrinsics)]
|
||||
|
||||
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
||||
|
||||
unsafe fn transmute<Src, Dst>(src: Src) -> Dst
|
||||
where
|
||||
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>,
|
||||
{
|
||||
core::intrinsics::transmute_unchecked(src)
|
||||
}
|
||||
|
||||
mod bare {
|
||||
use super::*;
|
||||
|
||||
fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
mod nested {
|
||||
use super::*;
|
||||
|
||||
fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
mod tuple {
|
||||
use super::*;
|
||||
|
||||
fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
|
||||
fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
mod r#struct {
|
||||
use super::*;
|
||||
|
||||
struct Struct<'a>(&'a u8);
|
||||
|
||||
fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
mod r#enum {
|
||||
use super::*;
|
||||
|
||||
enum Single<'a> {
|
||||
A(&'a u8),
|
||||
}
|
||||
|
||||
fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
|
||||
enum Multi<'a> {
|
||||
A(&'a u8),
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
|
||||
unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
mod hrtb {
|
||||
use super::*;
|
||||
|
||||
fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
|
||||
unsafe { extend_hrtb(src) } //~ ERROR borrowed data escapes outside of function
|
||||
}
|
||||
|
||||
unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8
|
||||
where
|
||||
for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8>,
|
||||
{
|
||||
core::intrinsics::transmute_unchecked(src)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,78 @@
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:20:18
|
||||
|
|
||||
LL | fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:28:18
|
||||
|
|
||||
LL | fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:36:18
|
||||
|
|
||||
LL | fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:40:18
|
||||
|
|
||||
LL | fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:50:18
|
||||
|
|
||||
LL | fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:62:18
|
||||
|
|
||||
LL | fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> $DIR/reject_lifetime_extension.rs:72:18
|
||||
|
|
||||
LL | fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
|
||||
| -- lifetime `'a` defined here
|
||||
LL | unsafe { transmute(src) }
|
||||
| ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
||||
|
||||
error[E0521]: borrowed data escapes outside of function
|
||||
--> $DIR/reject_lifetime_extension.rs:80:18
|
||||
|
|
||||
LL | fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
|
||||
| -- --- `src` is a reference that is only valid in the function body
|
||||
| |
|
||||
| lifetime `'a` defined here
|
||||
LL | unsafe { extend_hrtb(src) }
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| `src` escapes the function body here
|
||||
| argument requires that `'a` must outlive `'static`
|
||||
|
|
||||
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
|
||||
--> $DIR/reject_lifetime_extension.rs:85:25
|
||||
|
|
||||
LL | for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0521`.
|
Loading…
Reference in New Issue
Block a user