mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Merge pull request #18431 from lnicola/sync-from-rust
minor: Sync from downstream
This commit is contained in:
commit
eae9d7ad8d
@ -4029,6 +4029,7 @@ dependencies = [
|
||||
"rustc_hir",
|
||||
"rustc_hir_pretty",
|
||||
"rustc_index",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
"rustc_query_system",
|
||||
"rustc_serialize",
|
||||
@ -4495,6 +4496,7 @@ name = "rustc_transmute"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast_ir",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
@ -4502,7 +4504,6 @@ dependencies = [
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -68,15 +68,15 @@ Stabilized APIs
|
||||
- [`impl Default for std::collections::vec_deque::Iter`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.Iter.html#impl-Default-for-Iter%3C'_,+T%3E)
|
||||
- [`impl Default for std::collections::vec_deque::IterMut`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.IterMut.html#impl-Default-for-IterMut%3C'_,+T%3E)
|
||||
- [`Rc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit)
|
||||
- [`Rc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init)
|
||||
- [`Rc<MaybeUninit<T>>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init)
|
||||
- [`Rc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit_slice)
|
||||
- [`Rc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init-1)
|
||||
- [`Arc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit)
|
||||
- [`Arc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init)
|
||||
- [`Arc<MaybeUninit<T>>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init)
|
||||
- [`Arc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit_slice)
|
||||
- [`Arc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init-1)
|
||||
- [`Box<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit)
|
||||
- [`Box<T>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init)
|
||||
- [`Box<MaybeUninit<T>>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init)
|
||||
- [`Box<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit_slice)
|
||||
- [`Box<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init-1)
|
||||
- [`core::arch::x86_64::_bextri_u64`](https://doc.rust-lang.org/stable/core/arch/x86_64/fn._bextri_u64.html)
|
||||
|
@ -7,7 +7,7 @@ use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
Abi, AbiAndPrefAlign, Align, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
|
||||
LayoutS, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding,
|
||||
LayoutData, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding,
|
||||
Variants, WrappingRange,
|
||||
};
|
||||
|
||||
@ -26,7 +26,7 @@ fn absent<'a, FieldIdx, VariantIdx, F>(fields: &IndexSlice<FieldIdx, F>) -> bool
|
||||
where
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
|
||||
{
|
||||
let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
|
||||
// We cannot ignore alignment; that might lead us to entirely discard a variant and
|
||||
@ -89,7 +89,7 @@ impl<F> LayoutCalculatorError<F> {
|
||||
}
|
||||
|
||||
type LayoutCalculatorResult<FieldIdx, VariantIdx, F> =
|
||||
Result<LayoutS<FieldIdx, VariantIdx>, LayoutCalculatorError<F>>;
|
||||
Result<LayoutData<FieldIdx, VariantIdx>, LayoutCalculatorError<F>>;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LayoutCalculator<Cx> {
|
||||
@ -105,7 +105,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
&self,
|
||||
a: Scalar,
|
||||
b: Scalar,
|
||||
) -> LayoutS<FieldIdx, VariantIdx> {
|
||||
) -> LayoutData<FieldIdx, VariantIdx> {
|
||||
let dl = self.cx.data_layout();
|
||||
let b_align = b.align(dl);
|
||||
let align = a.align(dl).max(b_align).max(dl.aggregate_align);
|
||||
@ -119,7 +119,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
.chain(Niche::from_scalar(dl, Size::ZERO, a))
|
||||
.max_by_key(|niche| niche.available(dl));
|
||||
|
||||
LayoutS {
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: [Size::ZERO, b_offset].into(),
|
||||
@ -138,7 +138,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
>(
|
||||
&self,
|
||||
fields: &IndexSlice<FieldIdx, F>,
|
||||
@ -211,9 +211,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
|
||||
pub fn layout_of_never_type<FieldIdx: Idx, VariantIdx: Idx>(
|
||||
&self,
|
||||
) -> LayoutS<FieldIdx, VariantIdx> {
|
||||
) -> LayoutData<FieldIdx, VariantIdx> {
|
||||
let dl = self.cx.data_layout();
|
||||
LayoutS {
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Primitive,
|
||||
abi: Abi::Uninhabited,
|
||||
@ -229,7 +229,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
>(
|
||||
&self,
|
||||
repr: &ReprOptions,
|
||||
@ -292,7 +292,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
>(
|
||||
&self,
|
||||
repr: &ReprOptions,
|
||||
@ -384,7 +384,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
return Err(LayoutCalculatorError::EmptyUnion);
|
||||
};
|
||||
|
||||
Ok(LayoutS {
|
||||
Ok(LayoutData {
|
||||
variants: Variants::Single { index: only_variant_idx },
|
||||
fields: FieldsShape::Union(union_field_count),
|
||||
abi,
|
||||
@ -401,7 +401,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
>(
|
||||
&self,
|
||||
repr: &ReprOptions,
|
||||
@ -501,7 +501,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
>(
|
||||
&self,
|
||||
repr: &ReprOptions,
|
||||
@ -516,8 +516,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
// overall LayoutS. Store the overall LayoutS
|
||||
// and the variant LayoutSs here until then.
|
||||
struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
layout: LayoutS<FieldIdx, VariantIdx>,
|
||||
variants: IndexVec<VariantIdx, LayoutS<FieldIdx, VariantIdx>>,
|
||||
layout: LayoutData<FieldIdx, VariantIdx>,
|
||||
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
|
||||
}
|
||||
|
||||
let dl = self.cx.data_layout();
|
||||
@ -649,7 +649,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
Abi::Aggregate { sized: true }
|
||||
};
|
||||
|
||||
let layout = LayoutS {
|
||||
let layout = LayoutData {
|
||||
variants: Variants::Multiple {
|
||||
tag: niche_scalar,
|
||||
tag_encoding: TagEncoding::Niche {
|
||||
@ -958,7 +958,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
|
||||
let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
|
||||
|
||||
let tagged_layout = LayoutS {
|
||||
let tagged_layout = LayoutData {
|
||||
variants: Variants::Multiple {
|
||||
tag,
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
@ -1013,7 +1013,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
>(
|
||||
&self,
|
||||
fields: &IndexSlice<FieldIdx, F>,
|
||||
@ -1341,7 +1341,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
unadjusted_abi_align
|
||||
};
|
||||
|
||||
Ok(LayoutS {
|
||||
Ok(LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Arbitrary { offsets, memory_index },
|
||||
abi,
|
||||
@ -1357,10 +1357,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
|
||||
>(
|
||||
&self,
|
||||
layout: &LayoutS<FieldIdx, VariantIdx>,
|
||||
layout: &LayoutData<FieldIdx, VariantIdx>,
|
||||
fields: &IndexSlice<FieldIdx, F>,
|
||||
) -> String {
|
||||
let dl = self.cx.data_layout();
|
||||
|
@ -58,7 +58,7 @@ rustc_index::newtype_index! {
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
|
||||
#[rustc_pass_by_value]
|
||||
pub struct Layout<'a>(pub Interned<'a, LayoutS<FieldIdx, VariantIdx>>);
|
||||
pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);
|
||||
|
||||
impl<'a> fmt::Debug for Layout<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -68,8 +68,8 @@ impl<'a> fmt::Debug for Layout<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Deref for Layout<'a> {
|
||||
type Target = &'a LayoutS<FieldIdx, VariantIdx>;
|
||||
fn deref(&self) -> &&'a LayoutS<FieldIdx, VariantIdx> {
|
||||
type Target = &'a LayoutData<FieldIdx, VariantIdx>;
|
||||
fn deref(&self) -> &&'a LayoutData<FieldIdx, VariantIdx> {
|
||||
&self.0.0
|
||||
}
|
||||
}
|
||||
@ -142,8 +142,8 @@ impl<'a, Ty: fmt::Display> fmt::Debug for TyAndLayout<'a, Ty> {
|
||||
}
|
||||
|
||||
impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
|
||||
type Target = &'a LayoutS<FieldIdx, VariantIdx>;
|
||||
fn deref(&self) -> &&'a LayoutS<FieldIdx, VariantIdx> {
|
||||
type Target = &'a LayoutData<FieldIdx, VariantIdx>;
|
||||
fn deref(&self) -> &&'a LayoutData<FieldIdx, VariantIdx> {
|
||||
&self.layout.0.0
|
||||
}
|
||||
}
|
||||
|
@ -1485,7 +1485,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
tag: Scalar,
|
||||
tag_encoding: TagEncoding<VariantIdx>,
|
||||
tag_field: usize,
|
||||
variants: IndexVec<VariantIdx, LayoutS<FieldIdx, VariantIdx>>,
|
||||
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -1603,7 +1603,7 @@ impl Niche {
|
||||
// NOTE: This struct is generic over the FieldIdx and VariantIdx for rust-analyzer usage.
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub struct LayoutS<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
/// Says where the fields are located within the layout.
|
||||
pub fields: FieldsShape<FieldIdx>,
|
||||
|
||||
@ -1643,7 +1643,7 @@ pub struct LayoutS<FieldIdx: Idx, VariantIdx: Idx> {
|
||||
pub unadjusted_abi_align: Align,
|
||||
}
|
||||
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> {
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
||||
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
|
||||
pub fn is_aggregate(&self) -> bool {
|
||||
match self.abi {
|
||||
@ -1656,7 +1656,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> {
|
||||
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
|
||||
let size = scalar.size(cx);
|
||||
let align = scalar.align(cx);
|
||||
LayoutS {
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Primitive,
|
||||
abi: Abi::Scalar(scalar),
|
||||
@ -1669,7 +1669,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutS<FieldIdx, VariantIdx>
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutData<FieldIdx, VariantIdx>
|
||||
where
|
||||
FieldsShape<FieldIdx>: fmt::Debug,
|
||||
Variants<FieldIdx, VariantIdx>: fmt::Debug,
|
||||
@ -1678,7 +1678,7 @@ where
|
||||
// This is how `Layout` used to print before it become
|
||||
// `Interned<LayoutS>`. We print it like this to avoid having to update
|
||||
// expected output in a lot of tests.
|
||||
let LayoutS {
|
||||
let LayoutData {
|
||||
size,
|
||||
align,
|
||||
abi,
|
||||
@ -1723,7 +1723,7 @@ pub struct PointeeInfo {
|
||||
pub safe: Option<PointerKind>,
|
||||
}
|
||||
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutS<FieldIdx, VariantIdx> {
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
||||
/// Returns `true` if the layout corresponds to an unsized type.
|
||||
#[inline]
|
||||
pub fn is_unsized(&self) -> bool {
|
||||
|
@ -2697,7 +2697,7 @@ impl fmt::Debug for ImplPolarity {
|
||||
}
|
||||
|
||||
/// The polarity of a trait bound.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub enum BoundPolarity {
|
||||
/// `Type: Trait`
|
||||
@ -2719,7 +2719,7 @@ impl BoundPolarity {
|
||||
}
|
||||
|
||||
/// The constness of a trait bound.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub enum BoundConstness {
|
||||
/// `Type: Trait`
|
||||
|
@ -223,7 +223,7 @@ impl AttrItem {
|
||||
self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
|
||||
}
|
||||
|
||||
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
|
||||
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
|
||||
match &self.args {
|
||||
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
|
||||
MetaItemKind::list_from_tokens(args.tokens.clone())
|
||||
|
@ -135,7 +135,7 @@ pub trait Visitor<'ast>: Sized {
|
||||
/// or `ControlFlow<T>`.
|
||||
type Result: VisitorResult = ();
|
||||
|
||||
fn visit_ident(&mut self, _ident: Ident) -> Self::Result {
|
||||
fn visit_ident(&mut self, _ident: &'ast Ident) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_foreign_item(&mut self, i: &'ast ForeignItem) -> Self::Result {
|
||||
@ -173,9 +173,6 @@ pub trait Visitor<'ast>: Sized {
|
||||
fn visit_method_receiver_expr(&mut self, ex: &'ast Expr) -> Self::Result {
|
||||
self.visit_expr(ex)
|
||||
}
|
||||
fn visit_expr_post(&mut self, _ex: &'ast Expr) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_ty(&mut self, t: &'ast Ty) -> Self::Result {
|
||||
walk_ty(self, t)
|
||||
}
|
||||
@ -317,12 +314,12 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::R
|
||||
}
|
||||
|
||||
pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, Label { ident }: &'a Label) -> V::Result {
|
||||
visitor.visit_ident(*ident)
|
||||
visitor.visit_ident(ident)
|
||||
}
|
||||
|
||||
pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) -> V::Result {
|
||||
let Lifetime { id: _, ident } = lifetime;
|
||||
visitor.visit_ident(*ident)
|
||||
visitor.visit_ident(ident)
|
||||
}
|
||||
|
||||
pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef) -> V::Result
|
||||
@ -429,7 +426,7 @@ impl WalkItemKind for ItemKind {
|
||||
}) => {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
visit_opt!(visitor, visit_ident, *rename);
|
||||
visit_opt!(visitor, visit_ident, rename);
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
@ -437,9 +434,9 @@ impl WalkItemKind for ItemKind {
|
||||
try_visit!(visitor.visit_path(prefix, *id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
visitor.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
visitor.visit_ident(rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -472,7 +469,7 @@ where
|
||||
let Variant { attrs, id: _, span: _, vis, ident, data, disr_expr, is_placeholder: _ } = variant;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_vis(vis));
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_variant_data(data));
|
||||
visit_opt!(visitor, visit_variant_discr, disr_expr);
|
||||
V::Result::output()
|
||||
@ -481,7 +478,7 @@ where
|
||||
pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) -> V::Result {
|
||||
let ExprField { attrs, id: _, span: _, ident, expr, is_shorthand: _, is_placeholder: _ } = f;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_expr(expr));
|
||||
V::Result::output()
|
||||
}
|
||||
@ -489,7 +486,7 @@ pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) ->
|
||||
pub fn walk_pat_field<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a PatField) -> V::Result {
|
||||
let PatField { ident, pat, is_shorthand: _, attrs, id: _, span: _, is_placeholder: _ } = fp;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_pat(pat));
|
||||
V::Result::output()
|
||||
}
|
||||
@ -564,7 +561,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>(
|
||||
match kind {
|
||||
UseTreeKind::Simple(rename) => {
|
||||
// The extra IDs are handled during AST lowering.
|
||||
visit_opt!(visitor, visit_ident, *rename);
|
||||
visit_opt!(visitor, visit_ident, rename);
|
||||
}
|
||||
UseTreeKind::Glob => {}
|
||||
UseTreeKind::Nested { ref items, span: _ } => {
|
||||
@ -581,7 +578,7 @@ pub fn walk_path_segment<'a, V: Visitor<'a>>(
|
||||
segment: &'a PathSegment,
|
||||
) -> V::Result {
|
||||
let PathSegment { ident, id: _, args } = segment;
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
visit_opt!(visitor, visit_generic_args, args);
|
||||
V::Result::output()
|
||||
}
|
||||
@ -627,7 +624,7 @@ pub fn walk_assoc_item_constraint<'a, V: Visitor<'a>>(
|
||||
constraint: &'a AssocItemConstraint,
|
||||
) -> V::Result {
|
||||
let AssocItemConstraint { id: _, ident, gen_args, kind, span: _ } = constraint;
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
visit_opt!(visitor, visit_generic_args, gen_args);
|
||||
match kind {
|
||||
AssocItemConstraintKind::Equality { term } => match term {
|
||||
@ -665,7 +662,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
|
||||
try_visit!(visitor.visit_pat(subpattern));
|
||||
}
|
||||
PatKind::Ident(_bmode, ident, optional_subpattern) => {
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
visit_opt!(visitor, visit_pat, optional_subpattern);
|
||||
}
|
||||
PatKind::Lit(expression) => try_visit!(visitor.visit_expr(expression)),
|
||||
@ -751,7 +748,7 @@ pub fn walk_generic_param<'a, V: Visitor<'a>>(
|
||||
let GenericParam { id: _, ident, attrs, bounds, is_placeholder: _, kind, colon_span: _ } =
|
||||
param;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
|
||||
match kind {
|
||||
GenericParamKind::Lifetime => (),
|
||||
@ -889,7 +886,7 @@ impl WalkItemKind for AssocItemKind {
|
||||
}) => {
|
||||
try_visit!(walk_qself(visitor, qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
visit_opt!(visitor, visit_ident, *rename);
|
||||
visit_opt!(visitor, visit_ident, rename);
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
@ -897,9 +894,9 @@ impl WalkItemKind for AssocItemKind {
|
||||
try_visit!(visitor.visit_path(prefix, id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
visitor.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
visitor.visit_ident(rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -915,7 +912,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(
|
||||
item: &'a Item<impl WalkItemKind>,
|
||||
ctxt: AssocCtxt,
|
||||
) -> V::Result {
|
||||
let &Item { id: _, span: _, ident, ref vis, ref attrs, ref kind, tokens: _ } = item;
|
||||
let Item { id: _, span: _, ident, vis, attrs, kind, tokens: _ } = item;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_vis(vis));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
@ -935,7 +932,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef)
|
||||
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _ } = field;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_vis(vis));
|
||||
visit_opt!(visitor, visit_ident, *ident);
|
||||
visit_opt!(visitor, visit_ident, ident);
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
V::Result::output()
|
||||
}
|
||||
@ -1017,7 +1014,7 @@ pub fn walk_format_args<'a, V: Visitor<'a>>(visitor: &mut V, fmt: &'a FormatArgs
|
||||
for FormatArgument { kind, expr } in arguments.all_args() {
|
||||
match kind {
|
||||
FormatArgumentKind::Named(ident) | FormatArgumentKind::Captured(ident) => {
|
||||
try_visit!(visitor.visit_ident(*ident))
|
||||
try_visit!(visitor.visit_ident(ident))
|
||||
}
|
||||
FormatArgumentKind::Normal => {}
|
||||
}
|
||||
@ -1137,7 +1134,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||
}
|
||||
ExprKind::Field(subexpression, ident) => {
|
||||
try_visit!(visitor.visit_expr(subexpression));
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
}
|
||||
ExprKind::Index(main_expression, index_expression, _span) => {
|
||||
try_visit!(visitor.visit_expr(main_expression));
|
||||
@ -1172,7 +1169,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||
ExprKind::FormatArgs(f) => try_visit!(visitor.visit_format_args(f)),
|
||||
ExprKind::OffsetOf(container, fields) => {
|
||||
try_visit!(visitor.visit_ty(container));
|
||||
walk_list!(visitor, visit_ident, fields.iter().copied());
|
||||
walk_list!(visitor, visit_ident, fields.iter());
|
||||
}
|
||||
ExprKind::Yield(optional_expression) => {
|
||||
visit_opt!(visitor, visit_expr, optional_expression);
|
||||
@ -1185,7 +1182,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||
ExprKind::Dummy => {}
|
||||
}
|
||||
|
||||
visitor.visit_expr_post(expression)
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) -> V::Result {
|
||||
|
@ -49,7 +49,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
| asm::InlineAsmArch::RiscV64
|
||||
| asm::InlineAsmArch::LoongArch64
|
||||
);
|
||||
if !is_stable && !self.tcx.features().asm_experimental_arch {
|
||||
if !is_stable && !self.tcx.features().asm_experimental_arch() {
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::asm_experimental_arch,
|
||||
@ -65,7 +65,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
{
|
||||
self.dcx().emit_err(AttSyntaxOnlyX86 { span: sp });
|
||||
}
|
||||
if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind {
|
||||
if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind() {
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::asm_unwind,
|
||||
@ -237,7 +237,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::Label { block } => {
|
||||
if !self.tcx.features().asm_goto {
|
||||
if !self.tcx.features().asm_goto() {
|
||||
feature_err(
|
||||
sess,
|
||||
sym::asm_goto,
|
||||
|
@ -575,7 +575,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
} else {
|
||||
// Either `body.is_none()` or `is_never_pattern` here.
|
||||
if !is_never_pattern {
|
||||
if self.tcx.features().never_patterns {
|
||||
if self.tcx.features().never_patterns() {
|
||||
// If the feature is off we already emitted the error after parsing.
|
||||
let suggestion = span.shrink_to_hi();
|
||||
self.dcx().emit_err(MatchArmWithNoBody { span, suggestion });
|
||||
@ -717,7 +717,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
outer_hir_id: HirId,
|
||||
inner_hir_id: HirId,
|
||||
) {
|
||||
if self.tcx.features().async_fn_track_caller
|
||||
if self.tcx.features().async_fn_track_caller()
|
||||
&& let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
|
||||
&& attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
|
||||
{
|
||||
@ -1572,7 +1572,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
);
|
||||
}
|
||||
Some(hir::CoroutineKind::Coroutine(_)) => {
|
||||
if !self.tcx.features().coroutines {
|
||||
if !self.tcx.features().coroutines() {
|
||||
rustc_session::parse::feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::coroutines,
|
||||
@ -1584,7 +1584,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
false
|
||||
}
|
||||
None => {
|
||||
if !self.tcx.features().coroutines {
|
||||
if !self.tcx.features().coroutines() {
|
||||
rustc_session::parse::feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::coroutines,
|
||||
|
@ -57,7 +57,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
||||
owner: NodeId,
|
||||
f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
|
||||
) {
|
||||
let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index);
|
||||
let mut lctx = LoweringContext::new(self.tcx, self.resolver);
|
||||
lctx.with_hir_id_owner(owner, |lctx| f(lctx));
|
||||
|
||||
for (def_id, info) in lctx.children {
|
||||
@ -193,8 +193,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
|
||||
let (generics, (ty, body_id)) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -225,15 +223,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
);
|
||||
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, decl) =
|
||||
this.lower_generics(generics, header.constness, false, id, itctx, |this| {
|
||||
this.lower_fn_decl(
|
||||
decl,
|
||||
id,
|
||||
*fn_sig_span,
|
||||
FnDeclKind::Fn,
|
||||
coroutine_kind,
|
||||
)
|
||||
let (generics, decl) = this.lower_generics(generics, id, itctx, |this| {
|
||||
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coroutine_kind)
|
||||
});
|
||||
let sig = hir::FnSig {
|
||||
decl,
|
||||
@ -269,8 +260,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
|
||||
let (generics, ty) = self.lower_generics(
|
||||
&generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| match ty {
|
||||
@ -294,8 +283,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ItemKind::Enum(enum_definition, generics) => {
|
||||
let (generics, variants) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -309,8 +296,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ItemKind::Struct(struct_def, generics) => {
|
||||
let (generics, struct_def) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| this.lower_variant_data(hir_id, struct_def),
|
||||
@ -320,8 +305,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ItemKind::Union(vdata, generics) => {
|
||||
let (generics, vdata) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| this.lower_variant_data(hir_id, vdata),
|
||||
@ -353,7 +336,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
// parent lifetime.
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, (trait_ref, lowered_ty)) =
|
||||
self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| {
|
||||
self.lower_generics(ast_generics, id, itctx, |this| {
|
||||
let modifiers = TraitBoundModifiers {
|
||||
constness: BoundConstness::Never,
|
||||
asyncness: BoundAsyncness::Normal,
|
||||
@ -405,8 +388,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
|
||||
let (generics, (safety, items, bounds)) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -426,8 +407,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ItemKind::TraitAlias(generics, bounds) => {
|
||||
let (generics, bounds) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -604,50 +583,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
ctxt: AssocCtxt,
|
||||
parent_hir: &'hir hir::OwnerInfo<'hir>,
|
||||
) -> hir::OwnerNode<'hir> {
|
||||
// Evaluate with the lifetimes in `params` in-scope.
|
||||
// This is used to track which lifetimes have already been defined,
|
||||
// and which need to be replicated when lowering an async fn.
|
||||
|
||||
let parent_item = parent_hir.node().expect_item();
|
||||
let constness = match parent_item.kind {
|
||||
match parent_item.kind {
|
||||
hir::ItemKind::Impl(impl_) => {
|
||||
self.is_in_trait_impl = impl_.of_trait.is_some();
|
||||
// N.B. the impl should always lower to methods that have `const host: bool` params if the trait
|
||||
// is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from
|
||||
// calling non-const impls are done through associated types.
|
||||
if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) {
|
||||
if let Some(local_def) = def_id.as_local() {
|
||||
match &self.ast_index[local_def] {
|
||||
AstOwner::Item(ast::Item { attrs, .. }) => attrs
|
||||
.iter()
|
||||
.find(|attr| attr.has_name(sym::const_trait))
|
||||
.map_or(Const::No, |attr| Const::Yes(attr.span)),
|
||||
_ => Const::No,
|
||||
}
|
||||
} else if self.tcx.is_const_trait(def_id) {
|
||||
// FIXME(effects) span
|
||||
Const::Yes(self.tcx.def_ident_span(def_id).unwrap())
|
||||
} else {
|
||||
Const::No
|
||||
}
|
||||
} else {
|
||||
Const::No
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Trait(_, _, _, _, _) => parent_hir
|
||||
.attrs
|
||||
.get(parent_item.hir_id().local_id)
|
||||
.iter()
|
||||
.find(|attr| attr.has_name(sym::const_trait))
|
||||
.map_or(Const::No, |attr| Const::Yes(attr.span)),
|
||||
hir::ItemKind::Trait(_, _, _, _, _) => {}
|
||||
kind => {
|
||||
span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Evaluate with the lifetimes in `params` in-scope.
|
||||
// This is used to track which lifetimes have already been defined,
|
||||
// and which need to be replicated when lowering an async fn.
|
||||
match ctxt {
|
||||
AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)),
|
||||
AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)),
|
||||
AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)),
|
||||
AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,7 +615,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let fdec = &sig.decl;
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, (decl, fn_args)) =
|
||||
self.lower_generics(generics, Const::No, false, i.id, itctx, |this| {
|
||||
self.lower_generics(generics, i.id, itctx, |this| {
|
||||
(
|
||||
// Disallow `impl Trait` in foreign items.
|
||||
this.lower_fn_decl(
|
||||
@ -775,11 +727,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_trait_item(
|
||||
&mut self,
|
||||
i: &AssocItem,
|
||||
trait_constness: Const,
|
||||
) -> &'hir hir::TraitItem<'hir> {
|
||||
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
|
||||
let hir_id = self.lower_node_id(i.id);
|
||||
self.lower_attrs(hir_id, &i.attrs);
|
||||
let trait_item_def_id = hir_id.expect_owner();
|
||||
@ -788,8 +736,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
|
||||
let (generics, kind) = self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
i.id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -810,7 +756,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
i.id,
|
||||
FnDeclKind::Trait,
|
||||
sig.header.coroutine_kind,
|
||||
trait_constness,
|
||||
);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
|
||||
}
|
||||
@ -829,7 +774,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
i.id,
|
||||
FnDeclKind::Trait,
|
||||
sig.header.coroutine_kind,
|
||||
trait_constness,
|
||||
);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
|
||||
}
|
||||
@ -838,8 +782,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
|
||||
let (generics, kind) = self.lower_generics(
|
||||
&generics,
|
||||
Const::No,
|
||||
false,
|
||||
i.id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -912,11 +854,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
self.expr(span, hir::ExprKind::Err(guar))
|
||||
}
|
||||
|
||||
fn lower_impl_item(
|
||||
&mut self,
|
||||
i: &AssocItem,
|
||||
constness_of_trait: Const,
|
||||
) -> &'hir hir::ImplItem<'hir> {
|
||||
fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
|
||||
// Since `default impl` is not yet implemented, this is always true in impls.
|
||||
let has_value = true;
|
||||
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
|
||||
@ -926,8 +864,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let (generics, kind) = match &i.kind {
|
||||
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
|
||||
generics,
|
||||
Const::No,
|
||||
false,
|
||||
i.id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| {
|
||||
@ -953,7 +889,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
i.id,
|
||||
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
|
||||
sig.header.coroutine_kind,
|
||||
constness_of_trait,
|
||||
);
|
||||
|
||||
(generics, hir::ImplItemKind::Fn(sig, body_id))
|
||||
@ -963,8 +898,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
|
||||
self.lower_generics(
|
||||
&generics,
|
||||
Const::No,
|
||||
false,
|
||||
i.id,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| match ty {
|
||||
@ -1370,16 +1303,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
id: NodeId,
|
||||
kind: FnDeclKind,
|
||||
coroutine_kind: Option<CoroutineKind>,
|
||||
parent_constness: Const,
|
||||
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
|
||||
let header = self.lower_fn_header(sig.header, hir::Safety::Safe);
|
||||
// Don't pass along the user-provided constness of trait associated functions; we don't want to
|
||||
// synthesize a host effect param for them. We reject `const` on them during AST validation.
|
||||
let constness =
|
||||
if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness };
|
||||
let itctx = ImplTraitContext::Universal;
|
||||
let (generics, decl) =
|
||||
self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| {
|
||||
let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
|
||||
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
|
||||
});
|
||||
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
|
||||
@ -1460,8 +1387,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
fn lower_generics<T>(
|
||||
&mut self,
|
||||
generics: &Generics,
|
||||
constness: Const,
|
||||
force_append_constness: bool,
|
||||
parent_node_id: NodeId,
|
||||
itctx: ImplTraitContext,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
@ -1512,7 +1437,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
continue;
|
||||
}
|
||||
let is_param = *is_param.get_or_insert_with(compute_is_param);
|
||||
if !is_param && !self.tcx.features().more_maybe_bounds {
|
||||
if !is_param && !self.tcx.features().more_maybe_bounds() {
|
||||
self.tcx
|
||||
.sess
|
||||
.create_feature_err(
|
||||
@ -1524,30 +1449,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
// Desugar `~const` bound in generics into an additional `const host: bool` param
|
||||
// if the effects feature is enabled. This needs to be done before we lower where
|
||||
// clauses since where clauses need to bind to the DefId of the host param
|
||||
let host_param_parts = if let Const::Yes(span) = constness
|
||||
// if this comes from implementing a `const` trait, we must force constness to be appended
|
||||
// to the impl item, no matter whether effects is enabled.
|
||||
&& (self.tcx.features().effects || force_append_constness)
|
||||
{
|
||||
let span = self.lower_span(span);
|
||||
let param_node_id = self.next_node_id();
|
||||
let hir_id = self.next_id();
|
||||
let def_id = self.create_def(
|
||||
self.local_def_id(parent_node_id),
|
||||
param_node_id,
|
||||
sym::host,
|
||||
DefKind::ConstParam,
|
||||
span,
|
||||
);
|
||||
self.host_param_id = Some(def_id);
|
||||
Some((span, hir_id, def_id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new();
|
||||
predicates.extend(generics.params.iter().filter_map(|param| {
|
||||
self.lower_generic_bound_predicate(
|
||||
@ -1595,74 +1496,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
|
||||
predicates.extend(impl_trait_bounds.into_iter());
|
||||
|
||||
if let Some((span, hir_id, def_id)) = host_param_parts {
|
||||
let const_node_id = self.next_node_id();
|
||||
let anon_const_did =
|
||||
self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span);
|
||||
|
||||
let const_id = self.next_id();
|
||||
let const_expr_id = self.next_id();
|
||||
let bool_id = self.next_id();
|
||||
|
||||
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
|
||||
self.children.push((anon_const_did, hir::MaybeOwner::NonOwner(const_id)));
|
||||
|
||||
let const_body = self.lower_body(|this| {
|
||||
(&[], hir::Expr {
|
||||
hir_id: const_expr_id,
|
||||
kind: hir::ExprKind::Lit(
|
||||
this.arena.alloc(hir::Lit { node: LitKind::Bool(true), span }),
|
||||
),
|
||||
span,
|
||||
})
|
||||
});
|
||||
|
||||
let default_ac = self.arena.alloc(hir::AnonConst {
|
||||
def_id: anon_const_did,
|
||||
hir_id: const_id,
|
||||
body: const_body,
|
||||
span,
|
||||
});
|
||||
let default_ct = self.arena.alloc(hir::ConstArg {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ConstArgKind::Anon(default_ac),
|
||||
is_desugared_from_effects: false,
|
||||
});
|
||||
let param = hir::GenericParam {
|
||||
def_id,
|
||||
hir_id,
|
||||
name: hir::ParamName::Plain(Ident { name: sym::host, span }),
|
||||
span,
|
||||
kind: hir::GenericParamKind::Const {
|
||||
ty: self.arena.alloc(self.ty(
|
||||
span,
|
||||
hir::TyKind::Path(hir::QPath::Resolved(
|
||||
None,
|
||||
self.arena.alloc(hir::Path {
|
||||
res: Res::PrimTy(hir::PrimTy::Bool),
|
||||
span,
|
||||
segments: self.arena.alloc_from_iter([hir::PathSegment {
|
||||
ident: Ident { name: sym::bool, span },
|
||||
hir_id: bool_id,
|
||||
res: Res::PrimTy(hir::PrimTy::Bool),
|
||||
args: None,
|
||||
infer_args: false,
|
||||
}]),
|
||||
}),
|
||||
)),
|
||||
)),
|
||||
default: Some(default_ct),
|
||||
is_host_effect: true,
|
||||
synthetic: true,
|
||||
},
|
||||
colon_span: None,
|
||||
pure_wrt_drop: false,
|
||||
source: hir::GenericParamSource::Generics,
|
||||
};
|
||||
|
||||
params.push(param);
|
||||
}
|
||||
|
||||
let lowered_generics = self.arena.alloc(hir::Generics {
|
||||
params: self.arena.alloc_from_iter(params),
|
||||
predicates: self.arena.alloc_from_iter(predicates),
|
||||
|
@ -154,17 +154,10 @@ struct LoweringContext<'a, 'hir> {
|
||||
/// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this
|
||||
/// field from the original parameter 'a to the new parameter 'a1.
|
||||
generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
|
||||
|
||||
host_param_id: Option<LocalDefId>,
|
||||
ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'hir>,
|
||||
resolver: &'a mut ResolverAstLowering,
|
||||
ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
|
||||
) -> Self {
|
||||
fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
|
||||
Self {
|
||||
// Pseudo-globals.
|
||||
tcx,
|
||||
@ -193,7 +186,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
impl_trait_defs: Vec::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
|
||||
allow_gen_future: if tcx.features().async_fn_track_caller {
|
||||
allow_gen_future: if tcx.features().async_fn_track_caller() {
|
||||
[sym::gen_future, sym::closure_track_caller].into()
|
||||
} else {
|
||||
[sym::gen_future].into()
|
||||
@ -204,8 +197,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// interact with `gen`/`async gen` blocks
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
generics_def_id_map: Default::default(),
|
||||
host_param_id: None,
|
||||
ast_index,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1035,7 +1026,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
span: data.inputs_span,
|
||||
})
|
||||
};
|
||||
if !self.tcx.features().return_type_notation
|
||||
if !self.tcx.features().return_type_notation()
|
||||
&& self.tcx.sess.is_nightly_build()
|
||||
{
|
||||
add_feature_diagnostics(
|
||||
@ -1160,7 +1151,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)),
|
||||
ast::GenericArg::Type(ty) => {
|
||||
match &ty.kind {
|
||||
TyKind::Infer if self.tcx.features().generic_arg_infer => {
|
||||
TyKind::Infer if self.tcx.features().generic_arg_infer() => {
|
||||
return GenericArg::Infer(hir::InferArg {
|
||||
hir_id: self.lower_node_id(ty.id),
|
||||
span: self.lower_span(ty.span),
|
||||
@ -1500,7 +1491,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// is enabled. We don't check the span of the edition, since this is done
|
||||
// on a per-opaque basis to account for nested opaques.
|
||||
let always_capture_in_scope = match origin {
|
||||
_ if self.tcx.features().lifetime_capture_rules_2024 => true,
|
||||
_ if self.tcx.features().lifetime_capture_rules_2024() => true,
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => true,
|
||||
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(),
|
||||
hir::OpaqueTyOrigin::AsyncFn { .. } => {
|
||||
@ -1519,7 +1510,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// Feature gate for RPITIT + use<..>
|
||||
match origin {
|
||||
rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
|
||||
if !self.tcx.features().precise_capturing_in_traits
|
||||
if !self.tcx.features().precise_capturing_in_traits()
|
||||
&& let Some(span) = bounds.iter().find_map(|bound| match *bound {
|
||||
ast::GenericBound::Use(_, span) => Some(span),
|
||||
_ => None,
|
||||
@ -1956,7 +1947,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
hir::GenericBound::Trait(hir::PolyTraitRef {
|
||||
bound_generic_params: &[],
|
||||
modifiers: hir::TraitBoundModifier::None,
|
||||
modifiers: hir::TraitBoundModifiers::NONE,
|
||||
trait_ref: hir::TraitRef {
|
||||
path: self.make_lang_item_path(trait_lang_item, opaque_ty_span, Some(bound_args)),
|
||||
hir_ref_id: self.next_id(),
|
||||
@ -2054,11 +2045,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
param: &GenericParam,
|
||||
source: hir::GenericParamSource,
|
||||
) -> hir::GenericParam<'hir> {
|
||||
let (name, kind) = self.lower_generic_param_kind(
|
||||
param,
|
||||
source,
|
||||
attr::contains_name(¶m.attrs, sym::rustc_runtime),
|
||||
);
|
||||
let (name, kind) = self.lower_generic_param_kind(param, source);
|
||||
|
||||
let hir_id = self.lower_node_id(param.id);
|
||||
self.lower_attrs(hir_id, ¶m.attrs);
|
||||
@ -2078,7 +2065,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
&mut self,
|
||||
param: &GenericParam,
|
||||
source: hir::GenericParamSource,
|
||||
is_host_effect: bool,
|
||||
) -> (hir::ParamName, hir::GenericParamKind<'hir>) {
|
||||
match ¶m.kind {
|
||||
GenericParamKind::Lifetime => {
|
||||
@ -2144,7 +2130,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
(
|
||||
hir::ParamName::Plain(self.lower_ident(param.ident)),
|
||||
hir::GenericParamKind::Const { ty, default, is_host_effect, synthetic: false },
|
||||
hir::GenericParamKind::Const { ty, default, synthetic: false },
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2270,7 +2256,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen<'hir> {
|
||||
match c.value.kind {
|
||||
ExprKind::Underscore => {
|
||||
if self.tcx.features().generic_arg_infer {
|
||||
if self.tcx.features().generic_arg_infer() {
|
||||
hir::ArrayLen::Infer(hir::InferArg {
|
||||
hir_id: self.lower_node_id(c.id),
|
||||
span: self.lower_span(c.value.span),
|
||||
@ -2445,22 +2431,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
fn lower_trait_bound_modifiers(
|
||||
&mut self,
|
||||
modifiers: TraitBoundModifiers,
|
||||
) -> hir::TraitBoundModifier {
|
||||
// Invalid modifier combinations will cause an error during AST validation.
|
||||
// Arbitrarily pick a placeholder for them to make compilation proceed.
|
||||
match (modifiers.constness, modifiers.polarity) {
|
||||
(BoundConstness::Never, BoundPolarity::Positive) => hir::TraitBoundModifier::None,
|
||||
(_, BoundPolarity::Maybe(_)) => hir::TraitBoundModifier::Maybe,
|
||||
(BoundConstness::Never, BoundPolarity::Negative(_)) => {
|
||||
if self.tcx.features().negative_bounds {
|
||||
hir::TraitBoundModifier::Negative
|
||||
} else {
|
||||
hir::TraitBoundModifier::None
|
||||
}
|
||||
}
|
||||
(BoundConstness::Always(_), _) => hir::TraitBoundModifier::Const,
|
||||
(BoundConstness::Maybe(_), _) => hir::TraitBoundModifier::MaybeConst,
|
||||
}
|
||||
) -> hir::TraitBoundModifiers {
|
||||
hir::TraitBoundModifiers { constness: modifiers.constness, polarity: modifiers.polarity }
|
||||
}
|
||||
|
||||
// Helper methods for building HIR.
|
||||
@ -2626,7 +2598,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
|
||||
let principal = hir::PolyTraitRef {
|
||||
bound_generic_params: &[],
|
||||
modifiers: hir::TraitBoundModifier::None,
|
||||
modifiers: hir::TraitBoundModifiers::NONE,
|
||||
trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
|
||||
span: self.lower_span(span),
|
||||
};
|
||||
|
@ -34,7 +34,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
modifiers: Option<ast::TraitBoundModifiers>,
|
||||
) -> hir::QPath<'hir> {
|
||||
let qself_position = qself.as_ref().map(|q| q.position);
|
||||
let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
|
||||
let qself = qself
|
||||
.as_ref()
|
||||
// Reject cases like `<impl Trait>::Assoc` and `<impl Trait as Trait>::Assoc`.
|
||||
.map(|q| self.lower_ty(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)));
|
||||
|
||||
let partial_res =
|
||||
self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
|
||||
@ -75,6 +78,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
None
|
||||
};
|
||||
|
||||
// Only permit `impl Trait` in the final segment. E.g., we permit `Option<impl Trait>`,
|
||||
// `option::Option<T>::Xyz<impl Trait>` and reject `option::Option<impl Trait>::Xyz`.
|
||||
let itctx = |i| {
|
||||
if i + 1 == p.segments.len() {
|
||||
itctx
|
||||
} else {
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path)
|
||||
}
|
||||
};
|
||||
|
||||
let path_span_lo = p.span.shrink_to_lo();
|
||||
let proj_start = p.segments.len() - unresolved_segments;
|
||||
let path = self.arena.alloc(hir::Path {
|
||||
@ -121,7 +134,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
segment,
|
||||
param_mode,
|
||||
generic_args_mode,
|
||||
itctx,
|
||||
itctx(i),
|
||||
bound_modifier_allowed_features.clone(),
|
||||
)
|
||||
},
|
||||
@ -185,7 +198,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
segment,
|
||||
param_mode,
|
||||
generic_args_mode,
|
||||
itctx,
|
||||
itctx(i),
|
||||
None,
|
||||
));
|
||||
let qpath = hir::QPath::TypeRelative(ty, hir_segment);
|
||||
@ -268,7 +281,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
span: data.inputs_span,
|
||||
})
|
||||
};
|
||||
if !self.tcx.features().return_type_notation
|
||||
if !self.tcx.features().return_type_notation()
|
||||
&& self.tcx.sess.is_nightly_build()
|
||||
{
|
||||
add_feature_diagnostics(
|
||||
@ -496,7 +509,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^
|
||||
// ```
|
||||
FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => {
|
||||
if self.tcx.features().impl_trait_in_fn_trait_return {
|
||||
if self.tcx.features().impl_trait_in_fn_trait_return() {
|
||||
self.lower_ty(ty, itctx)
|
||||
} else {
|
||||
self.lower_ty(
|
||||
|
@ -146,8 +146,6 @@ ast_passes_generic_before_constraints = generic arguments must come before the f
|
||||
|
||||
ast_passes_generic_default_trailing = generic parameters with a default must be trailing
|
||||
|
||||
ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters
|
||||
|
||||
ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
|
||||
.help = remove one of these features
|
||||
|
||||
|
@ -80,10 +80,6 @@ struct AstValidator<'a> {
|
||||
|
||||
disallow_tilde_const: Option<TildeConstReason>,
|
||||
|
||||
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
||||
/// or `Foo::Bar<impl Trait>`
|
||||
is_impl_trait_banned: bool,
|
||||
|
||||
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
|
||||
extern_mod_safety: Option<Safety>,
|
||||
|
||||
@ -123,12 +119,6 @@ impl<'a> AstValidator<'a> {
|
||||
self.extern_mod_safety = old;
|
||||
}
|
||||
|
||||
fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
let old = mem::replace(&mut self.is_impl_trait_banned, true);
|
||||
f(self);
|
||||
self.is_impl_trait_banned = old;
|
||||
}
|
||||
|
||||
fn with_tilde_const(
|
||||
&mut self,
|
||||
disallowed: Option<TildeConstReason>,
|
||||
@ -213,43 +203,12 @@ impl<'a> AstValidator<'a> {
|
||||
.with_tilde_const(Some(TildeConstReason::TraitObject), |this| {
|
||||
visit::walk_ty(this, t)
|
||||
}),
|
||||
TyKind::Path(qself, path) => {
|
||||
// We allow these:
|
||||
// - `Option<impl Trait>`
|
||||
// - `option::Option<impl Trait>`
|
||||
// - `option::Option<T>::Foo<impl Trait>`
|
||||
//
|
||||
// But not these:
|
||||
// - `<impl Trait>::Foo`
|
||||
// - `option::Option<impl Trait>::Foo`.
|
||||
//
|
||||
// To implement this, we disallow `impl Trait` from `qself`
|
||||
// (for cases like `<impl Trait>::Foo>`)
|
||||
// but we allow `impl Trait` in `GenericArgs`
|
||||
// iff there are no more PathSegments.
|
||||
if let Some(qself) = qself {
|
||||
// `impl Trait` in `qself` is always illegal
|
||||
self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
|
||||
}
|
||||
|
||||
// Note that there should be a call to visit_path here,
|
||||
// so if any logic is added to process `Path`s a call to it should be
|
||||
// added both in visit_path and here. This code mirrors visit::walk_path.
|
||||
for (i, segment) in path.segments.iter().enumerate() {
|
||||
// Allow `impl Trait` iff we're on the final path segment
|
||||
if i == path.segments.len() - 1 {
|
||||
self.visit_path_segment(segment);
|
||||
} else {
|
||||
self.with_banned_impl_trait(|this| this.visit_path_segment(segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => visit::walk_ty(self, t),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
|
||||
if let Some(ident) = field.ident
|
||||
if let Some(ref ident) = field.ident
|
||||
&& ident.name == kw::Underscore
|
||||
{
|
||||
self.visit_vis(&field.vis);
|
||||
@ -295,7 +254,8 @@ impl<'a> AstValidator<'a> {
|
||||
return;
|
||||
};
|
||||
|
||||
let make_impl_const_sugg = if self.features.const_trait_impl
|
||||
let const_trait_impl = self.features.const_trait_impl();
|
||||
let make_impl_const_sugg = if const_trait_impl
|
||||
&& let TraitOrTraitImpl::TraitImpl {
|
||||
constness: Const::No,
|
||||
polarity: ImplPolarity::Positive,
|
||||
@ -308,9 +268,8 @@ impl<'a> AstValidator<'a> {
|
||||
None
|
||||
};
|
||||
|
||||
let make_trait_const_sugg = if self.features.const_trait_impl
|
||||
&& let TraitOrTraitImpl::Trait { span, constness: None } = parent
|
||||
{
|
||||
let make_trait_const_sugg =
|
||||
if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent {
|
||||
Some(span.shrink_to_lo())
|
||||
} else {
|
||||
None
|
||||
@ -737,10 +696,6 @@ impl<'a> AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
TyKind::ImplTrait(_, bounds) => {
|
||||
if self.is_impl_trait_banned {
|
||||
self.dcx().emit_err(errors::ImplTraitPath { span: ty.span });
|
||||
}
|
||||
|
||||
if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
|
||||
self.dcx().emit_err(errors::NestedImplTrait {
|
||||
span: ty.span,
|
||||
@ -899,7 +854,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
|
||||
this.visit_vis(&item.vis);
|
||||
this.visit_ident(item.ident);
|
||||
this.visit_ident(&item.ident);
|
||||
let disallowed = matches!(constness, Const::No)
|
||||
.then(|| TildeConstReason::TraitImpl { span: item.span });
|
||||
this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
|
||||
@ -953,7 +908,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
|
||||
this.visit_vis(&item.vis);
|
||||
this.visit_ident(item.ident);
|
||||
this.visit_ident(&item.ident);
|
||||
this.with_tilde_const(
|
||||
Some(TildeConstReason::Impl { span: item.span }),
|
||||
|this| this.visit_generics(generics),
|
||||
@ -991,7 +946,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_ident(&item.ident);
|
||||
let kind =
|
||||
FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, generics, body.as_deref());
|
||||
self.visit_fn(kind, item.span, item.id);
|
||||
@ -1058,7 +1013,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
|
||||
// context for the supertraits.
|
||||
this.visit_vis(&item.vis);
|
||||
this.visit_ident(item.ident);
|
||||
this.visit_ident(&item.ident);
|
||||
let disallowed = is_const_trait
|
||||
.is_none()
|
||||
.then(|| TildeConstReason::Trait { span: item.span });
|
||||
@ -1085,7 +1040,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
ItemKind::Struct(vdata, generics) => match vdata {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_ident(&item.ident);
|
||||
self.visit_generics(generics);
|
||||
// Permit `Anon{Struct,Union}` as field type.
|
||||
walk_list!(self, visit_struct_field_def, fields);
|
||||
@ -1101,7 +1056,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
match vdata {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_ident(&item.ident);
|
||||
self.visit_generics(generics);
|
||||
// Permit `Anon{Struct,Union}` as field type.
|
||||
walk_list!(self, visit_struct_field_def, fields);
|
||||
@ -1145,7 +1100,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
self.check_type_no_bounds(bounds, "this context");
|
||||
|
||||
if self.features.lazy_type_alias {
|
||||
if self.features.lazy_type_alias() {
|
||||
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
|
||||
self.dcx().emit_err(err);
|
||||
}
|
||||
@ -1286,7 +1241,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
GenericBound::Trait(trait_ref) => {
|
||||
match (ctxt, trait_ref.modifiers.constness, trait_ref.modifiers.polarity) {
|
||||
(BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
|
||||
if !self.features.more_maybe_bounds =>
|
||||
if !self.features.more_maybe_bounds() =>
|
||||
{
|
||||
self.sess
|
||||
.create_feature_err(
|
||||
@ -1299,7 +1254,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
.emit();
|
||||
}
|
||||
(BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
|
||||
if !self.features.more_maybe_bounds =>
|
||||
if !self.features.more_maybe_bounds() =>
|
||||
{
|
||||
self.sess
|
||||
.create_feature_err(
|
||||
@ -1521,7 +1476,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
|| matches!(sig.header.constness, Const::Yes(_)) =>
|
||||
{
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_ident(&item.ident);
|
||||
let kind = FnKind::Fn(
|
||||
FnCtxt::Assoc(ctxt),
|
||||
item.ident,
|
||||
@ -1729,7 +1684,6 @@ pub fn check_crate(
|
||||
has_proc_macro_decls: false,
|
||||
outer_impl_trait: None,
|
||||
disallow_tilde_const: Some(TildeConstReason::Item),
|
||||
is_impl_trait_banned: false,
|
||||
extern_mod_safety: None,
|
||||
lint_buffer: lints,
|
||||
};
|
||||
|
@ -418,13 +418,6 @@ pub(crate) struct TraitObjectBound {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_impl_trait_path, code = E0667)]
|
||||
pub(crate) struct ImplTraitPath {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_nested_impl_trait, code = E0666)]
|
||||
pub(crate) struct NestedImplTrait {
|
||||
|
@ -15,13 +15,13 @@ use crate::errors;
|
||||
/// The common case.
|
||||
macro_rules! gate {
|
||||
($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
|
||||
if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
|
||||
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
|
||||
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
||||
feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit();
|
||||
}
|
||||
}};
|
||||
($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
|
||||
if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
|
||||
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
|
||||
// FIXME: make this translatable
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
@ -43,7 +43,7 @@ macro_rules! gate_alt {
|
||||
/// The case involving a multispan.
|
||||
macro_rules! gate_multi {
|
||||
($visitor:expr, $feature:ident, $spans:expr, $explain:expr) => {{
|
||||
if !$visitor.features.$feature {
|
||||
if !$visitor.features.$feature() {
|
||||
let spans: Vec<_> =
|
||||
$spans.filter(|span| !span.allows_unstable(sym::$feature)).collect();
|
||||
if !spans.is_empty() {
|
||||
@ -56,7 +56,7 @@ macro_rules! gate_multi {
|
||||
/// The legacy case.
|
||||
macro_rules! gate_legacy {
|
||||
($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
|
||||
if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
|
||||
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
|
||||
feature_warn(&$visitor.sess, sym::$feature, $span, $explain);
|
||||
}
|
||||
}};
|
||||
@ -150,7 +150,7 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
|
||||
// FIXME(non_lifetime_binders): Const bound params are pretty broken.
|
||||
// Let's keep users from using this feature accidentally.
|
||||
if self.features.non_lifetime_binders {
|
||||
if self.features.non_lifetime_binders() {
|
||||
let const_param_spans: Vec<_> = params
|
||||
.iter()
|
||||
.filter_map(|param| match param.kind {
|
||||
@ -210,7 +210,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
}
|
||||
|
||||
// Emit errors for non-staged-api crates.
|
||||
if !self.features.staged_api {
|
||||
if !self.features.staged_api() {
|
||||
if attr.has_name(sym::unstable)
|
||||
|| attr.has_name(sym::stable)
|
||||
|| attr.has_name(sym::rustc_const_unstable)
|
||||
@ -470,7 +470,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
// Limit `min_specialization` to only specializing functions.
|
||||
gate_alt!(
|
||||
&self,
|
||||
self.features.specialization || (is_fn && self.features.min_specialization),
|
||||
self.features.specialization() || (is_fn && self.features.min_specialization()),
|
||||
sym::specialization,
|
||||
i.span,
|
||||
"specialization is unstable"
|
||||
@ -548,7 +548,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
gate_all!(return_type_notation, "return type notation is experimental");
|
||||
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
|
||||
|
||||
if !visitor.features.never_patterns {
|
||||
if !visitor.features.never_patterns() {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
for &span in spans {
|
||||
if span.allows_unstable(sym::never_patterns) {
|
||||
@ -572,7 +572,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
}
|
||||
}
|
||||
|
||||
if !visitor.features.negative_bounds {
|
||||
if !visitor.features.negative_bounds() {
|
||||
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
||||
sess.dcx().emit_err(errors::NegativeBoundUnsupported { span });
|
||||
}
|
||||
@ -600,14 +600,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
}
|
||||
|
||||
fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) {
|
||||
// checks if `#![feature]` has been used to enable any lang feature
|
||||
// does not check the same for lib features unless there's at least one
|
||||
// declared lang feature
|
||||
if !sess.opts.unstable_features.is_nightly_build() {
|
||||
if features.declared_features.is_empty() {
|
||||
// checks if `#![feature]` has been used to enable any feature.
|
||||
if sess.opts.unstable_features.is_nightly_build() {
|
||||
return;
|
||||
}
|
||||
if features.enabled_features().is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut errored = false;
|
||||
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
|
||||
// `feature(...)` used on non-nightly. This is definitely an error.
|
||||
let mut err = errors::FeatureOnNonNightly {
|
||||
span: attr.span,
|
||||
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
|
||||
@ -616,15 +618,14 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
|
||||
};
|
||||
|
||||
let mut all_stable = true;
|
||||
for ident in
|
||||
attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
|
||||
{
|
||||
for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) {
|
||||
let name = ident.name;
|
||||
let stable_since = features
|
||||
.declared_lang_features
|
||||
.enabled_lang_features()
|
||||
.iter()
|
||||
.flat_map(|&(feature, _, since)| if feature == name { since } else { None })
|
||||
.next();
|
||||
.find(|feat| feat.gate_name == name)
|
||||
.map(|feat| feat.stable_since)
|
||||
.flatten();
|
||||
if let Some(since) = stable_since {
|
||||
err.stable_features.push(errors::StableFeature { name, since });
|
||||
} else {
|
||||
@ -635,24 +636,25 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
|
||||
err.sugg = Some(attr.span);
|
||||
}
|
||||
sess.dcx().emit_err(err);
|
||||
errored = true;
|
||||
}
|
||||
}
|
||||
// Just make sure we actually error if anything is listed in `enabled_features`.
|
||||
assert!(errored);
|
||||
}
|
||||
|
||||
fn check_incompatible_features(sess: &Session, features: &Features) {
|
||||
let declared_features = features
|
||||
.declared_lang_features
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|(name, span, _)| (name, span))
|
||||
.chain(features.declared_lib_features.iter().copied());
|
||||
let enabled_lang_features =
|
||||
features.enabled_lang_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
|
||||
let enabled_lib_features =
|
||||
features.enabled_lib_features().iter().map(|feat| (feat.gate_name, feat.attr_sp));
|
||||
let enabled_features = enabled_lang_features.chain(enabled_lib_features);
|
||||
|
||||
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
|
||||
.iter()
|
||||
.filter(|&&(f1, f2)| features.active(f1) && features.active(f2))
|
||||
.filter(|(f1, f2)| features.enabled(*f1) && features.enabled(*f2))
|
||||
{
|
||||
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
|
||||
if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
|
||||
if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) {
|
||||
if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2)
|
||||
{
|
||||
let spans = vec![f1_span, f2_span];
|
||||
sess.dcx().emit_err(errors::IncompatibleFeatures {
|
||||
@ -671,10 +673,11 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
|
||||
}
|
||||
|
||||
// Ban GCE with the new solver, because it does not implement GCE correctly.
|
||||
if let Some(&(_, gce_span, _)) = features
|
||||
.declared_lang_features
|
||||
if let Some(gce_span) = features
|
||||
.enabled_lang_features()
|
||||
.iter()
|
||||
.find(|&&(feat, _, _)| feat == sym::generic_const_exprs)
|
||||
.find(|feat| feat.gate_name == sym::generic_const_exprs)
|
||||
.map(|feat| feat.attr_sp)
|
||||
{
|
||||
sess.dcx().emit_err(errors::IncompatibleFeatures {
|
||||
spans: vec![gce_span],
|
||||
|
@ -16,7 +16,7 @@ impl NodeCounter {
|
||||
}
|
||||
|
||||
impl<'ast> Visitor<'ast> for NodeCounter {
|
||||
fn visit_ident(&mut self, _ident: Ident) {
|
||||
fn visit_ident(&mut self, _ident: &Ident) {
|
||||
self.count += 1;
|
||||
}
|
||||
fn visit_foreign_item(&mut self, i: &ForeignItem) {
|
||||
|
@ -627,6 +627,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
|
||||
fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
|
||||
self.ibox(0);
|
||||
match item.unsafety {
|
||||
ast::Safety::Unsafe(_) => {
|
||||
self.word("unsafe");
|
||||
self.popen();
|
||||
}
|
||||
ast::Safety::Default | ast::Safety::Safe(_) => {}
|
||||
}
|
||||
match &item.args {
|
||||
AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common(
|
||||
Some(MacHeader::Path(&item.path)),
|
||||
@ -655,6 +662,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
self.word(token_str);
|
||||
}
|
||||
}
|
||||
match item.unsafety {
|
||||
ast::Safety::Unsafe(_) => self.pclose(),
|
||||
ast::Safety::Default | ast::Safety::Safe(_) => {}
|
||||
}
|
||||
self.end();
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ impl<'a> State<'a> {
|
||||
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
|
||||
}
|
||||
ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => {
|
||||
self.print_safety(*safety);
|
||||
self.print_item_const(
|
||||
ident,
|
||||
Some(*mutability),
|
||||
@ -46,6 +45,7 @@ impl<'a> State<'a> {
|
||||
ty,
|
||||
expr.as_deref(),
|
||||
vis,
|
||||
*safety,
|
||||
ast::Defaultness::Final,
|
||||
)
|
||||
}
|
||||
@ -84,10 +84,12 @@ impl<'a> State<'a> {
|
||||
ty: &ast::Ty,
|
||||
body: Option<&ast::Expr>,
|
||||
vis: &ast::Visibility,
|
||||
safety: ast::Safety,
|
||||
defaultness: ast::Defaultness,
|
||||
) {
|
||||
self.head("");
|
||||
self.print_visibility(vis);
|
||||
self.print_safety(safety);
|
||||
self.print_defaultness(defaultness);
|
||||
let leading = match mutbl {
|
||||
None => "const",
|
||||
@ -181,6 +183,7 @@ impl<'a> State<'a> {
|
||||
ty,
|
||||
body.as_deref(),
|
||||
&item.vis,
|
||||
ast::Safety::Default,
|
||||
ast::Defaultness::Final,
|
||||
);
|
||||
}
|
||||
@ -192,6 +195,7 @@ impl<'a> State<'a> {
|
||||
ty,
|
||||
expr.as_deref(),
|
||||
&item.vis,
|
||||
ast::Safety::Default,
|
||||
*defaultness,
|
||||
);
|
||||
}
|
||||
@ -549,6 +553,7 @@ impl<'a> State<'a> {
|
||||
ty,
|
||||
expr.as_deref(),
|
||||
vis,
|
||||
ast::Safety::Default,
|
||||
*defaultness,
|
||||
);
|
||||
}
|
||||
|
@ -91,6 +91,9 @@ attr_non_ident_feature =
|
||||
attr_rustc_allowed_unstable_pairing =
|
||||
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
|
||||
|
||||
attr_rustc_const_stable_indirect_pairing =
|
||||
`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
|
||||
|
||||
attr_rustc_promotable_pairing =
|
||||
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
|
||||
|
||||
|
@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::{RustcVersion, Session};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::symbol::{Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
||||
use crate::fluent_generated;
|
||||
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
||||
@ -92,7 +92,11 @@ impl Stability {
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct ConstStability {
|
||||
pub level: StabilityLevel,
|
||||
pub feature: Symbol,
|
||||
/// This can be `None` for functions that do not have an explicit const feature.
|
||||
/// We still track them for recursive const stability checks.
|
||||
pub feature: Option<Symbol>,
|
||||
/// This is true iff the `const_stable_indirect` attribute is present.
|
||||
pub const_stable_indirect: bool,
|
||||
/// whether the function has a `#[rustc_promotable]` attribute
|
||||
pub promotable: bool,
|
||||
}
|
||||
@ -268,17 +272,23 @@ pub fn find_stability(
|
||||
|
||||
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
|
||||
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
|
||||
///
|
||||
/// `is_const_fn` indicates whether this is a function marked as `const`. It will always
|
||||
/// be false for intrinsics in an `extern` block!
|
||||
pub fn find_const_stability(
|
||||
sess: &Session,
|
||||
attrs: &[Attribute],
|
||||
item_sp: Span,
|
||||
is_const_fn: bool,
|
||||
) -> Option<(ConstStability, Span)> {
|
||||
let mut const_stab: Option<(ConstStability, Span)> = None;
|
||||
let mut promotable = false;
|
||||
let mut const_stable_indirect = None;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name_or_empty() {
|
||||
sym::rustc_promotable => promotable = true,
|
||||
sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span),
|
||||
sym::rustc_const_unstable => {
|
||||
if const_stab.is_some() {
|
||||
sess.dcx()
|
||||
@ -287,8 +297,15 @@ pub fn find_const_stability(
|
||||
}
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
const_stab =
|
||||
Some((ConstStability { level, feature, promotable: false }, attr.span));
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature: Some(feature),
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
attr.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
sym::rustc_const_stable => {
|
||||
@ -298,15 +315,22 @@ pub fn find_const_stability(
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
const_stab =
|
||||
Some((ConstStability { level, feature, promotable: false }, attr.span));
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature: Some(feature),
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
attr.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the const-unstable info into the stability info
|
||||
// Merge promotable and not_exposed_on_stable into stability info
|
||||
if promotable {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => stab.promotable = promotable,
|
||||
@ -317,6 +341,46 @@ pub fn find_const_stability(
|
||||
}
|
||||
}
|
||||
}
|
||||
if const_stable_indirect.is_some() {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => {
|
||||
if stab.is_const_unstable() {
|
||||
stab.const_stable_indirect = true;
|
||||
} else {
|
||||
_ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
|
||||
span: item_sp,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// We ignore the `#[rustc_const_stable_indirect]` here, it should be picked up by
|
||||
// the `default_const_unstable` logic.
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure if `const_stable_indirect` is present, that is recorded. Also make sure all `const
|
||||
// fn` get *some* marker, since we are a staged_api crate and therefore will do recursive const
|
||||
// stability checks for them. We need to do this because the default for whether an unmarked
|
||||
// function enforces recursive stability differs between staged-api crates and force-unmarked
|
||||
// crates: in force-unmarked crates, only functions *explicitly* marked `const_stable_indirect`
|
||||
// enforce recursive stability. Therefore when `lookup_const_stability` is `None`, we have to
|
||||
// assume the function does not have recursive stability. All functions that *do* have recursive
|
||||
// stability must explicitly record this, and so that's what we do for all `const fn` in a
|
||||
// staged_api crate.
|
||||
if (is_const_fn || const_stable_indirect.is_some()) && const_stab.is_none() {
|
||||
let c = ConstStability {
|
||||
feature: None,
|
||||
const_stable_indirect: const_stable_indirect.is_some(),
|
||||
promotable: false,
|
||||
level: StabilityLevel::Unstable {
|
||||
reason: UnstableReason::Default,
|
||||
issue: None,
|
||||
is_soft: false,
|
||||
implied_by: None,
|
||||
},
|
||||
};
|
||||
const_stab = Some((c, const_stable_indirect.unwrap_or(DUMMY_SP)));
|
||||
}
|
||||
|
||||
const_stab
|
||||
}
|
||||
@ -619,11 +683,11 @@ pub fn eval_condition(
|
||||
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
|
||||
// and `true`, and we want to keep the former working without feature gate
|
||||
gate_cfg(
|
||||
&((
|
||||
&(
|
||||
if *b { kw::True } else { kw::False },
|
||||
sym::cfg_boolean_literals,
|
||||
|features: &Features| features.cfg_boolean_literals,
|
||||
)),
|
||||
|features: &Features| features.cfg_boolean_literals(),
|
||||
),
|
||||
cfg.span(),
|
||||
sess,
|
||||
features,
|
||||
@ -711,7 +775,7 @@ pub fn eval_condition(
|
||||
}
|
||||
sym::target => {
|
||||
if let Some(features) = features
|
||||
&& !features.cfg_target_compact
|
||||
&& !features.cfg_target_compact()
|
||||
{
|
||||
feature_err(
|
||||
sess,
|
||||
@ -831,7 +895,7 @@ pub fn find_deprecation(
|
||||
attrs: &[Attribute],
|
||||
) -> Option<(Deprecation, Span)> {
|
||||
let mut depr: Option<(Deprecation, Span)> = None;
|
||||
let is_rustc = features.staged_api;
|
||||
let is_rustc = features.staged_api();
|
||||
|
||||
'outer: for attr in attrs {
|
||||
if !attr.has_name(sym::deprecated) {
|
||||
@ -891,7 +955,7 @@ pub fn find_deprecation(
|
||||
}
|
||||
}
|
||||
sym::suggestion => {
|
||||
if !features.deprecated_suggestion {
|
||||
if !features.deprecated_suggestion() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: mi.span,
|
||||
@ -909,7 +973,7 @@ pub fn find_deprecation(
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
expected: if features.deprecated_suggestion {
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
|
@ -318,6 +318,13 @@ pub(crate) struct RustcPromotablePairing {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_rustc_const_stable_indirect_pairing)]
|
||||
pub(crate) struct RustcConstStableIndirectPairing {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_rustc_allowed_unstable_pairing, code = E0789)]
|
||||
pub(crate) struct RustcAllowedUnstablePairing {
|
||||
|
@ -65,6 +65,7 @@ impl<'tcx> UniverseInfo<'tcx> {
|
||||
UniverseInfoInner::RelateTys { expected, found } => {
|
||||
let err = mbcx.infcx.err_ctxt().report_mismatched_types(
|
||||
&cause,
|
||||
mbcx.param_env,
|
||||
expected,
|
||||
found,
|
||||
TypeError::RegionsPlaceholderMismatch,
|
||||
@ -480,12 +481,11 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
|
||||
.try_report_from_nll()
|
||||
.or_else(|| {
|
||||
if let SubregionOrigin::Subtype(trace) = cause {
|
||||
Some(
|
||||
infcx.err_ctxt().report_and_explain_type_error(
|
||||
Some(infcx.err_ctxt().report_and_explain_type_error(
|
||||
*trace,
|
||||
infcx.tcx.param_env(generic_param_scope),
|
||||
TypeError::RegionsPlaceholderMismatch,
|
||||
),
|
||||
)
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -959,13 +959,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
None
|
||||
}
|
||||
}
|
||||
hir::ExprKind::MethodCall(_, _, args, span) => {
|
||||
if let Some(def_id) = typeck_results.type_dependent_def_id(*hir_id) {
|
||||
Some((def_id, *span, *args))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
hir::ExprKind::MethodCall(_, _, args, span) => typeck_results
|
||||
.type_dependent_def_id(*hir_id)
|
||||
.map(|def_id| (def_id, *span, *args)),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
@ -360,6 +360,7 @@ fn check_opaque_type_well_formed<'tcx>(
|
||||
.err_ctxt()
|
||||
.report_mismatched_types(
|
||||
&ObligationCause::misc(definition_span, def_id),
|
||||
param_env,
|
||||
opaque_ty,
|
||||
definition_ty,
|
||||
err,
|
||||
|
@ -1035,7 +1035,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
|
||||
fn unsized_feature_enabled(&self) -> bool {
|
||||
let features = self.tcx().features();
|
||||
features.unsized_locals || features.unsized_fn_params
|
||||
features.unsized_locals() || features.unsized_fn_params()
|
||||
}
|
||||
|
||||
/// Equate the inferred type and the annotated type for user type annotations
|
||||
|
@ -69,7 +69,7 @@ pub(crate) fn expand_assert<'cx>(
|
||||
// If `generic_assert` is enabled, generates rich captured outputs
|
||||
//
|
||||
// FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
|
||||
else if cx.ecfg.features.generic_assert {
|
||||
else if cx.ecfg.features.generic_assert() {
|
||||
context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
|
||||
}
|
||||
// If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
|
||||
|
@ -19,7 +19,7 @@ macro_rules! path {
|
||||
($span:expr, $($part:ident)::*) => { vec![$(Ident::new(sym::$part, $span),)*] }
|
||||
}
|
||||
|
||||
pub(crate) fn expand_deriving_smart_ptr(
|
||||
pub(crate) fn expand_deriving_coerce_pointee(
|
||||
cx: &ExtCtxt<'_>,
|
||||
span: Span,
|
||||
_mitem: &MetaItem,
|
||||
@ -41,7 +41,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
cx.dcx()
|
||||
.struct_span_err(
|
||||
span,
|
||||
"`SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`",
|
||||
"`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`",
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
@ -54,7 +54,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
cx.dcx()
|
||||
.struct_span_err(
|
||||
span,
|
||||
"`SmartPointer` can only be derived on `struct`s with at least one field",
|
||||
"`CoercePointee` can only be derived on `struct`s with at least one field",
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
@ -64,7 +64,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
cx.dcx()
|
||||
.struct_span_err(
|
||||
span,
|
||||
"`SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`",
|
||||
"`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`",
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
@ -94,10 +94,10 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
.collect();
|
||||
|
||||
let pointee_param_idx = if type_params.is_empty() {
|
||||
// `#[derive(SmartPointer)]` requires at least one generic type on the target `struct`
|
||||
// `#[derive(CoercePointee)]` requires at least one generic type on the target `struct`
|
||||
cx.dcx().struct_span_err(
|
||||
span,
|
||||
"`SmartPointer` can only be derived on `struct`s that are generic over at least one type",
|
||||
"`CoercePointee` can only be derived on `struct`s that are generic over at least one type",
|
||||
).emit();
|
||||
return;
|
||||
} else if type_params.len() == 1 {
|
||||
@ -113,7 +113,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
(None, _) => {
|
||||
cx.dcx().struct_span_err(
|
||||
span,
|
||||
"exactly one generic type parameter must be marked as #[pointee] to derive SmartPointer traits",
|
||||
"exactly one generic type parameter must be marked as #[pointee] to derive CoercePointee traits",
|
||||
).emit();
|
||||
return;
|
||||
}
|
||||
@ -121,7 +121,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
cx.dcx()
|
||||
.struct_span_err(
|
||||
vec![one, another],
|
||||
"only one type parameter can be marked as `#[pointee]` when deriving SmartPointer traits",
|
||||
"only one type parameter can be marked as `#[pointee]` when deriving CoercePointee traits",
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
@ -185,7 +185,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
.struct_span_err(
|
||||
pointee_ty_ident.span,
|
||||
format!(
|
||||
"`derive(SmartPointer)` requires {} to be marked `?Sized`",
|
||||
"`derive(CoercePointee)` requires {} to be marked `?Sized`",
|
||||
pointee_ty_ident.name
|
||||
),
|
||||
)
|
||||
@ -195,7 +195,7 @@ pub(crate) fn expand_deriving_smart_ptr(
|
||||
let arg = GenericArg::Type(s_ty.clone());
|
||||
let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
|
||||
pointee.bounds.push(cx.trait_bound(unsize, false));
|
||||
// Drop `#[pointee]` attribute since it should not be recognized outside `derive(SmartPointer)`
|
||||
// Drop `#[pointee]` attribute since it should not be recognized outside `derive(CoercePointee)`
|
||||
pointee.attrs.retain(|attr| !attr.has_name(sym::pointee));
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, '
|
||||
rustc_ast::visit::walk_attribute(self, attr);
|
||||
}
|
||||
fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
|
||||
self.visit_ident(v.ident);
|
||||
self.visit_ident(&v.ident);
|
||||
self.visit_vis(&v.vis);
|
||||
self.visit_variant_data(&v.data);
|
||||
visit_opt!(self, visit_anon_const, &v.disr_expr);
|
||||
|
@ -22,12 +22,12 @@ macro path_std($($x:tt)*) {
|
||||
|
||||
pub(crate) mod bounds;
|
||||
pub(crate) mod clone;
|
||||
pub(crate) mod coerce_pointee;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod decodable;
|
||||
pub(crate) mod default;
|
||||
pub(crate) mod encodable;
|
||||
pub(crate) mod hash;
|
||||
pub(crate) mod smart_ptr;
|
||||
|
||||
#[path = "cmp/eq.rs"]
|
||||
pub(crate) mod eq;
|
||||
|
@ -133,7 +133,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
||||
PartialOrd: partial_ord::expand_deriving_partial_ord,
|
||||
RustcDecodable: decodable::expand_deriving_rustc_decodable,
|
||||
RustcEncodable: encodable::expand_deriving_rustc_encodable,
|
||||
SmartPointer: smart_ptr::expand_deriving_smart_ptr,
|
||||
CoercePointee: coerce_pointee::expand_deriving_coerce_pointee,
|
||||
}
|
||||
|
||||
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
|
||||
|
@ -313,14 +313,23 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
|
||||
match m {
|
||||
ProcMacro::Derive(cd) => {
|
||||
cx.resolver.declare_proc_macro(cd.id);
|
||||
cx.expr_call(span, proc_macro_ty_method_path(cx, custom_derive), thin_vec![
|
||||
// The call needs to use `harness_span` so that the const stability checker
|
||||
// accepts it.
|
||||
cx.expr_call(
|
||||
harness_span,
|
||||
proc_macro_ty_method_path(cx, custom_derive),
|
||||
thin_vec![
|
||||
cx.expr_str(span, cd.trait_name),
|
||||
cx.expr_array_ref(
|
||||
span,
|
||||
cd.attrs.iter().map(|&s| cx.expr_str(span, s)).collect::<ThinVec<_>>(),
|
||||
cd.attrs
|
||||
.iter()
|
||||
.map(|&s| cx.expr_str(span, s))
|
||||
.collect::<ThinVec<_>>(),
|
||||
),
|
||||
local_path(cx, cd.function_name),
|
||||
])
|
||||
],
|
||||
)
|
||||
}
|
||||
ProcMacro::Attr(ca) | ProcMacro::Bang(ca) => {
|
||||
cx.resolver.declare_proc_macro(ca.id);
|
||||
@ -330,7 +339,9 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
|
||||
ProcMacro::Derive(_) => unreachable!(),
|
||||
};
|
||||
|
||||
cx.expr_call(span, proc_macro_ty_method_path(cx, ident), thin_vec![
|
||||
// The call needs to use `harness_span` so that the const stability checker
|
||||
// accepts it.
|
||||
cx.expr_call(harness_span, proc_macro_ty_method_path(cx, ident), thin_vec![
|
||||
cx.expr_str(span, ca.function_name.name),
|
||||
local_path(cx, ca.function_name),
|
||||
])
|
||||
|
@ -47,12 +47,12 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
|
||||
|
||||
#[lang = "receiver"]
|
||||
pub trait Receiver {}
|
||||
#[lang = "legacy_receiver"]
|
||||
pub trait LegacyReceiver {}
|
||||
|
||||
impl<T: ?Sized> Receiver for &T {}
|
||||
impl<T: ?Sized> Receiver for &mut T {}
|
||||
impl<T: ?Sized> Receiver for Box<T> {}
|
||||
impl<T: ?Sized> LegacyReceiver for &T {}
|
||||
impl<T: ?Sized> LegacyReceiver for &mut T {}
|
||||
impl<T: ?Sized> LegacyReceiver for Box<T> {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub unsafe trait Copy {}
|
||||
|
@ -38,7 +38,7 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
|
||||
index d9de37e..8293fce 100644
|
||||
--- a/library/core/src/sync/atomic.rs
|
||||
+++ b/library/core/src/sync/atomic.rs
|
||||
@@ -2996,42 +2996,6 @@ atomic_int! {
|
||||
@@ -2996,44 +2996,6 @@ atomic_int! {
|
||||
8,
|
||||
u64 AtomicU64
|
||||
}
|
||||
@ -52,7 +52,8 @@ index d9de37e..8293fce 100644
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
|
||||
- rustc_const_unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- rustc_const_unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"),
|
||||
- "i128",
|
||||
- "#![feature(integer_atomics)]\n\n",
|
||||
@ -70,7 +71,8 @@ index d9de37e..8293fce 100644
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
|
||||
- rustc_const_unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- rustc_const_unstable(feature = "integer_atomics", issue = "99069"),
|
||||
- cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"),
|
||||
- "u128",
|
||||
- "#![feature(integer_atomics)]\n\n",
|
||||
|
@ -79,7 +79,7 @@ pub(super) fn add_local_place_comments<'tcx>(
|
||||
return;
|
||||
}
|
||||
let TyAndLayout { ty, layout } = place.layout();
|
||||
let rustc_target::abi::LayoutS { size, align, .. } = layout.0.0;
|
||||
let rustc_abi::LayoutData { size, align, .. } = layout.0.0;
|
||||
|
||||
let (kind, extra) = place.debug_comment();
|
||||
|
||||
|
@ -210,7 +210,6 @@ impl DebugContext {
|
||||
type_names::push_generic_params(
|
||||
tcx,
|
||||
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args),
|
||||
enclosing_fn_def_id,
|
||||
&mut name,
|
||||
);
|
||||
|
||||
|
@ -44,12 +44,12 @@ impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
|
||||
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {}
|
||||
|
||||
#[lang = "receiver"]
|
||||
pub trait Receiver {}
|
||||
#[lang = "legacy_receiver"]
|
||||
pub trait LegacyReceiver {}
|
||||
|
||||
impl<T: ?Sized> Receiver for &T {}
|
||||
impl<T: ?Sized> Receiver for &mut T {}
|
||||
impl<T: ?Sized, A: Allocator> Receiver for Box<T, A> {}
|
||||
impl<T: ?Sized> LegacyReceiver for &T {}
|
||||
impl<T: ?Sized> LegacyReceiver for &mut T {}
|
||||
impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub unsafe trait Copy {}
|
||||
|
@ -30,7 +30,7 @@ use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi};
|
||||
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi};
|
||||
|
||||
use crate::common::{SignType, TypeReflection, type_is_pointer};
|
||||
use crate::context::CodegenCx;
|
||||
@ -1725,16 +1725,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||
self.fptoint_sat(true, val, dest_ty)
|
||||
}
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
_fn_name: RValue<'gcc>,
|
||||
_hash: RValue<'gcc>,
|
||||
_num_counters: RValue<'gcc>,
|
||||
_index: RValue<'gcc>,
|
||||
) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
@ -2347,6 +2337,12 @@ impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> HasX86AbiOpt for Builder<'_, '_, 'tcx> {
|
||||
fn x86_abi_opt(&self) -> X86Abi {
|
||||
self.cx.x86_abi_opt()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToGccComp {
|
||||
fn to_gcc_comparison(&self) -> ComparisonOp;
|
||||
}
|
||||
|
@ -98,8 +98,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
|
||||
// whether we are sharing generics or not. The important thing here is
|
||||
// that the visibility we apply to the declaration is the same one that
|
||||
// has been applied to the definition (wherever that definition may be).
|
||||
let is_generic =
|
||||
instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some();
|
||||
let is_generic = instance.args.non_erasable_generics().next().is_some();
|
||||
|
||||
if is_generic {
|
||||
// This is a monomorphization. Its expected visibility depends
|
||||
|
@ -19,7 +19,9 @@ use rustc_session::Session;
|
||||
use rustc_span::source_map::respan;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
||||
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi};
|
||||
use rustc_target::spec::{
|
||||
HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi,
|
||||
};
|
||||
|
||||
use crate::callee::get_fn;
|
||||
use crate::common::SignType;
|
||||
@ -538,6 +540,12 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
|
||||
fn x86_abi_opt(&self) -> X86Abi {
|
||||
X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||
|
@ -197,7 +197,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
||||
/// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this
|
||||
/// is useful for indexing slices, as `&[T]`'s data pointer is `T*`.
|
||||
/// If the type is an unsized struct, the regular layout is generated,
|
||||
/// with the inner-most trailing unsized field using the "minimal unit"
|
||||
/// with the innermost trailing unsized field using the "minimal unit"
|
||||
/// of that field's type - this is useful for taking the address of
|
||||
/// that field and ensuring the struct has the right alignment.
|
||||
fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
|
||||
|
@ -77,20 +77,14 @@ pub(crate) unsafe fn codegen(
|
||||
// __rust_alloc_error_handler_should_panic
|
||||
let name = OomStrategy::SYMBOL;
|
||||
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
||||
llvm::LLVMRustSetVisibility(
|
||||
ll_g,
|
||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
||||
);
|
||||
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||
let val = tcx.sess.opts.unstable_opts.oom.should_panic();
|
||||
let llval = llvm::LLVMConstInt(i8, val as u64, False);
|
||||
llvm::LLVMSetInitializer(ll_g, llval);
|
||||
|
||||
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
|
||||
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
|
||||
llvm::LLVMRustSetVisibility(
|
||||
ll_g,
|
||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
||||
);
|
||||
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||
let llval = llvm::LLVMConstInt(i8, 0, False);
|
||||
llvm::LLVMSetInitializer(ll_g, llval);
|
||||
}
|
||||
@ -134,10 +128,7 @@ fn create_wrapper_function(
|
||||
None
|
||||
};
|
||||
|
||||
llvm::LLVMRustSetVisibility(
|
||||
llfn,
|
||||
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
|
||||
);
|
||||
llvm::set_visibility(llfn, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
|
||||
|
||||
if tcx.sess.must_emit_unwind_tables() {
|
||||
let uwtable =
|
||||
@ -151,7 +142,7 @@ fn create_wrapper_function(
|
||||
// -> ! DIFlagNoReturn
|
||||
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
|
||||
}
|
||||
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
|
||||
llvm::set_visibility(callee, llvm::Visibility::Hidden);
|
||||
|
||||
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr());
|
||||
|
||||
|
@ -1043,7 +1043,7 @@ unsafe fn embed_bitcode(
|
||||
|
||||
let section = bitcode_section_name(cgcx);
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
||||
|
||||
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
|
||||
@ -1061,7 +1061,7 @@ unsafe fn embed_bitcode(
|
||||
c".llvmcmd"
|
||||
};
|
||||
llvm::LLVMSetSection(llglobal, section.as_ptr());
|
||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
} else {
|
||||
// We need custom section flags, so emit module-level inline assembly.
|
||||
let section_flags = if cgcx.is_pe_coff { "n" } else { "e" };
|
||||
@ -1096,7 +1096,7 @@ fn create_msvc_imps(
|
||||
let ptr_ty = Type::ptr_llcx(llcx);
|
||||
let globals = base::iter_globals(llmod)
|
||||
.filter(|&val| {
|
||||
llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage
|
||||
llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage
|
||||
&& llvm::LLVMIsDeclaration(val) == 0
|
||||
})
|
||||
.filter_map(|val| {
|
||||
@ -1115,7 +1115,7 @@ fn create_msvc_imps(
|
||||
for (imp_name, val) in globals {
|
||||
let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr());
|
||||
llvm::LLVMSetInitializer(imp, val);
|
||||
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
|
||||
llvm::set_linkage(imp, llvm::Linkage::ExternalLinkage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1165,39 +1165,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size);
|
||||
}
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
num_counters: &'ll Value,
|
||||
index: &'ll Value,
|
||||
) {
|
||||
debug!(
|
||||
"instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, num_counters, index
|
||||
);
|
||||
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrProfIncrementIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_i32()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, num_counters, index];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
llty: &'ll Type,
|
||||
@ -1667,6 +1634,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
kcfi_bundle
|
||||
}
|
||||
|
||||
/// Emits a call to `llvm.instrprof.increment`. Used by coverage instrumentation.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
num_counters: &'ll Value,
|
||||
index: &'ll Value,
|
||||
) {
|
||||
self.call_intrinsic("llvm.instrprof.increment", &[fn_name, hash, num_counters, index]);
|
||||
}
|
||||
|
||||
/// Emits a call to `llvm.instrprof.mcdc.parameters`.
|
||||
///
|
||||
/// This doesn't produce any code directly, but is used as input by
|
||||
@ -1676,40 +1655,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
///
|
||||
/// [`CodeGenPGO::emitMCDCParameters`]:
|
||||
/// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_parameters(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
bitmap_bits: &'ll Value,
|
||||
) {
|
||||
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bits);
|
||||
|
||||
assert!(
|
||||
crate::llvm_util::get_version() >= (19, 0, 0),
|
||||
"MCDC intrinsics require LLVM 19 or later"
|
||||
);
|
||||
|
||||
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, bitmap_bits];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
}
|
||||
self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_tvbitmap_update(
|
||||
&mut self,
|
||||
fn_name: &'ll Value,
|
||||
@ -1717,39 +1677,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
bitmap_index: &'ll Value,
|
||||
mcdc_temp: &'ll Value,
|
||||
) {
|
||||
debug!(
|
||||
"mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?})",
|
||||
fn_name, hash, bitmap_index, mcdc_temp
|
||||
);
|
||||
assert!(
|
||||
crate::llvm_util::get_version() >= (19, 0, 0),
|
||||
"MCDC intrinsics require LLVM 19 or later"
|
||||
);
|
||||
|
||||
let llfn =
|
||||
unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) };
|
||||
let llty = self.cx.type_func(
|
||||
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_ptr()],
|
||||
self.cx.type_void(),
|
||||
);
|
||||
let args = &[fn_name, hash, bitmap_index, mcdc_temp];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
[].as_ptr(),
|
||||
0 as c_uint,
|
||||
);
|
||||
self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_condbitmap_reset(&mut self, mcdc_temp: &'ll Value) {
|
||||
self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) {
|
||||
debug!("mcdc_condbitmap_update() with args ({:?}, {:?})", cond_index, mcdc_temp);
|
||||
assert!(
|
||||
crate::llvm_util::get_version() >= (19, 0, 0),
|
||||
"MCDC intrinsics require LLVM 19 or later"
|
||||
|
@ -95,11 +95,10 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
|
||||
// whether we are sharing generics or not. The important thing here is
|
||||
// that the visibility we apply to the declaration is the same one that
|
||||
// has been applied to the definition (wherever that definition may be).
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage);
|
||||
|
||||
let is_generic =
|
||||
instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some();
|
||||
llvm::set_linkage(llfn, llvm::Linkage::ExternalLinkage);
|
||||
unsafe {
|
||||
let is_generic = instance.args.non_erasable_generics().next().is_some();
|
||||
|
||||
let is_hidden = if is_generic {
|
||||
// This is a monomorphization of a generic function.
|
||||
@ -136,7 +135,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
|
||||
|| !cx.tcx.is_reachable_non_generic(instance_def_id))
|
||||
};
|
||||
if is_hidden {
|
||||
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
|
||||
llvm::set_visibility(llfn, llvm::Visibility::Hidden);
|
||||
}
|
||||
|
||||
// MinGW: For backward compatibility we rely on the linker to decide whether it
|
||||
|
@ -219,8 +219,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
llvm::LLVMSetInitializer(g, sc);
|
||||
llvm::LLVMSetGlobalConstant(g, True);
|
||||
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
|
||||
llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage);
|
||||
}
|
||||
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
|
||||
(s.to_owned(), g)
|
||||
})
|
||||
.1;
|
||||
|
@ -172,10 +172,9 @@ fn check_and_apply_linkage<'ll, 'tcx>(
|
||||
if let Some(linkage) = attrs.import_linkage {
|
||||
debug!("get_static: sym={} linkage={:?}", sym, linkage);
|
||||
|
||||
unsafe {
|
||||
// Declare a symbol `foo` with the desired linkage.
|
||||
let g1 = cx.declare_global(sym, cx.type_i8());
|
||||
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
|
||||
llvm::set_linkage(g1, base::linkage_to_llvm(linkage));
|
||||
|
||||
// Declare an internal global `extern_with_linkage_foo` which
|
||||
// is initialized with the address of `foo`. If `foo` is
|
||||
@ -191,10 +190,9 @@ fn check_and_apply_linkage<'ll, 'tcx>(
|
||||
symbol_name: sym,
|
||||
})
|
||||
});
|
||||
llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage);
|
||||
llvm::LLVMSetInitializer(g2, g1);
|
||||
llvm::set_linkage(g2, llvm::Linkage::InternalLinkage);
|
||||
unsafe { llvm::LLVMSetInitializer(g2, g1) };
|
||||
g2
|
||||
}
|
||||
} else if cx.tcx.sess.target.arch == "x86"
|
||||
&& let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym)
|
||||
{
|
||||
@ -224,24 +222,22 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
unsafe {
|
||||
let gv = match kind {
|
||||
Some(kind) if !self.tcx.sess.fewer_names() => {
|
||||
let name = self.generate_local_symbol_name(kind);
|
||||
let gv = self.define_global(&name, self.val_ty(cv)).unwrap_or_else(|| {
|
||||
bug!("symbol `{}` is already defined", name);
|
||||
});
|
||||
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_linkage(gv, llvm::Linkage::PrivateLinkage);
|
||||
gv
|
||||
}
|
||||
_ => self.define_private_global(self.val_ty(cv)),
|
||||
};
|
||||
llvm::LLVMSetInitializer(gv, cv);
|
||||
unsafe { llvm::LLVMSetInitializer(gv, cv) };
|
||||
set_global_alignment(self, gv, align);
|
||||
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
|
||||
gv
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
|
||||
@ -292,9 +288,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
let g = self.declare_global(sym, llty);
|
||||
|
||||
if !self.tcx.is_reachable_non_generic(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden);
|
||||
}
|
||||
llvm::set_visibility(g, llvm::Visibility::Hidden);
|
||||
}
|
||||
|
||||
g
|
||||
@ -312,7 +306,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
}
|
||||
|
||||
let dso_local = unsafe { self.should_assume_dso_local(g, true) };
|
||||
let dso_local = self.should_assume_dso_local(g, true);
|
||||
if dso_local {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
@ -401,8 +395,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
let name = llvm::get_value_name(g).to_vec();
|
||||
llvm::set_value_name(g, b"");
|
||||
|
||||
let linkage = llvm::LLVMRustGetLinkage(g);
|
||||
let visibility = llvm::LLVMRustGetVisibility(g);
|
||||
let linkage = llvm::get_linkage(g);
|
||||
let visibility = llvm::get_visibility(g);
|
||||
|
||||
let new_g = llvm::LLVMRustGetOrInsertGlobal(
|
||||
self.llmod,
|
||||
@ -411,8 +405,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
val_llty,
|
||||
);
|
||||
|
||||
llvm::LLVMRustSetLinkage(new_g, linkage);
|
||||
llvm::LLVMRustSetVisibility(new_g, visibility);
|
||||
llvm::set_linkage(new_g, linkage);
|
||||
llvm::set_visibility(new_g, visibility);
|
||||
|
||||
// The old global has had its name removed but is returned by
|
||||
// get_static since it is in the instance cache. Provide an
|
||||
|
@ -80,6 +80,7 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {
|
||||
|
||||
pub isize_ty: &'ll Type,
|
||||
|
||||
/// Extra codegen state needed when coverage instrumentation is enabled.
|
||||
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
|
||||
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
|
||||
|
||||
@ -592,11 +593,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
&self.statics_to_rauw
|
||||
}
|
||||
|
||||
/// Extra state that is only available when coverage instrumentation is enabled.
|
||||
#[inline]
|
||||
pub(crate) fn coverage_context(
|
||||
&self,
|
||||
) -> Option<&coverageinfo::CrateCoverageContext<'ll, 'tcx>> {
|
||||
self.coverage_cx.as_ref()
|
||||
pub(crate) fn coverage_cx(&self) -> &coverageinfo::CrateCoverageContext<'ll, 'tcx> {
|
||||
self.coverage_cx.as_ref().expect("only called when coverage instrumentation is enabled")
|
||||
}
|
||||
|
||||
pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) {
|
||||
@ -605,7 +605,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
unsafe {
|
||||
let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr());
|
||||
llvm::LLVMSetInitializer(g, array);
|
||||
llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
|
||||
llvm::set_linkage(g, llvm::Linkage::AppendingLinkage);
|
||||
llvm::LLVMSetSection(g, c"llvm.metadata".as_ptr());
|
||||
}
|
||||
}
|
||||
@ -1099,6 +1099,10 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
|
||||
if self.sess().instrument_coverage() {
|
||||
ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void);
|
||||
if crate::llvm_util::get_version() >= (19, 0, 0) {
|
||||
ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void);
|
||||
ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void);
|
||||
}
|
||||
}
|
||||
|
||||
ifn!("llvm.type.test", fn(ptr, t_metadata) -> i1);
|
||||
|
@ -1,11 +1,9 @@
|
||||
use rustc_middle::mir::coverage::{
|
||||
ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind, SourceRegion,
|
||||
};
|
||||
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};
|
||||
|
||||
/// Must match the layout of `LLVMRustCounterKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum CounterKind {
|
||||
pub(crate) enum CounterKind {
|
||||
Zero = 0,
|
||||
CounterValueReference = 1,
|
||||
Expression = 2,
|
||||
@ -25,9 +23,9 @@ pub enum CounterKind {
|
||||
/// Must match the layout of `LLVMRustCounter`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Counter {
|
||||
pub(crate) struct Counter {
|
||||
// Important: The layout (order and types of fields) must match its C++ counterpart.
|
||||
pub kind: CounterKind,
|
||||
pub(crate) kind: CounterKind,
|
||||
id: u32,
|
||||
}
|
||||
|
||||
@ -36,7 +34,7 @@ impl Counter {
|
||||
pub(crate) const ZERO: Self = Self { kind: CounterKind::Zero, id: 0 };
|
||||
|
||||
/// Constructs a new `Counter` of kind `CounterValueReference`.
|
||||
pub fn counter_value_reference(counter_id: CounterId) -> Self {
|
||||
pub(crate) fn counter_value_reference(counter_id: CounterId) -> Self {
|
||||
Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() }
|
||||
}
|
||||
|
||||
@ -59,7 +57,7 @@ impl Counter {
|
||||
/// Must match the layout of `LLVMRustCounterExprKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum ExprKind {
|
||||
pub(crate) enum ExprKind {
|
||||
Subtract = 0,
|
||||
Add = 1,
|
||||
}
|
||||
@ -69,48 +67,13 @@ pub enum ExprKind {
|
||||
/// Must match the layout of `LLVMRustCounterExpression`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct CounterExpression {
|
||||
pub kind: ExprKind,
|
||||
pub lhs: Counter,
|
||||
pub rhs: Counter,
|
||||
pub(crate) struct CounterExpression {
|
||||
pub(crate) kind: ExprKind,
|
||||
pub(crate) lhs: Counter,
|
||||
pub(crate) rhs: Counter,
|
||||
}
|
||||
|
||||
/// Corresponds to enum `llvm::coverage::CounterMappingRegion::RegionKind`.
|
||||
///
|
||||
/// Must match the layout of `LLVMRustCounterMappingRegionKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
enum RegionKind {
|
||||
/// A CodeRegion associates some code with a counter
|
||||
CodeRegion = 0,
|
||||
|
||||
/// An ExpansionRegion represents a file expansion region that associates
|
||||
/// a source range with the expansion of a virtual source file, such as
|
||||
/// for a macro instantiation or #include file.
|
||||
ExpansionRegion = 1,
|
||||
|
||||
/// A SkippedRegion represents a source range with code that was skipped
|
||||
/// by a preprocessor or similar means.
|
||||
SkippedRegion = 2,
|
||||
|
||||
/// A GapRegion is like a CodeRegion, but its count is only set as the
|
||||
/// line execution count when its the only region in the line.
|
||||
GapRegion = 3,
|
||||
|
||||
/// A BranchRegion represents leaf-level boolean expressions and is
|
||||
/// associated with two counters, each representing the number of times the
|
||||
/// expression evaluates to true or false.
|
||||
BranchRegion = 4,
|
||||
|
||||
/// A DecisionRegion represents a top-level boolean expression and is
|
||||
/// associated with a variable length bitmap index and condition number.
|
||||
MCDCDecisionRegion = 5,
|
||||
|
||||
/// A Branch Region can be extended to include IDs to facilitate MC/DC.
|
||||
MCDCBranchRegion = 6,
|
||||
}
|
||||
|
||||
mod mcdc {
|
||||
pub(crate) mod mcdc {
|
||||
use rustc_middle::mir::coverage::{ConditionId, ConditionInfo, DecisionInfo};
|
||||
|
||||
/// Must match the layout of `LLVMRustMCDCDecisionParameters`.
|
||||
@ -121,8 +84,6 @@ mod mcdc {
|
||||
num_conditions: u16,
|
||||
}
|
||||
|
||||
// ConditionId in llvm is `unsigned int` at 18 while `int16_t` at
|
||||
// [19](https://github.com/llvm/llvm-project/pull/81257).
|
||||
type LLVMConditionId = i16;
|
||||
|
||||
/// Must match the layout of `LLVMRustMCDCBranchParameters`.
|
||||
@ -133,38 +94,6 @@ mod mcdc {
|
||||
condition_ids: [LLVMConditionId; 2],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum ParameterTag {
|
||||
None = 0,
|
||||
Decision = 1,
|
||||
Branch = 2,
|
||||
}
|
||||
/// Same layout with `LLVMRustMCDCParameters`
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct Parameters {
|
||||
tag: ParameterTag,
|
||||
decision_params: DecisionParameters,
|
||||
branch_params: BranchParameters,
|
||||
}
|
||||
|
||||
impl Parameters {
|
||||
pub(crate) fn none() -> Self {
|
||||
Self {
|
||||
tag: ParameterTag::None,
|
||||
decision_params: Default::default(),
|
||||
branch_params: Default::default(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn decision(decision_params: DecisionParameters) -> Self {
|
||||
Self { tag: ParameterTag::Decision, decision_params, branch_params: Default::default() }
|
||||
}
|
||||
pub(crate) fn branch(branch_params: BranchParameters) -> Self {
|
||||
Self { tag: ParameterTag::Branch, decision_params: Default::default(), branch_params }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConditionInfo> for BranchParameters {
|
||||
fn from(value: ConditionInfo) -> Self {
|
||||
let to_llvm_cond_id = |cond_id: Option<ConditionId>| {
|
||||
@ -186,267 +115,68 @@ mod mcdc {
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
|
||||
/// coverage map, in accordance with the
|
||||
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
|
||||
/// The struct composes fields representing the `Counter` type and value(s) (injected counter
|
||||
/// ID, or expression type and operands), the source file (an indirect index into a "filenames
|
||||
/// array", encoded separately), and source location (start and end positions of the represented
|
||||
/// code region).
|
||||
/// A span of source code coordinates to be embedded in coverage metadata.
|
||||
///
|
||||
/// Corresponds to struct `llvm::coverage::CounterMappingRegion`.
|
||||
///
|
||||
/// Must match the layout of `LLVMRustCounterMappingRegion`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
/// Must match the layout of `LLVMRustCoverageSpan`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct CounterMappingRegion {
|
||||
/// The counter type and type-dependent counter data, if any.
|
||||
counter: Counter,
|
||||
|
||||
/// If the `RegionKind` is a `BranchRegion`, this represents the counter
|
||||
/// for the false branch of the region.
|
||||
false_counter: Counter,
|
||||
|
||||
mcdc_params: mcdc::Parameters,
|
||||
/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
|
||||
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
|
||||
/// that, in turn, are used to look up the filename for this region.
|
||||
pub(crate) struct CoverageSpan {
|
||||
/// Local index into the function's local-to-global file ID table.
|
||||
/// The value at that index is itself an index into the coverage filename
|
||||
/// table in the CGU's `__llvm_covmap` section.
|
||||
file_id: u32,
|
||||
|
||||
/// If the `RegionKind` is an `ExpansionRegion`, the `expanded_file_id` can be used to find
|
||||
/// the mapping regions created as a result of macro expansion, by checking if their file id
|
||||
/// matches the expanded file id.
|
||||
expanded_file_id: u32,
|
||||
|
||||
/// 1-based starting line of the mapping region.
|
||||
/// 1-based starting line of the source code span.
|
||||
start_line: u32,
|
||||
|
||||
/// 1-based starting column of the mapping region.
|
||||
/// 1-based starting column of the source code span.
|
||||
start_col: u32,
|
||||
|
||||
/// 1-based ending line of the mapping region.
|
||||
/// 1-based ending line of the source code span.
|
||||
end_line: u32,
|
||||
|
||||
/// 1-based ending column of the mapping region. If the high bit is set, the current
|
||||
/// mapping region is a gap area.
|
||||
/// 1-based ending column of the source code span. High bit must be unset.
|
||||
end_col: u32,
|
||||
|
||||
kind: RegionKind,
|
||||
}
|
||||
|
||||
impl CounterMappingRegion {
|
||||
pub(crate) fn from_mapping(
|
||||
mapping_kind: &MappingKind,
|
||||
local_file_id: u32,
|
||||
source_region: &SourceRegion,
|
||||
) -> Self {
|
||||
let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } =
|
||||
source_region;
|
||||
match *mapping_kind {
|
||||
MappingKind::Code(term) => Self::code_region(
|
||||
Counter::from_term(term),
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
),
|
||||
MappingKind::Branch { true_term, false_term } => Self::branch_region(
|
||||
Counter::from_term(true_term),
|
||||
Counter::from_term(false_term),
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
),
|
||||
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
|
||||
Self::mcdc_branch_region(
|
||||
Counter::from_term(true_term),
|
||||
Counter::from_term(false_term),
|
||||
mcdc_params,
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
)
|
||||
}
|
||||
MappingKind::MCDCDecision(decision_info) => Self::decision_region(
|
||||
decision_info,
|
||||
local_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn code_region(
|
||||
counter: Counter,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::CodeRegion,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn branch_region(
|
||||
counter: Counter,
|
||||
false_counter: Counter,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter,
|
||||
false_counter,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::BranchRegion,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_branch_region(
|
||||
counter: Counter,
|
||||
false_counter: Counter,
|
||||
condition_info: ConditionInfo,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter,
|
||||
false_counter,
|
||||
mcdc_params: mcdc::Parameters::branch(condition_info.into()),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::MCDCBranchRegion,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn decision_region(
|
||||
decision_info: DecisionInfo,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
let mcdc_params = mcdc::Parameters::decision(decision_info.into());
|
||||
|
||||
Self {
|
||||
counter: Counter::ZERO,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params,
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::MCDCDecisionRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// This function might be used in the future; the LLVM API is still evolving, as is coverage
|
||||
// support.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn expansion_region(
|
||||
file_id: u32,
|
||||
expanded_file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter: Counter::ZERO,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::ExpansionRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// This function might be used in the future; the LLVM API is still evolving, as is coverage
|
||||
// support.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn skipped_region(
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter: Counter::ZERO,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col,
|
||||
kind: RegionKind::SkippedRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// This function might be used in the future; the LLVM API is still evolving, as is coverage
|
||||
// support.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn gap_region(
|
||||
counter: Counter,
|
||||
file_id: u32,
|
||||
start_line: u32,
|
||||
start_col: u32,
|
||||
end_line: u32,
|
||||
end_col: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
counter,
|
||||
false_counter: Counter::ZERO,
|
||||
mcdc_params: mcdc::Parameters::none(),
|
||||
file_id,
|
||||
expanded_file_id: 0,
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
end_col: (1_u32 << 31) | end_col,
|
||||
kind: RegionKind::GapRegion,
|
||||
}
|
||||
impl CoverageSpan {
|
||||
pub(crate) fn from_source_region(file_id: u32, code_region: &SourceRegion) -> Self {
|
||||
let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region;
|
||||
// Internally, LLVM uses the high bit of `end_col` to distinguish between
|
||||
// code regions and gap regions, so it can't be used by the column number.
|
||||
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");
|
||||
Self { file_id, start_line, start_col, end_line, end_col }
|
||||
}
|
||||
}
|
||||
|
||||
/// Must match the layout of `LLVMRustCoverageCodeRegion`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct CodeRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) counter: Counter,
|
||||
}
|
||||
|
||||
/// Must match the layout of `LLVMRustCoverageBranchRegion`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct BranchRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) true_counter: Counter,
|
||||
pub(crate) false_counter: Counter,
|
||||
}
|
||||
|
||||
/// Must match the layout of `LLVMRustCoverageMCDCBranchRegion`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MCDCBranchRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) true_counter: Counter,
|
||||
pub(crate) false_counter: Counter,
|
||||
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
|
||||
}
|
||||
|
||||
/// Must match the layout of `LLVMRustCoverageMCDCDecisionRegion`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MCDCDecisionRegion {
|
||||
pub(crate) span: CoverageSpan,
|
||||
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
|
||||
}
|
||||
|
@ -1,18 +1,23 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
|
||||
use rustc_abi::Align;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::coverage::MappingKind;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::def_id::DefIdSet;
|
||||
use rustc_target::spec::HasTargetSpec;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::ffi::CounterMappingRegion;
|
||||
use crate::coverageinfo::ffi;
|
||||
use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
|
||||
use crate::{coverageinfo, llvm};
|
||||
|
||||
@ -49,11 +54,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
add_unused_functions(cx);
|
||||
}
|
||||
|
||||
let function_coverage_map = match cx.coverage_context() {
|
||||
Some(ctx) => ctx.take_function_coverage_map(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
let function_coverage_map = cx.coverage_cx().take_function_coverage_map();
|
||||
if function_coverage_map.is_empty() {
|
||||
// This module has no functions with coverage instrumentation
|
||||
return;
|
||||
@ -77,11 +78,9 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
|
||||
// Generate the coverage map header, which contains the filenames used by
|
||||
// this CGU's coverage mappings, and store it in a well-known global.
|
||||
let cov_data_val = generate_coverage_map(cx, covmap_version, filenames_size, filenames_val);
|
||||
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
|
||||
generate_covmap_record(cx, covmap_version, filenames_size, filenames_val);
|
||||
|
||||
let mut unused_function_names = Vec::new();
|
||||
let covfun_section_name = coverageinfo::covfun_section_name(cx);
|
||||
|
||||
// Encode coverage mappings and generate function records
|
||||
for (instance, function_coverage) in function_coverage_entries {
|
||||
@ -110,9 +109,8 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
||||
unused_function_names.push(mangled_function_name);
|
||||
}
|
||||
|
||||
save_function_record(
|
||||
generate_covfun_record(
|
||||
cx,
|
||||
&covfun_section_name,
|
||||
mangled_function_name,
|
||||
source_hash,
|
||||
filenames_ref,
|
||||
@ -237,7 +235,10 @@ fn encode_mappings_for_function(
|
||||
let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
|
||||
|
||||
let mut virtual_file_mapping = VirtualFileMapping::default();
|
||||
let mut mapping_regions = Vec::with_capacity(counter_regions.len());
|
||||
let mut code_regions = vec![];
|
||||
let mut branch_regions = vec![];
|
||||
let mut mcdc_branch_regions = vec![];
|
||||
let mut mcdc_decision_regions = vec![];
|
||||
|
||||
// Group mappings into runs with the same filename, preserving the order
|
||||
// yielded by `FunctionCoverage`.
|
||||
@ -257,11 +258,36 @@ fn encode_mappings_for_function(
|
||||
// form suitable for FFI.
|
||||
for (mapping_kind, region) in counter_regions_for_file {
|
||||
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
|
||||
mapping_regions.push(CounterMappingRegion::from_mapping(
|
||||
&mapping_kind,
|
||||
local_file_id.as_u32(),
|
||||
region,
|
||||
));
|
||||
let span = ffi::CoverageSpan::from_source_region(local_file_id.as_u32(), region);
|
||||
match mapping_kind {
|
||||
MappingKind::Code(term) => {
|
||||
code_regions
|
||||
.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
|
||||
}
|
||||
MappingKind::Branch { true_term, false_term } => {
|
||||
branch_regions.push(ffi::BranchRegion {
|
||||
span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
|
||||
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
|
||||
span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCDecision(mcdc_decision_params) => {
|
||||
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
|
||||
span,
|
||||
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(
|
||||
mcdc_decision_params,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,21 +296,24 @@ fn encode_mappings_for_function(
|
||||
coverageinfo::write_mapping_to_buffer(
|
||||
virtual_file_mapping.into_vec(),
|
||||
expressions,
|
||||
mapping_regions,
|
||||
&code_regions,
|
||||
&branch_regions,
|
||||
&mcdc_branch_regions,
|
||||
&mcdc_decision_regions,
|
||||
buffer,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct coverage map header and the array of function records, and combine them into the
|
||||
/// coverage map. Save the coverage map data into the LLVM IR as a static global using a
|
||||
/// specific, well-known section and name.
|
||||
fn generate_coverage_map<'ll>(
|
||||
/// Generates the contents of the covmap record for this CGU, which mostly
|
||||
/// consists of a header and a list of filenames. The record is then stored
|
||||
/// as a global variable in the `__llvm_covmap` section.
|
||||
fn generate_covmap_record<'ll>(
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
version: u32,
|
||||
filenames_size: usize,
|
||||
filenames_val: &'ll llvm::Value,
|
||||
) -> &'ll llvm::Value {
|
||||
) {
|
||||
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
|
||||
|
||||
// Create the coverage data header (Note, fields 0 and 2 are now always zero,
|
||||
@ -299,15 +328,37 @@ fn generate_coverage_map<'ll>(
|
||||
);
|
||||
|
||||
// Create the complete LLVM coverage data value to add to the LLVM IR
|
||||
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
|
||||
let covmap_data =
|
||||
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false);
|
||||
|
||||
let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
|
||||
llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
|
||||
}))
|
||||
.unwrap();
|
||||
debug!("covmap var name: {:?}", covmap_var_name);
|
||||
|
||||
let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
|
||||
llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s);
|
||||
}))
|
||||
.expect("covmap section name should not contain NUL");
|
||||
debug!("covmap section name: {:?}", covmap_section_name);
|
||||
|
||||
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &covmap_var_name);
|
||||
llvm::set_initializer(llglobal, covmap_data);
|
||||
llvm::set_global_constant(llglobal, true);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_section(llglobal, &covmap_section_name);
|
||||
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
|
||||
// <https://llvm.org/docs/CoverageMappingFormat.html>
|
||||
llvm::set_alignment(llglobal, Align::EIGHT);
|
||||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
||||
/// Construct a function record and combine it with the function's coverage mapping data.
|
||||
/// Save the function record into the LLVM IR as a static global using a
|
||||
/// specific, well-known section and name.
|
||||
fn save_function_record(
|
||||
/// Generates the contents of the covfun record for this function, which
|
||||
/// contains the function's coverage mapping data. The record is then stored
|
||||
/// as a global variable in the `__llvm_covfun` section.
|
||||
fn generate_covfun_record(
|
||||
cx: &CodegenCx<'_, '_>,
|
||||
covfun_section_name: &CStr,
|
||||
mangled_function_name: &str,
|
||||
source_hash: u64,
|
||||
filenames_ref: u64,
|
||||
@ -334,13 +385,28 @@ fn save_function_record(
|
||||
/*packed=*/ true,
|
||||
);
|
||||
|
||||
coverageinfo::save_func_record_to_mod(
|
||||
cx,
|
||||
covfun_section_name,
|
||||
func_name_hash,
|
||||
func_record_val,
|
||||
is_used,
|
||||
);
|
||||
// Choose a variable name to hold this function's covfun data.
|
||||
// Functions that are used have a suffix ("u") to distinguish them from
|
||||
// unused copies of the same function (from different CGUs), so that if a
|
||||
// linker sees both it won't discard the used copy's data.
|
||||
let func_record_var_name =
|
||||
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
|
||||
.unwrap();
|
||||
debug!("function record var name: {:?}", func_record_var_name);
|
||||
|
||||
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
|
||||
llvm::set_initializer(llglobal, func_record_val);
|
||||
llvm::set_global_constant(llglobal, true);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
|
||||
llvm::set_section(llglobal, cx.covfun_section_name());
|
||||
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
|
||||
// <https://llvm.org/docs/CoverageMappingFormat.html>
|
||||
llvm::set_alignment(llglobal, Align::EIGHT);
|
||||
if cx.target_spec().supports_comdat() {
|
||||
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
|
||||
}
|
||||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
||||
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
|
||||
@ -472,9 +538,5 @@ fn add_unused_function_coverage<'tcx>(
|
||||
// zero, because none of its counters/expressions are marked as seen.
|
||||
let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info);
|
||||
|
||||
if let Some(coverage_context) = cx.coverage_context() {
|
||||
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
|
||||
} else {
|
||||
bug!("Could not get the `coverage_context`");
|
||||
}
|
||||
cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage);
|
||||
}
|
||||
|
@ -1,24 +1,20 @@
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods,
|
||||
MiscCodegenMethods, StaticCodegenMethods,
|
||||
BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_llvm::RustString;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::coverage::CoverageKind;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::spec::HasTargetSpec;
|
||||
use rustc_target::abi::Size;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
|
||||
use crate::coverageinfo::map_data::FunctionCoverageCollector;
|
||||
use crate::llvm;
|
||||
|
||||
@ -33,6 +29,8 @@ pub(crate) struct CrateCoverageContext<'ll, 'tcx> {
|
||||
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
|
||||
|
||||
covfun_section_name: OnceCell<CString>,
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
@ -41,6 +39,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
function_coverage_map: Default::default(),
|
||||
pgo_func_name_var_map: Default::default(),
|
||||
mcdc_condition_bitmap_map: Default::default(),
|
||||
covfun_section_name: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,27 +66,38 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// These methods used to be part of trait `CoverageInfoMethods`, which no longer
|
||||
// exists after most coverage code was moved out of SSA.
|
||||
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
pub(crate) fn coverageinfo_finalize(&self) {
|
||||
mapgen::finalize(self)
|
||||
}
|
||||
|
||||
/// Returns the section name to use when embedding per-function coverage information
|
||||
/// in the object file, according to the target's object file format. LLVM's coverage
|
||||
/// tools use information from this section when producing coverage reports.
|
||||
///
|
||||
/// Typical values are:
|
||||
/// - `__llvm_covfun` on Linux
|
||||
/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
|
||||
/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
|
||||
fn covfun_section_name(&self) -> &CStr {
|
||||
self.coverage_cx().covfun_section_name.get_or_init(|| {
|
||||
CString::new(llvm::build_byte_buffer(|s| unsafe {
|
||||
llvm::LLVMRustCoverageWriteFuncSectionNameToString(self.llmod, s);
|
||||
}))
|
||||
.expect("covfun section name should not contain NUL")
|
||||
})
|
||||
}
|
||||
|
||||
/// For LLVM codegen, returns a function-specific `Value` for a global
|
||||
/// string, to hold the function name passed to LLVM intrinsic
|
||||
/// `instrprof.increment()`. The `Value` is only created once per instance.
|
||||
/// Multiple invocations with the same instance return the same `Value`.
|
||||
fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
|
||||
if let Some(coverage_context) = self.coverage_context() {
|
||||
debug!("getting pgo_func_name_var for instance={:?}", instance);
|
||||
let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut();
|
||||
let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut();
|
||||
pgo_func_name_var_map
|
||||
.entry(instance)
|
||||
.or_insert_with(|| create_pgo_func_name_var(self, instance))
|
||||
} else {
|
||||
bug!("Could not get the `coverage_context`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,11 +131,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
cond_bitmaps.push(cond_bitmap);
|
||||
}
|
||||
|
||||
self.coverage_context()
|
||||
.expect("always present when coverage is enabled")
|
||||
.mcdc_condition_bitmap_map
|
||||
.borrow_mut()
|
||||
.insert(instance, cond_bitmaps);
|
||||
self.coverage_cx().mcdc_condition_bitmap_map.borrow_mut().insert(instance, cond_bitmaps);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
@ -146,8 +152,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(coverage_context) = bx.coverage_context() else { return };
|
||||
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
||||
let mut coverage_map = bx.coverage_cx().function_coverage_map.borrow_mut();
|
||||
let func_coverage = coverage_map
|
||||
.entry(instance)
|
||||
.or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info));
|
||||
@ -189,7 +194,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
}
|
||||
CoverageKind::CondBitmapUpdate { index, decision_depth } => {
|
||||
drop(coverage_map);
|
||||
let cond_bitmap = coverage_context
|
||||
let cond_bitmap = bx
|
||||
.coverage_cx()
|
||||
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
|
||||
.expect("mcdc cond bitmap should have been allocated for updating");
|
||||
let cond_index = bx.const_i32(index as i32);
|
||||
@ -197,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
}
|
||||
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
|
||||
drop(coverage_map);
|
||||
let cond_bitmap = coverage_context
|
||||
let cond_bitmap = bx.coverage_cx()
|
||||
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
|
||||
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
|
||||
assert!(
|
||||
@ -209,6 +215,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_index = bx.const_u32(bitmap_idx);
|
||||
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
|
||||
bx.mcdc_condbitmap_reset(cond_bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -257,8 +264,11 @@ pub(crate) fn write_filenames_section_to_buffer<'a>(
|
||||
|
||||
pub(crate) fn write_mapping_to_buffer(
|
||||
virtual_file_mapping: Vec<u32>,
|
||||
expressions: Vec<CounterExpression>,
|
||||
mapping_regions: Vec<CounterMappingRegion>,
|
||||
expressions: Vec<ffi::CounterExpression>,
|
||||
code_regions: &[ffi::CodeRegion],
|
||||
branch_regions: &[ffi::BranchRegion],
|
||||
mcdc_branch_regions: &[ffi::MCDCBranchRegion],
|
||||
mcdc_decision_regions: &[ffi::MCDCDecisionRegion],
|
||||
buffer: &RustString,
|
||||
) {
|
||||
unsafe {
|
||||
@ -267,8 +277,14 @@ pub(crate) fn write_mapping_to_buffer(
|
||||
virtual_file_mapping.len() as c_uint,
|
||||
expressions.as_ptr(),
|
||||
expressions.len() as c_uint,
|
||||
mapping_regions.as_ptr(),
|
||||
mapping_regions.len() as c_uint,
|
||||
code_regions.as_ptr(),
|
||||
code_regions.len() as c_uint,
|
||||
branch_regions.as_ptr(),
|
||||
branch_regions.len() as c_uint,
|
||||
mcdc_branch_regions.as_ptr(),
|
||||
mcdc_branch_regions.len() as c_uint,
|
||||
mcdc_decision_regions.as_ptr(),
|
||||
mcdc_decision_regions.len() as c_uint,
|
||||
buffer,
|
||||
);
|
||||
}
|
||||
@ -281,82 +297,3 @@ pub(crate) fn hash_bytes(bytes: &[u8]) -> u64 {
|
||||
pub(crate) fn mapping_version() -> u32 {
|
||||
unsafe { llvm::LLVMRustCoverageMappingVersion() }
|
||||
}
|
||||
|
||||
pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
cov_data_val: &'ll llvm::Value,
|
||||
) {
|
||||
let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
|
||||
llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
|
||||
}))
|
||||
.unwrap();
|
||||
debug!("covmap var name: {:?}", covmap_var_name);
|
||||
|
||||
let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
|
||||
llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s);
|
||||
}))
|
||||
.expect("covmap section name should not contain NUL");
|
||||
debug!("covmap section name: {:?}", covmap_section_name);
|
||||
|
||||
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name);
|
||||
llvm::set_initializer(llglobal, cov_data_val);
|
||||
llvm::set_global_constant(llglobal, true);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||
llvm::set_section(llglobal, &covmap_section_name);
|
||||
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
|
||||
llvm::set_alignment(llglobal, Align::EIGHT);
|
||||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
||||
pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
covfun_section_name: &CStr,
|
||||
func_name_hash: u64,
|
||||
func_record_val: &'ll llvm::Value,
|
||||
is_used: bool,
|
||||
) {
|
||||
// Assign a name to the function record. This is used to merge duplicates.
|
||||
//
|
||||
// In LLVM, a "translation unit" (effectively, a `Crate` in Rust) can describe functions that
|
||||
// are included-but-not-used. If (or when) Rust generates functions that are
|
||||
// included-but-not-used, note that a dummy description for a function included-but-not-used
|
||||
// in a Crate can be replaced by full description provided by a different Crate. The two kinds
|
||||
// of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by
|
||||
// appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging.
|
||||
let func_record_var_name =
|
||||
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
|
||||
.unwrap();
|
||||
debug!("function record var name: {:?}", func_record_var_name);
|
||||
debug!("function record section name: {:?}", covfun_section_name);
|
||||
|
||||
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
|
||||
llvm::set_initializer(llglobal, func_record_val);
|
||||
llvm::set_global_constant(llglobal, true);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
|
||||
llvm::set_section(llglobal, covfun_section_name);
|
||||
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
|
||||
llvm::set_alignment(llglobal, Align::EIGHT);
|
||||
if cx.target_spec().supports_comdat() {
|
||||
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
|
||||
}
|
||||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
||||
/// Returns the section name string to pass through to the linker when embedding
|
||||
/// per-function coverage information in the object file, according to the target
|
||||
/// platform's object file format.
|
||||
///
|
||||
/// LLVM's coverage tools read coverage mapping details from this section when
|
||||
/// producing coverage reports.
|
||||
///
|
||||
/// Typical values are:
|
||||
/// - `__llvm_covfun` on Linux
|
||||
/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
|
||||
/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
|
||||
pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> CString {
|
||||
CString::new(llvm::build_byte_buffer(|s| unsafe {
|
||||
llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
|
||||
}))
|
||||
.expect("covfun section name should not contain NUL")
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>(
|
||||
llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents));
|
||||
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
|
||||
llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global);
|
||||
llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::set_linkage(section_var, llvm::Linkage::LinkOnceODRLinkage);
|
||||
// This should make sure that the whole section is not larger than
|
||||
// the string it contains. Otherwise we get a warning from GDB.
|
||||
llvm::LLVMSetAlignment(section_var, 1);
|
||||
|
@ -350,7 +350,6 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
type_names::push_generic_params(
|
||||
tcx,
|
||||
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args),
|
||||
enclosing_fn_def_id,
|
||||
&mut name,
|
||||
);
|
||||
|
||||
|
@ -785,13 +785,12 @@ fn codegen_msvc_try<'ll>(
|
||||
let type_info =
|
||||
bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false);
|
||||
let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
||||
|
||||
llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
||||
if bx.cx.tcx.sess.target.supports_comdat() {
|
||||
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
||||
}
|
||||
llvm::LLVMSetInitializer(tydesc, type_info);
|
||||
}
|
||||
unsafe { llvm::LLVMSetInitializer(tydesc, type_info) };
|
||||
|
||||
// The flag value of 8 indicates that we are catching the exception by
|
||||
// reference instead of by value. We can't use catch by value because
|
||||
@ -1064,7 +1063,7 @@ fn gen_fn<'ll, 'tcx>(
|
||||
cx.set_frame_pointer_type(llfn);
|
||||
cx.apply_target_cpu_attr(llfn);
|
||||
// FIXME(eddyb) find a nicer way to do this.
|
||||
unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
|
||||
llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage);
|
||||
let llbb = Builder::append_block(cx, llfn, "entry-block");
|
||||
let bx = Builder::build(cx, llbb);
|
||||
codegen(bx);
|
||||
|
@ -1,9 +1,11 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
|
||||
use rustc_macros::TryFromU32;
|
||||
use rustc_target::spec::SymbolVisibility;
|
||||
|
||||
use super::RustString;
|
||||
@ -19,6 +21,30 @@ pub type Bool = c_uint;
|
||||
pub const True: Bool = 1 as Bool;
|
||||
pub const False: Bool = 0 as Bool;
|
||||
|
||||
/// Wrapper for a raw enum value returned from LLVM's C APIs.
|
||||
///
|
||||
/// For C enums returned by LLVM, it's risky to use a Rust enum as the return
|
||||
/// type, because it would be UB if a later version of LLVM adds a new enum
|
||||
/// value and returns it. Instead, return this raw wrapper, then convert to the
|
||||
/// Rust-side enum explicitly.
|
||||
#[repr(transparent)]
|
||||
pub struct RawEnum<T> {
|
||||
value: u32,
|
||||
/// We don't own or consume a `T`, but we can produce one.
|
||||
_rust_side_type: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<T: TryFrom<u32>> RawEnum<T> {
|
||||
#[track_caller]
|
||||
pub(crate) fn to_rust(self) -> T
|
||||
where
|
||||
T::Error: Debug,
|
||||
{
|
||||
// If this fails, the Rust-side enum is out of sync with LLVM's enum.
|
||||
T::try_from(self.value).expect("enum value returned by LLVM should be known")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)] // Variants constructed by C++.
|
||||
@ -108,26 +134,36 @@ pub enum CallConv {
|
||||
AvrInterrupt = 85,
|
||||
}
|
||||
|
||||
/// LLVMRustLinkage
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
/// Must match the layout of `LLVMLinkage`.
|
||||
#[derive(Copy, Clone, PartialEq, TryFromU32)]
|
||||
#[repr(C)]
|
||||
pub enum Linkage {
|
||||
ExternalLinkage = 0,
|
||||
AvailableExternallyLinkage = 1,
|
||||
LinkOnceAnyLinkage = 2,
|
||||
LinkOnceODRLinkage = 3,
|
||||
WeakAnyLinkage = 4,
|
||||
WeakODRLinkage = 5,
|
||||
AppendingLinkage = 6,
|
||||
InternalLinkage = 7,
|
||||
PrivateLinkage = 8,
|
||||
ExternalWeakLinkage = 9,
|
||||
CommonLinkage = 10,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
LinkOnceODRAutoHideLinkage = 4,
|
||||
WeakAnyLinkage = 5,
|
||||
WeakODRLinkage = 6,
|
||||
AppendingLinkage = 7,
|
||||
InternalLinkage = 8,
|
||||
PrivateLinkage = 9,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
DLLImportLinkage = 10,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
DLLExportLinkage = 11,
|
||||
ExternalWeakLinkage = 12,
|
||||
#[deprecated = "marked obsolete by LLVM"]
|
||||
GhostLinkage = 13,
|
||||
CommonLinkage = 14,
|
||||
LinkerPrivateLinkage = 15,
|
||||
LinkerPrivateWeakLinkage = 16,
|
||||
}
|
||||
|
||||
// LLVMRustVisibility
|
||||
/// Must match the layout of `LLVMVisibility`.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq, TryFromU32)]
|
||||
pub enum Visibility {
|
||||
Default = 0,
|
||||
Hidden = 1,
|
||||
@ -945,7 +981,11 @@ unsafe extern "C" {
|
||||
|
||||
// Operations on global variables, functions, and aliases (globals)
|
||||
pub fn LLVMIsDeclaration(Global: &Value) -> Bool;
|
||||
pub fn LLVMGetLinkage(Global: &Value) -> RawEnum<Linkage>;
|
||||
pub fn LLVMSetLinkage(Global: &Value, RustLinkage: Linkage);
|
||||
pub fn LLVMSetSection(Global: &Value, Section: *const c_char);
|
||||
pub fn LLVMGetVisibility(Global: &Value) -> RawEnum<Visibility>;
|
||||
pub fn LLVMSetVisibility(Global: &Value, Viz: Visibility);
|
||||
pub fn LLVMGetAlignment(Global: &Value) -> c_uint;
|
||||
pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint);
|
||||
pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass);
|
||||
@ -1521,10 +1561,6 @@ unsafe extern "C" {
|
||||
) -> bool;
|
||||
|
||||
// Operations on global variables, functions, and aliases (globals)
|
||||
pub fn LLVMRustGetLinkage(Global: &Value) -> Linkage;
|
||||
pub fn LLVMRustSetLinkage(Global: &Value, RustLinkage: Linkage);
|
||||
pub fn LLVMRustGetVisibility(Global: &Value) -> Visibility;
|
||||
pub fn LLVMRustSetVisibility(Global: &Value, Viz: Visibility);
|
||||
pub fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool);
|
||||
|
||||
// Operations on global variables
|
||||
@ -1615,10 +1651,6 @@ unsafe extern "C" {
|
||||
pub fn LLVMRustSetAllowReassoc(Instr: &Value);
|
||||
|
||||
// Miscellaneous instructions
|
||||
pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value;
|
||||
pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value;
|
||||
|
||||
pub fn LLVMRustBuildCall<'a>(
|
||||
B: &Builder<'a>,
|
||||
Ty: &'a Type,
|
||||
@ -1744,7 +1776,7 @@ unsafe extern "C" {
|
||||
) -> bool;
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn LLVMRustCoverageWriteFilenamesSectionToBuffer(
|
||||
pub(crate) fn LLVMRustCoverageWriteFilenamesSectionToBuffer(
|
||||
Filenames: *const *const c_char,
|
||||
FilenamesLen: size_t,
|
||||
Lengths: *const size_t,
|
||||
@ -1753,33 +1785,39 @@ unsafe extern "C" {
|
||||
);
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn LLVMRustCoverageWriteMappingToBuffer(
|
||||
pub(crate) fn LLVMRustCoverageWriteMappingToBuffer(
|
||||
VirtualFileMappingIDs: *const c_uint,
|
||||
NumVirtualFileMappingIDs: c_uint,
|
||||
Expressions: *const crate::coverageinfo::ffi::CounterExpression,
|
||||
NumExpressions: c_uint,
|
||||
MappingRegions: *const crate::coverageinfo::ffi::CounterMappingRegion,
|
||||
NumMappingRegions: c_uint,
|
||||
CodeRegions: *const crate::coverageinfo::ffi::CodeRegion,
|
||||
NumCodeRegions: c_uint,
|
||||
BranchRegions: *const crate::coverageinfo::ffi::BranchRegion,
|
||||
NumBranchRegions: c_uint,
|
||||
MCDCBranchRegions: *const crate::coverageinfo::ffi::MCDCBranchRegion,
|
||||
NumMCDCBranchRegions: c_uint,
|
||||
MCDCDecisionRegions: *const crate::coverageinfo::ffi::MCDCDecisionRegion,
|
||||
NumMCDCDecisionRegions: c_uint,
|
||||
BufferOut: &RustString,
|
||||
);
|
||||
|
||||
pub fn LLVMRustCoverageCreatePGOFuncNameVar(
|
||||
pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar(
|
||||
F: &Value,
|
||||
FuncName: *const c_char,
|
||||
FuncNameLen: size_t,
|
||||
) -> &Value;
|
||||
pub fn LLVMRustCoverageHashByteArray(Bytes: *const c_char, NumBytes: size_t) -> u64;
|
||||
pub(crate) fn LLVMRustCoverageHashByteArray(Bytes: *const c_char, NumBytes: size_t) -> u64;
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn LLVMRustCoverageWriteMapSectionNameToString(M: &Module, Str: &RustString);
|
||||
pub(crate) fn LLVMRustCoverageWriteMapSectionNameToString(M: &Module, Str: &RustString);
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn LLVMRustCoverageWriteFuncSectionNameToString(M: &Module, Str: &RustString);
|
||||
pub(crate) fn LLVMRustCoverageWriteFuncSectionNameToString(M: &Module, Str: &RustString);
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
pub fn LLVMRustCoverageWriteMappingVarNameToString(Str: &RustString);
|
||||
pub(crate) fn LLVMRustCoverageWriteMappingVarNameToString(Str: &RustString);
|
||||
|
||||
pub fn LLVMRustCoverageMappingVersion() -> u32;
|
||||
pub(crate) fn LLVMRustCoverageMappingVersion() -> u32;
|
||||
pub fn LLVMRustDebugMetadataVersion() -> u32;
|
||||
pub fn LLVMRustVersionMajor() -> u32;
|
||||
pub fn LLVMRustVersionMinor() -> u32;
|
||||
|
@ -232,15 +232,23 @@ pub fn set_global_constant(llglobal: &Value, is_constant: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_linkage(llglobal: &Value) -> Linkage {
|
||||
unsafe { LLVMGetLinkage(llglobal) }.to_rust()
|
||||
}
|
||||
|
||||
pub fn set_linkage(llglobal: &Value, linkage: Linkage) {
|
||||
unsafe {
|
||||
LLVMRustSetLinkage(llglobal, linkage);
|
||||
LLVMSetLinkage(llglobal, linkage);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_visibility(llglobal: &Value) -> Visibility {
|
||||
unsafe { LLVMGetVisibility(llglobal) }.to_rust()
|
||||
}
|
||||
|
||||
pub fn set_visibility(llglobal: &Value, visibility: Visibility) {
|
||||
unsafe {
|
||||
LLVMRustSetVisibility(llglobal, visibility);
|
||||
LLVMSetVisibility(llglobal, visibility);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,6 +248,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
||||
("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
|
||||
("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
|
||||
("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
|
||||
("aarch64", "pauth-lr") if get_version().0 < 19 => None,
|
||||
// Before LLVM 20 those two features were packaged together as b16b16
|
||||
("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
|
||||
("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
|
||||
|
@ -39,9 +39,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
.emit_fatal(SymbolAlreadyDefined { span: self.tcx.def_span(def_id), symbol_name })
|
||||
});
|
||||
|
||||
llvm::set_linkage(g, base::linkage_to_llvm(linkage));
|
||||
llvm::set_visibility(g, base::visibility_to_llvm(visibility));
|
||||
unsafe {
|
||||
llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage));
|
||||
llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility));
|
||||
if self.should_assume_dso_local(g, false) {
|
||||
llvm::LLVMRustSetDSOLocal(g, true);
|
||||
}
|
||||
@ -61,7 +61,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
|
||||
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
|
||||
unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) };
|
||||
llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
|
||||
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
|
||||
base::set_link_section(lldecl, attrs);
|
||||
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
|
||||
@ -78,21 +78,15 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
&& linkage != Linkage::Private
|
||||
&& self.tcx.is_compiler_builtins(LOCAL_CRATE)
|
||||
{
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden);
|
||||
}
|
||||
llvm::set_visibility(lldecl, llvm::Visibility::Hidden);
|
||||
} else {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility));
|
||||
}
|
||||
llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility));
|
||||
}
|
||||
|
||||
debug!("predefine_fn: instance = {:?}", instance);
|
||||
|
||||
unsafe {
|
||||
if self.should_assume_dso_local(lldecl, false) {
|
||||
llvm::LLVMRustSetDSOLocal(lldecl, true);
|
||||
}
|
||||
unsafe { llvm::LLVMRustSetDSOLocal(lldecl, true) };
|
||||
}
|
||||
|
||||
self.instances.borrow_mut().insert(instance, lldecl);
|
||||
@ -102,13 +96,13 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
impl CodegenCx<'_, '_> {
|
||||
/// Whether a definition or declaration can be assumed to be local to a group of
|
||||
/// libraries that form a single DSO or executable.
|
||||
pub(crate) unsafe fn should_assume_dso_local(
|
||||
pub(crate) fn should_assume_dso_local(
|
||||
&self,
|
||||
llval: &llvm::Value,
|
||||
is_declaration: bool,
|
||||
) -> bool {
|
||||
let linkage = unsafe { llvm::LLVMRustGetLinkage(llval) };
|
||||
let visibility = unsafe { llvm::LLVMRustGetVisibility(llval) };
|
||||
let linkage = llvm::get_linkage(llval);
|
||||
let visibility = llvm::get_visibility(llval);
|
||||
|
||||
if matches!(linkage, llvm::Linkage::InternalLinkage | llvm::Linkage::PrivateLinkage) {
|
||||
return true;
|
||||
|
@ -191,7 +191,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
||||
/// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this
|
||||
/// is useful for indexing slices, as `&[T]`'s data pointer is `T*`.
|
||||
/// If the type is an unsized struct, the regular layout is generated,
|
||||
/// with the inner-most trailing unsized field using the "minimal unit"
|
||||
/// with the innermost trailing unsized field using the "minimal unit"
|
||||
/// of that field's type - this is useful for taking the address of
|
||||
/// that field and ensuring the struct has the right alignment.
|
||||
fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type {
|
||||
|
@ -312,7 +312,7 @@ fn exported_symbols_provider_local(
|
||||
|
||||
match *mono_item {
|
||||
MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => {
|
||||
if args.non_erasable_generics(tcx, def).next().is_some() {
|
||||
if args.non_erasable_generics().next().is_some() {
|
||||
let symbol = ExportedSymbol::Generic(def, args);
|
||||
symbols.push((symbol, SymbolExportInfo {
|
||||
level: SymbolExportLevel::Rust,
|
||||
@ -321,12 +321,9 @@ fn exported_symbols_provider_local(
|
||||
}));
|
||||
}
|
||||
}
|
||||
MonoItem::Fn(Instance { def: InstanceKind::DropGlue(def_id, Some(ty)), args }) => {
|
||||
MonoItem::Fn(Instance { def: InstanceKind::DropGlue(_, Some(ty)), args }) => {
|
||||
// A little sanity-check
|
||||
assert_eq!(
|
||||
args.non_erasable_generics(tcx, def_id).next(),
|
||||
Some(GenericArgKind::Type(ty))
|
||||
);
|
||||
assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty)));
|
||||
symbols.push((ExportedSymbol::DropGlue(ty), SymbolExportInfo {
|
||||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
@ -334,14 +331,11 @@ fn exported_symbols_provider_local(
|
||||
}));
|
||||
}
|
||||
MonoItem::Fn(Instance {
|
||||
def: InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty)),
|
||||
def: InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)),
|
||||
args,
|
||||
}) => {
|
||||
// A little sanity-check
|
||||
assert_eq!(
|
||||
args.non_erasable_generics(tcx, def_id).next(),
|
||||
Some(GenericArgKind::Type(ty))
|
||||
);
|
||||
assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty)));
|
||||
symbols.push((ExportedSymbol::AsyncDropGlueCtorShim(ty), SymbolExportInfo {
|
||||
level: SymbolExportLevel::Rust,
|
||||
kind: SymbolExportKind::Text,
|
||||
|
@ -888,7 +888,7 @@ impl CrateInfo {
|
||||
// below.
|
||||
//
|
||||
// In order to get this left-to-right dependency ordering, we use the reverse
|
||||
// postorder of all crates putting the leaves at the right-most positions.
|
||||
// postorder of all crates putting the leaves at the rightmost positions.
|
||||
let mut compiler_builtins = None;
|
||||
let mut used_crates: Vec<_> = tcx
|
||||
.postorder_cnums(())
|
||||
|
@ -137,7 +137,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
let inner = attr.meta_item_list();
|
||||
match inner.as_deref() {
|
||||
Some([item]) if item.has_name(sym::linker) => {
|
||||
if !tcx.features().used_with_arg {
|
||||
if !tcx.features().used_with_arg() {
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::used_with_arg,
|
||||
@ -149,7 +149,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
|
||||
}
|
||||
Some([item]) if item.has_name(sym::compiler) => {
|
||||
if !tcx.features().used_with_arg {
|
||||
if !tcx.features().used_with_arg() {
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::used_with_arg,
|
||||
@ -213,7 +213,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
.emit();
|
||||
}
|
||||
if is_closure
|
||||
&& !tcx.features().closure_track_caller
|
||||
&& !tcx.features().closure_track_caller()
|
||||
&& !attr.span.allows_unstable(sym::closure_track_caller)
|
||||
{
|
||||
feature_err(
|
||||
@ -268,7 +268,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
//
|
||||
// This exception needs to be kept in sync with allowing
|
||||
// `#[target_feature]` on `main` and `start`.
|
||||
} else if !tcx.features().target_feature_11 {
|
||||
} else if !tcx.features().target_feature_11() {
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::target_feature_11,
|
||||
@ -584,7 +584,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
// its parent function, which effectively inherits the features anyway. Boxing this closure
|
||||
// would result in this closure being compiled without the inherited target features, but this
|
||||
// is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
|
||||
if tcx.features().target_feature_11
|
||||
if tcx.features().target_feature_11()
|
||||
&& tcx.is_closure_like(did.to_def_id())
|
||||
&& codegen_fn_attrs.inline != InlineAttr::Always
|
||||
{
|
||||
|
@ -110,14 +110,14 @@ fn push_debuginfo_type_name<'tcx>(
|
||||
ty_and_layout,
|
||||
&|output, visited| {
|
||||
push_item_name(tcx, def.did(), true, output);
|
||||
push_generic_params_internal(tcx, args, def.did(), output, visited);
|
||||
push_generic_params_internal(tcx, args, output, visited);
|
||||
},
|
||||
output,
|
||||
visited,
|
||||
);
|
||||
} else {
|
||||
push_item_name(tcx, def.did(), qualified, output);
|
||||
push_generic_params_internal(tcx, args, def.did(), output, visited);
|
||||
push_generic_params_internal(tcx, args, output, visited);
|
||||
}
|
||||
}
|
||||
ty::Tuple(component_types) => {
|
||||
@ -251,13 +251,8 @@ fn push_debuginfo_type_name<'tcx>(
|
||||
let principal =
|
||||
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
|
||||
push_item_name(tcx, principal.def_id, qualified, output);
|
||||
let principal_has_generic_params = push_generic_params_internal(
|
||||
tcx,
|
||||
principal.args,
|
||||
principal.def_id,
|
||||
output,
|
||||
visited,
|
||||
);
|
||||
let principal_has_generic_params =
|
||||
push_generic_params_internal(tcx, principal.args, output, visited);
|
||||
|
||||
let projection_bounds: SmallVec<[_; 4]> = trait_data
|
||||
.projection_bounds()
|
||||
@ -538,13 +533,7 @@ pub fn compute_debuginfo_vtable_name<'tcx>(
|
||||
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref);
|
||||
push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
|
||||
visited.clear();
|
||||
push_generic_params_internal(
|
||||
tcx,
|
||||
trait_ref.args,
|
||||
trait_ref.def_id,
|
||||
&mut vtable_name,
|
||||
&mut visited,
|
||||
);
|
||||
push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
|
||||
} else {
|
||||
vtable_name.push('_');
|
||||
}
|
||||
@ -647,12 +636,11 @@ fn push_unqualified_item_name(
|
||||
fn push_generic_params_internal<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
def_id: DefId,
|
||||
output: &mut String,
|
||||
visited: &mut FxHashSet<Ty<'tcx>>,
|
||||
) -> bool {
|
||||
assert_eq!(args, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args));
|
||||
let mut args = args.non_erasable_generics(tcx, def_id).peekable();
|
||||
let mut args = args.non_erasable_generics().peekable();
|
||||
if args.peek().is_none() {
|
||||
return false;
|
||||
}
|
||||
@ -736,12 +724,11 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
|
||||
pub fn push_generic_params<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
def_id: DefId,
|
||||
output: &mut String,
|
||||
) {
|
||||
let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
|
||||
let mut visited = FxHashSet::default();
|
||||
push_generic_params_internal(tcx, args, def_id, output, &mut visited);
|
||||
push_generic_params_internal(tcx, args, output, &mut visited);
|
||||
}
|
||||
|
||||
fn push_closure_or_coroutine_name<'tcx>(
|
||||
@ -786,7 +773,7 @@ fn push_closure_or_coroutine_name<'tcx>(
|
||||
// FIXME(async_closures): This is probably not going to be correct w.r.t.
|
||||
// multiple coroutine flavors. Maybe truncate to (parent + 1)?
|
||||
let args = args.truncate_to(tcx, generics);
|
||||
push_generic_params_internal(tcx, args, enclosing_fn_def_id, output, visited);
|
||||
push_generic_params_internal(tcx, args, output, visited);
|
||||
}
|
||||
|
||||
fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
|
||||
|
@ -5,7 +5,6 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
@ -61,30 +60,9 @@ pub(crate) fn from_target_feature(
|
||||
return None;
|
||||
};
|
||||
|
||||
// Only allow features whose feature gates have been enabled.
|
||||
// Only allow target features whose feature gates have been enabled.
|
||||
let allowed = match feature_gate.as_ref().copied() {
|
||||
Some(sym::arm_target_feature) => rust_features.arm_target_feature,
|
||||
Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature,
|
||||
Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature,
|
||||
Some(sym::mips_target_feature) => rust_features.mips_target_feature,
|
||||
Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
|
||||
Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
|
||||
Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
|
||||
Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
|
||||
Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
|
||||
Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
|
||||
Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
|
||||
Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
|
||||
Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature,
|
||||
Some(sym::csky_target_feature) => rust_features.csky_target_feature,
|
||||
Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature,
|
||||
Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature,
|
||||
Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature,
|
||||
Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
|
||||
Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
|
||||
Some(sym::xop_target_feature) => rust_features.xop_target_feature,
|
||||
Some(sym::s390x_target_feature) => rust_features.s390x_target_feature,
|
||||
Some(name) => bug!("unknown target feature gate {}", name),
|
||||
Some(name) => rust_features.enabled(name),
|
||||
None => true,
|
||||
};
|
||||
if !allowed {
|
||||
|
@ -437,14 +437,6 @@ pub trait BuilderMethods<'a, 'tcx>:
|
||||
/// Called for `StorageDead`
|
||||
fn lifetime_end(&mut self, ptr: Self::Value, size: Size);
|
||||
|
||||
fn instrprof_increment(
|
||||
&mut self,
|
||||
fn_name: Self::Value,
|
||||
hash: Self::Value,
|
||||
num_counters: Self::Value,
|
||||
index: Self::Value,
|
||||
);
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
llty: Self::Type,
|
||||
|
@ -41,8 +41,6 @@ const_eval_const_context = {$kind ->
|
||||
*[other] {""}
|
||||
}
|
||||
|
||||
const_eval_const_stable = const-stable functions can only call other const-stable functions
|
||||
|
||||
const_eval_copy_nonoverlapping_overlapping =
|
||||
`copy_nonoverlapping` called on overlapping ranges
|
||||
|
||||
@ -259,6 +257,9 @@ const_eval_non_const_fn_call =
|
||||
const_eval_non_const_impl =
|
||||
impl defined here, but it is not `const`
|
||||
|
||||
const_eval_non_const_intrinsic =
|
||||
cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_not_enough_caller_args =
|
||||
calling a function with fewer arguments than it requires
|
||||
|
||||
@ -397,17 +398,29 @@ const_eval_uninhabited_enum_variant_read =
|
||||
read discriminant of an uninhabited enum variant
|
||||
const_eval_uninhabited_enum_variant_written =
|
||||
writing discriminant of an uninhabited enum variant
|
||||
|
||||
const_eval_unmarked_const_fn_exposed = `{$def_path}` cannot be (indirectly) exposed to stable
|
||||
.help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]`
|
||||
const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable
|
||||
.help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_indirect]` (but this requires team approval)
|
||||
|
||||
const_eval_unreachable = entering unreachable code
|
||||
const_eval_unreachable_unwind =
|
||||
unwinding past a stack frame that does not allow unwinding
|
||||
|
||||
const_eval_unsized_local = unsized locals are not supported
|
||||
const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn
|
||||
const_eval_unstable_in_stable_exposed =
|
||||
const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]`
|
||||
.is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features
|
||||
.unstable_sugg = if the {$is_function_call2 ->
|
||||
[true] caller
|
||||
*[false] function
|
||||
} is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do)
|
||||
.bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
|
||||
|
||||
const_eval_unstable_in_stable =
|
||||
const-stable function cannot use `#[feature({$gate})]`
|
||||
.unstable_sugg = if the function is not (yet) meant to be stable, make this function unstably const
|
||||
.bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (but requires team approval)
|
||||
const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic
|
||||
.help = add `#![feature({$feature})]` to the crate attributes to enable
|
||||
|
||||
const_eval_unterminated_c_string =
|
||||
reading a null-terminated string starting at {$pointer} with no null found before end of allocation
|
||||
|
@ -5,6 +5,7 @@ use std::borrow::Cow;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_attr::{ConstStability, StabilityLevel};
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
@ -28,8 +29,8 @@ use super::ops::{self, NonConstOp, Status};
|
||||
use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
|
||||
use super::resolver::FlowSensitiveAnalysis;
|
||||
use super::{ConstCx, Qualif};
|
||||
use crate::const_eval::is_unstable_const_fn;
|
||||
use crate::errors::UnstableInStable;
|
||||
use crate::check_consts::is_safe_to_expose_on_stable_const_fn;
|
||||
use crate::errors;
|
||||
|
||||
type QualifResults<'mir, 'tcx, Q> =
|
||||
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
|
||||
@ -274,19 +275,22 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
||||
/// context.
|
||||
pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) {
|
||||
let gate = match op.status_in_item(self.ccx) {
|
||||
Status::Allowed => return,
|
||||
|
||||
Status::Unstable(gate) if self.tcx.features().active(gate) => {
|
||||
let unstable_in_stable = self.ccx.is_const_stable_const_fn()
|
||||
&& !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate);
|
||||
if unstable_in_stable {
|
||||
emit_unstable_in_stable_error(self.ccx, span, gate);
|
||||
Status::Unstable { gate, safe_to_expose_on_stable, is_function_call }
|
||||
if self.tcx.features().enabled(gate) =>
|
||||
{
|
||||
// Generally this is allowed since the feature gate is enabled -- except
|
||||
// if this function wants to be safe-to-expose-on-stable.
|
||||
if !safe_to_expose_on_stable
|
||||
&& self.enforce_recursive_const_stability()
|
||||
&& !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate)
|
||||
{
|
||||
emit_unstable_in_stable_exposed_error(self.ccx, span, gate, is_function_call);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Status::Unstable(gate) => Some(gate),
|
||||
Status::Unstable { gate, .. } => Some(gate),
|
||||
Status::Forbidden => None,
|
||||
};
|
||||
|
||||
@ -304,7 +308,13 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
||||
self.error_emitted = Some(reported);
|
||||
}
|
||||
|
||||
ops::DiagImportance::Secondary => self.secondary_errors.push(err),
|
||||
ops::DiagImportance::Secondary => {
|
||||
self.secondary_errors.push(err);
|
||||
self.tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
"compilation must fail when there is a secondary const checker error",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -569,6 +579,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
|
||||
ty::FnPtr(..) => {
|
||||
self.check_op(ops::FnCallIndirect);
|
||||
// We can get here without an error in miri-unleashed mode... might as well
|
||||
// skip the rest of the checks as well then.
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
@ -604,14 +616,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
|
||||
let mut is_trait = false;
|
||||
// Attempting to call a trait method?
|
||||
if tcx.trait_of_item(callee).is_some() {
|
||||
if let Some(trait_did) = tcx.trait_of_item(callee) {
|
||||
trace!("attempting to call a trait method");
|
||||
|
||||
let trait_is_const = tcx.is_const_trait(trait_did);
|
||||
// trait method calls are only permitted when `effects` is enabled.
|
||||
// we don't error, since that is handled by typeck. We try to resolve
|
||||
// the trait into the concrete method, and uses that for const stability
|
||||
// checks.
|
||||
// typeck ensures the conditions for calling a const trait method are met,
|
||||
// so we only error if the trait isn't const. We try to resolve the trait
|
||||
// into the concrete method, and uses that for const stability checks.
|
||||
// FIXME(effects) we might consider moving const stability checks to typeck as well.
|
||||
if tcx.features().effects {
|
||||
if tcx.features().effects() && trait_is_const {
|
||||
// This skips the check below that ensures we only call `const fn`.
|
||||
is_trait = true;
|
||||
|
||||
if let Ok(Some(instance)) =
|
||||
@ -625,18 +640,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
callee = def;
|
||||
}
|
||||
} else {
|
||||
// if the trait is const but the user has not enabled the feature(s),
|
||||
// suggest them.
|
||||
let feature = if trait_is_const {
|
||||
Some(if tcx.features().const_trait_impl() {
|
||||
sym::effects
|
||||
} else {
|
||||
sym::const_trait_impl
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
caller,
|
||||
callee,
|
||||
args: fn_args,
|
||||
span: *fn_span,
|
||||
call_source,
|
||||
feature: Some(if tcx.features().const_trait_impl {
|
||||
sym::effects
|
||||
} else {
|
||||
sym::const_trait_impl
|
||||
}),
|
||||
feature,
|
||||
});
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -650,29 +674,73 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
// const-eval of the `begin_panic` fn assumes the argument is `&str`
|
||||
if tcx.is_lang_item(callee, LangItem::BeginPanic) {
|
||||
match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() {
|
||||
ty::Ref(_, ty, _) if ty.is_str() => return,
|
||||
ty::Ref(_, ty, _) if ty.is_str() => {}
|
||||
_ => self.check_op(ops::PanicNonStr),
|
||||
}
|
||||
// Allow this call, skip all the checks below.
|
||||
return;
|
||||
}
|
||||
|
||||
// const-eval of `#[rustc_const_panic_str]` functions assumes the argument is `&&str`
|
||||
if tcx.has_attr(callee, sym::rustc_const_panic_str) {
|
||||
match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() {
|
||||
ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) =>
|
||||
{
|
||||
{}
|
||||
_ => {
|
||||
self.check_op(ops::PanicNonStr);
|
||||
}
|
||||
}
|
||||
// Allow this call, skip all the checks below.
|
||||
return;
|
||||
}
|
||||
_ => self.check_op(ops::PanicNonStr),
|
||||
}
|
||||
}
|
||||
|
||||
// This can be called on stable via the `vec!` macro.
|
||||
if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) {
|
||||
self.check_op(ops::HeapAllocation);
|
||||
// Allow this call, skip all the checks below.
|
||||
return;
|
||||
}
|
||||
|
||||
if !tcx.is_const_fn_raw(callee) && !is_trait {
|
||||
// Intrinsics are language primitives, not regular calls, so treat them separately.
|
||||
if let Some(intrinsic) = tcx.intrinsic(callee) {
|
||||
match tcx.lookup_const_stability(callee) {
|
||||
None => {
|
||||
// Non-const intrinsic.
|
||||
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });
|
||||
}
|
||||
Some(ConstStability { feature: None, const_stable_indirect, .. }) => {
|
||||
// Intrinsic does not need a separate feature gate (we rely on the
|
||||
// regular stability checker). However, we have to worry about recursive
|
||||
// const stability.
|
||||
if !const_stable_indirect && self.enforce_recursive_const_stability() {
|
||||
self.dcx().emit_err(errors::UnmarkedIntrinsicExposed {
|
||||
span: self.span,
|
||||
def_path: self.tcx.def_path_str(callee),
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(ConstStability {
|
||||
feature: Some(feature),
|
||||
level: StabilityLevel::Unstable { .. },
|
||||
const_stable_indirect,
|
||||
..
|
||||
}) => {
|
||||
self.check_op(ops::IntrinsicUnstable {
|
||||
name: intrinsic.name,
|
||||
feature,
|
||||
const_stable_indirect,
|
||||
});
|
||||
}
|
||||
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
|
||||
// All good.
|
||||
}
|
||||
}
|
||||
// This completes the checks for intrinsics.
|
||||
return;
|
||||
}
|
||||
|
||||
// Trait functions are not `const fn` so we have to skip them here.
|
||||
if !tcx.is_const_fn(callee) && !is_trait {
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
caller,
|
||||
callee,
|
||||
@ -681,66 +749,68 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
call_source,
|
||||
feature: None,
|
||||
});
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the `const fn` we are trying to call is not const-stable, ensure that we have
|
||||
// the proper feature gate enabled.
|
||||
if let Some((gate, implied_by)) = is_unstable_const_fn(tcx, callee) {
|
||||
trace!(?gate, "calling unstable const fn");
|
||||
if self.span.allows_unstable(gate) {
|
||||
return;
|
||||
// Finally, stability for regular function calls -- this is the big one.
|
||||
match tcx.lookup_const_stability(callee) {
|
||||
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
|
||||
// All good.
|
||||
}
|
||||
if let Some(implied_by_gate) = implied_by
|
||||
&& self.span.allows_unstable(implied_by_gate)
|
||||
None | Some(ConstStability { feature: None, .. }) => {
|
||||
// This doesn't need a separate const-stability check -- const-stability equals
|
||||
// regular stability, and regular stability is checked separately.
|
||||
// However, we *do* have to worry about *recursive* const stability.
|
||||
if self.enforce_recursive_const_stability()
|
||||
&& !is_safe_to_expose_on_stable_const_fn(tcx, callee)
|
||||
{
|
||||
self.dcx().emit_err(errors::UnmarkedConstFnExposed {
|
||||
span: self.span,
|
||||
def_path: self.tcx.def_path_str(callee),
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(ConstStability {
|
||||
feature: Some(feature),
|
||||
level: StabilityLevel::Unstable { implied_by: implied_feature, .. },
|
||||
..
|
||||
}) => {
|
||||
// An unstable const fn with a feature gate.
|
||||
let callee_safe_to_expose_on_stable =
|
||||
is_safe_to_expose_on_stable_const_fn(tcx, callee);
|
||||
|
||||
// We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if
|
||||
// the callee is safe to expose, to avoid bypassing recursive stability.
|
||||
if (self.span.allows_unstable(feature)
|
||||
|| implied_feature.is_some_and(|f| self.span.allows_unstable(f)))
|
||||
&& callee_safe_to_expose_on_stable
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calling an unstable function *always* requires that the corresponding gate
|
||||
// (or implied gate) be enabled, even if the function has
|
||||
// `#[rustc_allow_const_fn_unstable(the_gate)]`.
|
||||
let gate_declared = |gate| tcx.features().declared(gate);
|
||||
let feature_gate_declared = gate_declared(gate);
|
||||
let implied_gate_declared = implied_by.is_some_and(gate_declared);
|
||||
if !feature_gate_declared && !implied_gate_declared {
|
||||
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
|
||||
return;
|
||||
}
|
||||
|
||||
// If this crate is not using stability attributes, or the caller is not claiming to be a
|
||||
// stable `const fn`, that is all that is required.
|
||||
if !self.ccx.is_const_stable_const_fn() {
|
||||
trace!("crate not using stability attributes or caller not stably const");
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we are something const-stable calling a const-unstable fn.
|
||||
if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
|
||||
trace!("rustc_allow_const_fn_unstable gate active");
|
||||
return;
|
||||
}
|
||||
|
||||
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that
|
||||
// have no `rustc_const_stable` attributes to be const-unstable as well. This
|
||||
// should be fixed later.
|
||||
let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none()
|
||||
&& tcx.lookup_stability(callee).is_some_and(|s| s.is_unstable());
|
||||
if callee_is_unstable_unmarked {
|
||||
trace!("callee_is_unstable_unmarked");
|
||||
// We do not use `const` modifiers for intrinsic "functions", as intrinsics are
|
||||
// `extern` functions, and these have no way to get marked `const`. So instead we
|
||||
// use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const`
|
||||
if self.ccx.is_const_stable_const_fn() || tcx.intrinsic(callee).is_some() {
|
||||
self.check_op(ops::FnCallUnstable(callee, None));
|
||||
return;
|
||||
// We can't use `check_op` to check whether the feature is enabled because
|
||||
// the logic is a bit different than elsewhere: local functions don't need
|
||||
// the feature gate, and there might be an "implied" gate that also suffices
|
||||
// to allow this.
|
||||
let feature_enabled = callee.is_local()
|
||||
|| tcx.features().enabled(feature)
|
||||
|| implied_feature.is_some_and(|f| tcx.features().enabled(f));
|
||||
// We do *not* honor this if we are in the "danger zone": we have to enforce
|
||||
// recursive const-stability and the callee is not safe-to-expose. In that
|
||||
// case we need `check_op` to do the check.
|
||||
let danger_zone = !callee_safe_to_expose_on_stable
|
||||
&& self.enforce_recursive_const_stability();
|
||||
if danger_zone || !feature_enabled {
|
||||
self.check_op(ops::FnCallUnstable {
|
||||
def_id: callee,
|
||||
feature,
|
||||
safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("permitting call");
|
||||
}
|
||||
|
||||
// Forbid all `Drop` terminators unless the place being dropped is a local with no
|
||||
@ -785,11 +855,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||
|
||||
TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
|
||||
|
||||
TerminatorKind::Yield { .. } => self.check_op(ops::Coroutine(
|
||||
TerminatorKind::Yield { .. } => {
|
||||
self.check_op(ops::Coroutine(
|
||||
self.tcx
|
||||
.coroutine_kind(self.body.source.def_id())
|
||||
.expect("Only expected to have a yield in a coroutine"),
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
TerminatorKind::CoroutineDrop => {
|
||||
span_bug!(
|
||||
@ -819,8 +891,19 @@ fn is_int_bool_float_or_char(ty: Ty<'_>) -> bool {
|
||||
ty.is_bool() || ty.is_integral() || ty.is_char() || ty.is_floating_point()
|
||||
}
|
||||
|
||||
fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol) {
|
||||
fn emit_unstable_in_stable_exposed_error(
|
||||
ccx: &ConstCx<'_, '_>,
|
||||
span: Span,
|
||||
gate: Symbol,
|
||||
is_function_call: bool,
|
||||
) -> ErrorGuaranteed {
|
||||
let attr_span = ccx.tcx.def_span(ccx.def_id()).shrink_to_lo();
|
||||
|
||||
ccx.dcx().emit_err(UnstableInStable { gate: gate.to_string(), span, attr_span });
|
||||
ccx.dcx().emit_err(errors::UnstableInStableExposed {
|
||||
gate: gate.to_string(),
|
||||
span,
|
||||
attr_span,
|
||||
is_function_call,
|
||||
is_function_call2: is_function_call,
|
||||
})
|
||||
}
|
||||
|
@ -59,10 +59,12 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
|
||||
self.const_kind.expect("`const_kind` must not be called on a non-const fn")
|
||||
}
|
||||
|
||||
pub fn is_const_stable_const_fn(&self) -> bool {
|
||||
pub fn enforce_recursive_const_stability(&self) -> bool {
|
||||
// We can skip this if `staged_api` is not enabled, since in such crates
|
||||
// `lookup_const_stability` will always be `None`.
|
||||
self.const_kind == Some(hir::ConstContext::ConstFn)
|
||||
&& self.tcx.features().staged_api
|
||||
&& is_const_stable_const_fn(self.tcx, self.def_id().to_def_id())
|
||||
&& self.tcx.features().staged_api()
|
||||
&& is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id())
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
@ -90,50 +92,38 @@ pub fn rustc_allow_const_fn_unstable(
|
||||
attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given `const fn` is "const-stable".
|
||||
/// Returns `true` if the given `const fn` is "safe to expose on stable".
|
||||
///
|
||||
/// Panics if the given `DefId` does not refer to a `const fn`.
|
||||
///
|
||||
/// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable"
|
||||
/// functions can be called in a const-context by users of the stable compiler. "const-stable"
|
||||
/// functions are subject to more stringent restrictions than "const-unstable" functions: They
|
||||
/// cannot use unstable features and can only call other "const-stable" functions.
|
||||
pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
// A default body in a `#[const_trait]` is not const-stable because const
|
||||
// trait fns currently cannot be const-stable. We shouldn't
|
||||
// restrict default bodies to only call const-stable functions.
|
||||
/// This is relevant within a `staged_api` crate. Unlike with normal features, the use of unstable
|
||||
/// const features *recursively* taints the functions that use them. This is to avoid accidentally
|
||||
/// exposing e.g. the implementation of an unstable const intrinsic on stable. So we partition the
|
||||
/// world into two functions: those that are safe to expose on stable (and hence may not use
|
||||
/// unstable features, not even recursively), and those that are not.
|
||||
pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
// A default body in a `#[const_trait]` is not const-stable because const trait fns currently
|
||||
// cannot be const-stable. These functions can't be called from anything stable, so we shouldn't
|
||||
// restrict them to only call const-stable functions.
|
||||
if tcx.is_const_default_method(def_id) {
|
||||
// FIXME(const_trait_impl): we have to eventually allow some of these if these things can ever be stable.
|
||||
// They should probably behave like regular `const fn` for that...
|
||||
return false;
|
||||
}
|
||||
|
||||
// Const-stability is only relevant for `const fn`.
|
||||
assert!(tcx.is_const_fn_raw(def_id));
|
||||
assert!(tcx.is_const_fn(def_id));
|
||||
|
||||
// A function is only const-stable if it has `#[rustc_const_stable]` or it the trait it belongs
|
||||
// to is const-stable.
|
||||
match tcx.lookup_const_stability(def_id) {
|
||||
Some(stab) => stab.is_const_stable(),
|
||||
None if is_parent_const_stable_trait(tcx, def_id) => {
|
||||
// Remove this when `#![feature(const_trait_impl)]` is stabilized,
|
||||
// returning `true` unconditionally.
|
||||
tcx.dcx().span_delayed_bug(
|
||||
tcx.def_span(def_id),
|
||||
"trait implementations cannot be const stable yet",
|
||||
);
|
||||
true
|
||||
None => {
|
||||
// Only marked functions can be trusted. Note that this may be a function in a
|
||||
// non-staged-API crate where no recursive checks were done!
|
||||
false
|
||||
}
|
||||
Some(stab) => {
|
||||
// We consider things safe-to-expose if they are stable, if they don't have any explicit
|
||||
// const stability attribute, or if they are marked as `const_stable_indirect`.
|
||||
stab.is_const_stable() || stab.feature.is_none() || stab.const_stable_indirect
|
||||
}
|
||||
None => false, // By default, items are not const stable.
|
||||
}
|
||||
}
|
||||
|
||||
fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
let local_def_id = def_id.expect_local();
|
||||
let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
|
||||
|
||||
let parent_owner_id = tcx.parent_hir_id(hir_id).owner;
|
||||
if !tcx.is_const_trait_impl_raw(parent_owner_id.to_def_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tcx.lookup_const_stability(parent_owner_id).is_some_and(|stab| stab.is_const_stable())
|
||||
}
|
||||
|
@ -26,8 +26,16 @@ use crate::{errors, fluent_generated};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Status {
|
||||
Allowed,
|
||||
Unstable(Symbol),
|
||||
Unstable {
|
||||
/// The feature that must be enabled to use this operation.
|
||||
gate: Symbol,
|
||||
/// Whether it is allowed to use this operation from stable `const fn`.
|
||||
/// This will usually be `false`.
|
||||
safe_to_expose_on_stable: bool,
|
||||
/// We indicate whether this is a function call, since we can use targeted
|
||||
/// diagnostics for "callee is not safe to expose om stable".
|
||||
is_function_call: bool,
|
||||
},
|
||||
Forbidden,
|
||||
}
|
||||
|
||||
@ -40,9 +48,9 @@ pub enum DiagImportance {
|
||||
Secondary,
|
||||
}
|
||||
|
||||
/// An operation that is not *always* allowed in a const context.
|
||||
/// An operation that is *not allowed* in a const context.
|
||||
pub trait NonConstOp<'tcx>: std::fmt::Debug {
|
||||
/// Returns an enum indicating whether this operation is allowed within the given item.
|
||||
/// Returns an enum indicating whether this operation can be enabled with a feature gate.
|
||||
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
|
||||
Status::Forbidden
|
||||
}
|
||||
@ -114,7 +122,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
|
||||
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
|
||||
// FIXME(effects) revisit this
|
||||
if !tcx.is_const_trait_impl_raw(data.impl_def_id) {
|
||||
if !tcx.is_const_trait_impl(data.impl_def_id) {
|
||||
let span = tcx.def_span(data.impl_def_id);
|
||||
err.subdiagnostic(errors::NonConstImplNote { span });
|
||||
}
|
||||
@ -166,7 +174,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
let note = match self_ty.kind() {
|
||||
FnDef(def_id, ..) => {
|
||||
let span = tcx.def_span(*def_id);
|
||||
if ccx.tcx.is_const_fn_raw(*def_id) {
|
||||
if ccx.tcx.is_const_fn(*def_id) {
|
||||
span_bug!(span, "calling const FnDef errored when it shouldn't");
|
||||
}
|
||||
|
||||
@ -298,27 +306,75 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
///
|
||||
/// Contains the name of the feature that would allow the use of this function.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FnCallUnstable(pub DefId, pub Option<Symbol>);
|
||||
pub(crate) struct FnCallUnstable {
|
||||
pub def_id: DefId,
|
||||
pub feature: Symbol,
|
||||
pub safe_to_expose_on_stable: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
|
||||
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
|
||||
Status::Unstable {
|
||||
gate: self.feature,
|
||||
safe_to_expose_on_stable: self.safe_to_expose_on_stable,
|
||||
is_function_call: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
let FnCallUnstable(def_id, feature) = *self;
|
||||
|
||||
let mut err = ccx
|
||||
.dcx()
|
||||
.create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
|
||||
|
||||
let mut err = ccx.dcx().create_err(errors::UnstableConstFn {
|
||||
span,
|
||||
def_path: ccx.tcx.def_path_str(self.def_id),
|
||||
});
|
||||
// FIXME: make this translatable
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
if ccx.is_const_stable_const_fn() {
|
||||
err.help(fluent_generated::const_eval_const_stable);
|
||||
} else if ccx.tcx.sess.is_nightly_build() {
|
||||
if let Some(feature) = feature {
|
||||
err.help(format!("add `#![feature({feature})]` to the crate attributes to enable"));
|
||||
err.help(format!("add `#![feature({})]` to the crate attributes to enable", self.feature));
|
||||
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
/// A call to an intrinsic that is just not const-callable at all.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct IntrinsicNonConst {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
ccx.dcx().create_err(errors::NonConstIntrinsic {
|
||||
span,
|
||||
name: self.name,
|
||||
kind: ccx.const_kind(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A call to an intrinsic that is just not const-callable at all.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct IntrinsicUnstable {
|
||||
pub name: Symbol,
|
||||
pub feature: Symbol,
|
||||
pub const_stable_indirect: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
|
||||
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
|
||||
Status::Unstable {
|
||||
gate: self.feature,
|
||||
safe_to_expose_on_stable: self.const_stable_indirect,
|
||||
// We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`,
|
||||
// that's not a trivial change!
|
||||
is_function_call: false,
|
||||
}
|
||||
}
|
||||
|
||||
err
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
ccx.dcx().create_err(errors::UnstableIntrinsic {
|
||||
span,
|
||||
name: self.name,
|
||||
feature: self.feature,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,7 +387,11 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine {
|
||||
hir::CoroutineSource::Block,
|
||||
) = self.0
|
||||
{
|
||||
Status::Unstable(sym::const_async_blocks)
|
||||
Status::Unstable {
|
||||
gate: sym::const_async_blocks,
|
||||
safe_to_expose_on_stable: false,
|
||||
is_function_call: false,
|
||||
}
|
||||
} else {
|
||||
Status::Forbidden
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use crate::check_consts::rustc_allow_const_fn_unstable;
|
||||
/// elaboration.
|
||||
pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool {
|
||||
// Const-stable functions must always use the stable live drop checker...
|
||||
if ccx.is_const_stable_const_fn() {
|
||||
if ccx.enforce_recursive_const_stability() {
|
||||
// ...except if they have the feature flag set via `rustc_allow_const_fn_unstable`.
|
||||
return rustc_allow_const_fn_unstable(
|
||||
ccx.tcx,
|
||||
@ -24,7 +24,7 @@ pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool {
|
||||
);
|
||||
}
|
||||
|
||||
ccx.tcx.features().const_precise_live_drops
|
||||
ccx.tcx.features().const_precise_live_drops()
|
||||
}
|
||||
|
||||
/// Look for live drops in a const context.
|
||||
|
@ -1,25 +1,8 @@
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use {rustc_attr as attr, rustc_hir as hir};
|
||||
|
||||
/// Whether the `def_id` is an unstable const fn and what feature gate(s) are necessary to enable
|
||||
/// it.
|
||||
pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<(Symbol, Option<Symbol>)> {
|
||||
if tcx.is_const_fn_raw(def_id) {
|
||||
let const_stab = tcx.lookup_const_stability(def_id)?;
|
||||
match const_stab.level {
|
||||
attr::StabilityLevel::Unstable { implied_by, .. } => {
|
||||
Some((const_stab.feature, implied_by))
|
||||
}
|
||||
attr::StabilityLevel::Stable { .. } => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
let parent_id = tcx.local_parent(def_id);
|
||||
|
@ -219,7 +219,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
|
||||
}
|
||||
|
||||
/// "Intercept" a function call, because we have something special to do for it.
|
||||
/// All `#[rustc_do_not_const_check]` functions should be hooked here.
|
||||
/// All `#[rustc_do_not_const_check]` functions MUST be hooked here.
|
||||
/// If this returns `Some` function, which may be `instance` or a different function with
|
||||
/// compatible arguments, then evaluation should continue with that function.
|
||||
/// If this returns `None`, the function call has been handled and the function has returned.
|
||||
@ -431,8 +431,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
||||
// sensitive check here. But we can at least rule out functions that are not const at
|
||||
// all. That said, we have to allow calling functions inside a trait marked with
|
||||
// #[const_trait]. These *are* const-checked!
|
||||
// FIXME: why does `is_const_fn_raw` not classify them as const?
|
||||
if (!ecx.tcx.is_const_fn_raw(def) && !ecx.tcx.is_const_default_method(def))
|
||||
// FIXME(effects): why does `is_const_fn` not classify them as const?
|
||||
if (!ecx.tcx.is_const_fn(def) && !ecx.tcx.is_const_default_method(def))
|
||||
|| ecx.tcx.has_attr(def, sym::rustc_do_not_const_check)
|
||||
{
|
||||
// We certainly do *not* want to actually call the fn
|
||||
|
@ -14,7 +14,7 @@ use rustc_middle::mir::interpret::{
|
||||
UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
|
||||
};
|
||||
use rustc_middle::ty::{self, Mutability, Ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::abi::WrappingRange;
|
||||
use rustc_target::abi::call::AdjustForForeignAbiError;
|
||||
|
||||
@ -44,11 +44,15 @@ pub(crate) struct MutablePtrInFinal {
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unstable_in_stable)]
|
||||
pub(crate) struct UnstableInStable {
|
||||
#[diag(const_eval_unstable_in_stable_exposed)]
|
||||
pub(crate) struct UnstableInStableExposed {
|
||||
pub gate: String,
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[help(const_eval_is_function_call)]
|
||||
pub is_function_call: bool,
|
||||
/// Need to duplicate the field so that fluent also provides it as a variable...
|
||||
pub is_function_call2: bool,
|
||||
#[suggestion(
|
||||
const_eval_unstable_sugg,
|
||||
code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n",
|
||||
@ -117,6 +121,34 @@ pub(crate) struct UnstableConstFn {
|
||||
pub def_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unstable_intrinsic)]
|
||||
#[help]
|
||||
pub(crate) struct UnstableIntrinsic {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
pub feature: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unmarked_const_fn_exposed)]
|
||||
#[help]
|
||||
pub(crate) struct UnmarkedConstFnExposed {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub def_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unmarked_intrinsic_exposed)]
|
||||
#[help]
|
||||
pub(crate) struct UnmarkedIntrinsicExposed {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub def_path: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_mutable_ref_escaping, code = E0764)]
|
||||
pub(crate) struct MutableRefEscaping {
|
||||
@ -153,6 +185,15 @@ pub(crate) struct NonConstFnCall {
|
||||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_non_const_intrinsic)]
|
||||
pub(crate) struct NonConstIntrinsic {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_unallowed_op_in_const_context)]
|
||||
pub(crate) struct UnallowedOpInConstContext {
|
||||
|
@ -2,15 +2,13 @@
|
||||
//!
|
||||
//! Algorithm based on Loukas Georgiadis,
|
||||
//! "Linear-Time Algorithms for Dominators and Related Problems",
|
||||
//! <ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf>
|
||||
//! <https://www.cs.princeton.edu/techreports/2005/737.pdf>
|
||||
//!
|
||||
//! Additionally useful is the original Lengauer-Tarjan paper on this subject,
|
||||
//! "A Fast Algorithm for Finding Dominators in a Flowgraph"
|
||||
//! Thomas Lengauer and Robert Endre Tarjan.
|
||||
//! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
|
||||
use super::ControlFlowGraph;
|
||||
@ -64,9 +62,6 @@ fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool {
|
||||
}
|
||||
|
||||
fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
||||
// compute the post order index (rank) for each node
|
||||
let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
|
||||
|
||||
// We allocate capacity for the full set of nodes, because most of the time
|
||||
// most of the nodes *are* reachable.
|
||||
let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
|
||||
@ -83,12 +78,10 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
||||
pre_order_to_real.push(graph.start_node());
|
||||
parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now.
|
||||
real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO);
|
||||
let mut post_order_idx = 0;
|
||||
|
||||
// Traverse the graph, collecting a number of things:
|
||||
//
|
||||
// * Preorder mapping (to it, and back to the actual ordering)
|
||||
// * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product)
|
||||
// * Parents for each vertex in the preorder tree
|
||||
//
|
||||
// These are all done here rather than through one of the 'standard'
|
||||
@ -104,8 +97,6 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
||||
continue 'recurse;
|
||||
}
|
||||
}
|
||||
post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx;
|
||||
post_order_idx += 1;
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
@ -282,7 +273,7 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
||||
|
||||
let time = compute_access_time(start_node, &immediate_dominators);
|
||||
|
||||
Inner { post_order_rank, immediate_dominators, time }
|
||||
Inner { immediate_dominators, time }
|
||||
}
|
||||
|
||||
/// Evaluate the link-eval virtual forest, providing the currently minimum semi
|
||||
@ -348,7 +339,6 @@ fn compress(
|
||||
/// Tracks the list of dominators for each node.
|
||||
#[derive(Clone, Debug)]
|
||||
struct Inner<N: Idx> {
|
||||
post_order_rank: IndexVec<N, usize>,
|
||||
// Even though we track only the immediate dominator of each node, it's
|
||||
// possible to get its full list of dominators by looking up the dominator
|
||||
// of each dominator.
|
||||
@ -379,17 +369,6 @@ impl<Node: Idx> Dominators<Node> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
|
||||
/// relationship, the dominator will always precede the dominated. (The relative ordering
|
||||
/// of two unrelated nodes will also be consistent, but otherwise the order has no
|
||||
/// meaning.) This method cannot be used to determine if either Node dominates the other.
|
||||
pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering {
|
||||
match &self.kind {
|
||||
Kind::Path => lhs.index().cmp(&rhs.index()),
|
||||
Kind::General(g) => g.post_order_rank[rhs].cmp(&g.post_order_rank[lhs]),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `a` dominates `b`.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -45,6 +45,7 @@ const fn unstable_fn() {}
|
||||
#[stable(feature = "stable_struct", since = "1.39.0")] // ok!
|
||||
struct Stable;
|
||||
|
||||
#[stable(feature = "stable_fn", since = "1.39.0")]
|
||||
#[rustc_const_stable(feature = "stable_fn", since = "1.39.0")] // ok!
|
||||
const fn stable_fn() {}
|
||||
```
|
||||
|
@ -30,6 +30,7 @@ To fix this issue, you need to provide the `since` field. Example:
|
||||
#[stable(feature = "_stable_fn", since = "1.0.0")] // ok!
|
||||
fn _stable_fn() {}
|
||||
|
||||
#[stable(feature = "_stable_const_fn", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "_stable_const_fn", since = "1.0.0")] // ok!
|
||||
const fn _stable_const_fn() {}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
A `#![feature]` attribute was declared multiple times.
|
||||
The same feature is enabled multiple times with `#![feature]` attributes
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0636
|
||||
#![allow(stable_features)]
|
||||
#![feature(rust1)]
|
||||
#![feature(rust1)] // error: the feature `rust1` has already been declared
|
||||
#![feature(rust1)] // error: the feature `rust1` has already been enabled
|
||||
```
|
||||
|
@ -1,8 +1,10 @@
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
`impl Trait` is not allowed in path parameters.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0667
|
||||
```ignore (removed error code)
|
||||
fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
|
||||
x.next().unwrap()
|
||||
}
|
||||
@ -11,7 +13,7 @@ fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
|
||||
You cannot use `impl Trait` in path parameters. If you want something
|
||||
equivalent, you can do this instead:
|
||||
|
||||
```
|
||||
```ignore (removed error code)
|
||||
fn some_fn<T: Iterator>(mut x: T) -> T::Item { // ok!
|
||||
x.next().unwrap()
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
A `#![feature]` attribute was declared for a feature that is stable in the
|
||||
A `#![feature]` attribute was used for a feature that is stable in the
|
||||
current edition, but not in all editions.
|
||||
|
||||
Erroneous code example:
|
||||
|
@ -58,9 +58,9 @@ impl HumanReadableErrorType {
|
||||
struct Margin {
|
||||
/// The available whitespace in the left that can be consumed when centering.
|
||||
pub whitespace_left: usize,
|
||||
/// The column of the beginning of left-most span.
|
||||
/// The column of the beginning of leftmost span.
|
||||
pub span_left: usize,
|
||||
/// The column of the end of right-most span.
|
||||
/// The column of the end of rightmost span.
|
||||
pub span_right: usize,
|
||||
/// The beginning of the line to be displayed.
|
||||
pub computed_left: usize,
|
||||
@ -128,7 +128,7 @@ impl Margin {
|
||||
} else {
|
||||
0
|
||||
};
|
||||
// We want to show as much as possible, max_line_len is the right-most boundary for the
|
||||
// We want to show as much as possible, max_line_len is the rightmost boundary for the
|
||||
// relevant code.
|
||||
self.computed_right = max(max_line_len, self.computed_left);
|
||||
|
||||
@ -685,7 +685,7 @@ impl HumanEmitter {
|
||||
buffer.puts(line_offset, code_offset, "...", Style::LineNumber);
|
||||
}
|
||||
if margin.was_cut_right(line_len) {
|
||||
// We have stripped some code after the right-most span end, make it clear we did so.
|
||||
// We have stripped some code after the rightmost span end, make it clear we did so.
|
||||
buffer.puts(line_offset, code_offset + taken - 3, "...", Style::LineNumber);
|
||||
}
|
||||
buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber);
|
||||
|
@ -25,7 +25,7 @@ expand_collapse_debuginfo_illegal =
|
||||
illegal value for attribute #[collapse_debuginfo(no|external|yes)]
|
||||
|
||||
expand_count_repetition_misplaced =
|
||||
`count` can not be placed inside the inner-most repetition
|
||||
`count` can not be placed inside the innermost repetition
|
||||
|
||||
expand_crate_name_in_cfg_attr =
|
||||
`crate_name` within an `#![cfg_attr]` attribute is forbidden
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::default::Default;
|
||||
use std::iter;
|
||||
use std::path::Component::Prefix;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -865,7 +866,9 @@ impl SyntaxExtension {
|
||||
})
|
||||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let stability = attr::find_stability(sess, attrs, span);
|
||||
let const_stability = attr::find_const_stability(sess, attrs, span);
|
||||
// We set `is_const_fn` false to avoid getting any implicit const stability.
|
||||
let const_stability =
|
||||
attr::find_const_stability(sess, attrs, span, /* is_const_fn */ false);
|
||||
let body_stability = attr::find_body_stability(sess, attrs);
|
||||
if let Some((_, sp)) = const_stability {
|
||||
sess.dcx().emit_err(errors::MacroConstStability {
|
||||
@ -1293,7 +1296,12 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
|
||||
base_path.push(path);
|
||||
Ok(base_path)
|
||||
} else {
|
||||
Ok(path)
|
||||
// This ensures that Windows verbatim paths are fixed if mixed path separators are used,
|
||||
// which can happen when `concat!` is used to join paths.
|
||||
match path.components().next() {
|
||||
Some(Prefix(prefix)) if prefix.kind().is_verbatim() => Ok(path.components().collect()),
|
||||
_ => Ok(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,8 @@ use rustc_ast::{
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_feature::{
|
||||
ACCEPTED_FEATURES, AttributeSafety, Features, REMOVED_FEATURES, UNSTABLE_FEATURES,
|
||||
ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features,
|
||||
REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES,
|
||||
};
|
||||
use rustc_lint_defs::BuiltinLintDiag;
|
||||
use rustc_parse::validate_attr;
|
||||
@ -52,7 +53,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
||||
|
||||
let mut features = Features::default();
|
||||
|
||||
// Process all features declared in the code.
|
||||
// Process all features enabled in the code.
|
||||
for attr in krate_attrs {
|
||||
for mi in feature_list(attr) {
|
||||
let name = match mi.ident() {
|
||||
@ -76,8 +77,8 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
||||
}
|
||||
};
|
||||
|
||||
// If the declared feature has been removed, issue an error.
|
||||
if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||
// If the enabled feature has been removed, issue an error.
|
||||
if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||
sess.dcx().emit_err(FeatureRemoved {
|
||||
span: mi.span(),
|
||||
reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
|
||||
@ -85,14 +86,17 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the declared feature is stable, record it.
|
||||
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
|
||||
let since = Some(Symbol::intern(f.since));
|
||||
features.set_declared_lang_feature(name, mi.span(), since);
|
||||
// If the enabled feature is stable, record it.
|
||||
if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) {
|
||||
features.set_enabled_lang_feature(EnabledLangFeature {
|
||||
gate_name: name,
|
||||
attr_sp: mi.span(),
|
||||
stable_since: Some(Symbol::intern(f.since)),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// If `-Z allow-features` is used and the declared feature is
|
||||
// If `-Z allow-features` is used and the enabled feature is
|
||||
// unstable and not also listed as one of the allowed features,
|
||||
// issue an error.
|
||||
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
|
||||
@ -102,9 +106,8 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
||||
}
|
||||
}
|
||||
|
||||
// If the declared feature is unstable, record it.
|
||||
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||
(f.set_enabled)(&mut features);
|
||||
// If the enabled feature is unstable, record it.
|
||||
if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() {
|
||||
// When the ICE comes from core, alloc or std (approximation of the standard
|
||||
// library), there's a chance that the person hitting the ICE may be using
|
||||
// -Zbuild-std or similar with an untested target. The bug is probably in the
|
||||
@ -115,13 +118,19 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
||||
{
|
||||
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
features.set_declared_lang_feature(name, mi.span(), None);
|
||||
|
||||
features.set_enabled_lang_feature(EnabledLangFeature {
|
||||
gate_name: name,
|
||||
attr_sp: mi.span(),
|
||||
stable_since: None,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, the feature is unknown. Record it as a lib feature.
|
||||
// It will be checked later.
|
||||
features.set_declared_lib_feature(name, mi.span());
|
||||
// Otherwise, the feature is unknown. Enable it as a lib feature.
|
||||
// It will be checked later whether the feature really exists.
|
||||
features
|
||||
.set_enabled_lib_feature(EnabledLibFeature { gate_name: name, attr_sp: mi.span() });
|
||||
|
||||
// Similar to above, detect internal lib features to suppress
|
||||
// the ICE message that asks for a report.
|
||||
@ -396,7 +405,7 @@ impl<'a> StripUnconfigured<'a> {
|
||||
/// If attributes are not allowed on expressions, emit an error for `attr`
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
|
||||
if self.features.is_some_and(|features| !features.stmt_expr_attributes)
|
||||
if self.features.is_some_and(|features| !features.stmt_expr_attributes())
|
||||
&& !attr.span.allows_unstable(sym::stmt_expr_attributes)
|
||||
{
|
||||
let mut err = feature_err(
|
||||
|
@ -867,7 +867,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
| Annotatable::FieldDef(..)
|
||||
| Annotatable::Variant(..) => panic!("unexpected annotatable"),
|
||||
};
|
||||
if self.cx.ecfg.features.proc_macro_hygiene {
|
||||
if self.cx.ecfg.features.proc_macro_hygiene() {
|
||||
return;
|
||||
}
|
||||
feature_err(
|
||||
@ -905,7 +905,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
if !self.cx.ecfg.features.proc_macro_hygiene {
|
||||
if !self.cx.ecfg.features.proc_macro_hygiene() {
|
||||
annotatable.visit_with(&mut GateProcMacroInput { sess: &self.cx.sess });
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,11 @@ use std::collections::hash_map::Entry;
|
||||
use std::{mem, slice};
|
||||
|
||||
use ast::token::IdentIsRaw;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::token::NtPatKind::*;
|
||||
use rustc_ast::token::TokenKind::*;
|
||||
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
|
||||
use rustc_ast::{DUMMY_NODE_ID, NodeId};
|
||||
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr::{self as attr, TransparencyError};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
@ -370,34 +369,32 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
|
||||
pub fn compile_declarative_macro(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
def: &ast::Item,
|
||||
macro_def: &ast::MacroDef,
|
||||
ident: Ident,
|
||||
attrs: &[ast::Attribute],
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
edition: Edition,
|
||||
) -> (SyntaxExtension, Vec<(usize, Span)>) {
|
||||
debug!("compile_declarative_macro: {:?}", def);
|
||||
let mk_syn_ext = |expander| {
|
||||
SyntaxExtension::new(
|
||||
sess,
|
||||
features,
|
||||
SyntaxExtensionKind::LegacyBang(expander),
|
||||
def.span,
|
||||
span,
|
||||
Vec::new(),
|
||||
edition,
|
||||
def.ident.name,
|
||||
&def.attrs,
|
||||
def.id != DUMMY_NODE_ID,
|
||||
ident.name,
|
||||
attrs,
|
||||
node_id != DUMMY_NODE_ID,
|
||||
)
|
||||
};
|
||||
let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new());
|
||||
|
||||
let dcx = sess.dcx();
|
||||
let lhs_nm = Ident::new(sym::lhs, def.span);
|
||||
let rhs_nm = Ident::new(sym::rhs, def.span);
|
||||
let lhs_nm = Ident::new(sym::lhs, span);
|
||||
let rhs_nm = Ident::new(sym::rhs, span);
|
||||
let tt_spec = Some(NonterminalKind::TT);
|
||||
|
||||
let macro_def = match &def.kind {
|
||||
ast::ItemKind::MacroDef(def) => def,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let macro_rules = macro_def.macro_rules;
|
||||
|
||||
// Parse the macro_rules! invocation
|
||||
@ -410,25 +407,22 @@ pub fn compile_declarative_macro(
|
||||
let argument_gram = vec![
|
||||
mbe::TokenTree::Sequence(DelimSpan::dummy(), mbe::SequenceRepetition {
|
||||
tts: vec![
|
||||
mbe::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec),
|
||||
mbe::TokenTree::token(token::FatArrow, def.span),
|
||||
mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
|
||||
mbe::TokenTree::MetaVarDecl(span, lhs_nm, tt_spec),
|
||||
mbe::TokenTree::token(token::FatArrow, span),
|
||||
mbe::TokenTree::MetaVarDecl(span, rhs_nm, tt_spec),
|
||||
],
|
||||
separator: Some(Token::new(
|
||||
if macro_rules { token::Semi } else { token::Comma },
|
||||
def.span,
|
||||
)),
|
||||
kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span),
|
||||
separator: Some(Token::new(if macro_rules { token::Semi } else { token::Comma }, span)),
|
||||
kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, span),
|
||||
num_captures: 2,
|
||||
}),
|
||||
// to phase into semicolon-termination instead of semicolon-separation
|
||||
mbe::TokenTree::Sequence(DelimSpan::dummy(), mbe::SequenceRepetition {
|
||||
tts: vec![mbe::TokenTree::token(
|
||||
if macro_rules { token::Semi } else { token::Comma },
|
||||
def.span,
|
||||
span,
|
||||
)],
|
||||
separator: None,
|
||||
kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, def.span),
|
||||
kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, span),
|
||||
num_captures: 0,
|
||||
}),
|
||||
];
|
||||
@ -460,7 +454,7 @@ pub fn compile_declarative_macro(
|
||||
};
|
||||
|
||||
let s = parse_failure_msg(&token, track.get_expected_token());
|
||||
let sp = token.span.substitute_dummy(def.span);
|
||||
let sp = token.span.substitute_dummy(span);
|
||||
let mut err = sess.dcx().struct_span_err(sp, s);
|
||||
err.span_label(sp, msg);
|
||||
annotate_doc_comment(&mut err, sess.source_map(), sp);
|
||||
@ -468,7 +462,7 @@ pub fn compile_declarative_macro(
|
||||
return dummy_syn_ext(guar);
|
||||
}
|
||||
Error(sp, msg) => {
|
||||
let guar = sess.dcx().span_err(sp.substitute_dummy(def.span), msg);
|
||||
let guar = sess.dcx().span_err(sp.substitute_dummy(span), msg);
|
||||
return dummy_syn_ext(guar);
|
||||
}
|
||||
ErrorReported(guar) => {
|
||||
@ -489,7 +483,7 @@ pub fn compile_declarative_macro(
|
||||
&TokenStream::new(vec![tt.clone()]),
|
||||
true,
|
||||
sess,
|
||||
def.id,
|
||||
node_id,
|
||||
features,
|
||||
edition,
|
||||
)
|
||||
@ -497,13 +491,13 @@ pub fn compile_declarative_macro(
|
||||
.unwrap();
|
||||
// We don't handle errors here, the driver will abort
|
||||
// after parsing/expansion. We can report every error in every macro this way.
|
||||
check_emission(check_lhs_nt_follows(sess, def, &tt));
|
||||
check_emission(check_lhs_nt_follows(sess, node_id, &tt));
|
||||
return tt;
|
||||
}
|
||||
sess.dcx().span_bug(def.span, "wrong-structured lhs")
|
||||
sess.dcx().span_bug(span, "wrong-structured lhs")
|
||||
})
|
||||
.collect::<Vec<mbe::TokenTree>>(),
|
||||
_ => sess.dcx().span_bug(def.span, "wrong-structured lhs"),
|
||||
_ => sess.dcx().span_bug(span, "wrong-structured lhs"),
|
||||
};
|
||||
|
||||
let rhses = match &argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
|
||||
@ -515,17 +509,17 @@ pub fn compile_declarative_macro(
|
||||
&TokenStream::new(vec![tt.clone()]),
|
||||
false,
|
||||
sess,
|
||||
def.id,
|
||||
node_id,
|
||||
features,
|
||||
edition,
|
||||
)
|
||||
.pop()
|
||||
.unwrap();
|
||||
}
|
||||
sess.dcx().span_bug(def.span, "wrong-structured rhs")
|
||||
sess.dcx().span_bug(span, "wrong-structured rhs")
|
||||
})
|
||||
.collect::<Vec<mbe::TokenTree>>(),
|
||||
_ => sess.dcx().span_bug(def.span, "wrong-structured rhs"),
|
||||
_ => sess.dcx().span_bug(span, "wrong-structured rhs"),
|
||||
};
|
||||
|
||||
for rhs in &rhses {
|
||||
@ -537,15 +531,9 @@ pub fn compile_declarative_macro(
|
||||
check_emission(check_lhs_no_empty_seq(sess, slice::from_ref(lhs)));
|
||||
}
|
||||
|
||||
check_emission(macro_check::check_meta_variables(
|
||||
&sess.psess,
|
||||
def.id,
|
||||
def.span,
|
||||
&lhses,
|
||||
&rhses,
|
||||
));
|
||||
check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses));
|
||||
|
||||
let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
|
||||
let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules);
|
||||
match transparency_error {
|
||||
Some(TransparencyError::UnknownTransparency(value, span)) => {
|
||||
dcx.span_err(span, format!("unknown macro transparency: `{value}`"));
|
||||
@ -564,7 +552,7 @@ pub fn compile_declarative_macro(
|
||||
|
||||
// Compute the spans of the macro rules for unused rule linting.
|
||||
// Also, we are only interested in non-foreign macros.
|
||||
let rule_spans = if def.id != DUMMY_NODE_ID {
|
||||
let rule_spans = if node_id != DUMMY_NODE_ID {
|
||||
lhses
|
||||
.iter()
|
||||
.zip(rhses.iter())
|
||||
@ -590,15 +578,15 @@ pub fn compile_declarative_macro(
|
||||
mbe::TokenTree::Delimited(.., delimited) => {
|
||||
mbe::macro_parser::compute_locs(&delimited.tts)
|
||||
}
|
||||
_ => sess.dcx().span_bug(def.span, "malformed macro lhs"),
|
||||
_ => sess.dcx().span_bug(span, "malformed macro lhs"),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expander = Box::new(MacroRulesMacroExpander {
|
||||
name: def.ident,
|
||||
span: def.span,
|
||||
node_id: def.id,
|
||||
name: ident,
|
||||
span,
|
||||
node_id,
|
||||
transparency,
|
||||
lhses,
|
||||
rhses,
|
||||
@ -608,13 +596,13 @@ pub fn compile_declarative_macro(
|
||||
|
||||
fn check_lhs_nt_follows(
|
||||
sess: &Session,
|
||||
def: &ast::Item,
|
||||
node_id: NodeId,
|
||||
lhs: &mbe::TokenTree,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
// lhs is going to be like TokenTree::Delimited(...), where the
|
||||
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
|
||||
if let mbe::TokenTree::Delimited(.., delimited) = lhs {
|
||||
check_matcher(sess, def, &delimited.tts)
|
||||
check_matcher(sess, node_id, &delimited.tts)
|
||||
} else {
|
||||
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
|
||||
Err(sess.dcx().span_err(lhs.span(), msg))
|
||||
@ -686,12 +674,12 @@ fn check_rhs(sess: &Session, rhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed
|
||||
|
||||
fn check_matcher(
|
||||
sess: &Session,
|
||||
def: &ast::Item,
|
||||
node_id: NodeId,
|
||||
matcher: &[mbe::TokenTree],
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let first_sets = FirstSets::new(matcher);
|
||||
let empty_suffix = TokenSet::empty();
|
||||
check_matcher_core(sess, def, &first_sets, matcher, &empty_suffix)?;
|
||||
check_matcher_core(sess, node_id, &first_sets, matcher, &empty_suffix)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1028,7 +1016,7 @@ impl<'tt> TokenSet<'tt> {
|
||||
// see `FirstSets::new`.
|
||||
fn check_matcher_core<'tt>(
|
||||
sess: &Session,
|
||||
def: &ast::Item,
|
||||
node_id: NodeId,
|
||||
first_sets: &FirstSets<'tt>,
|
||||
matcher: &'tt [mbe::TokenTree],
|
||||
follow: &TokenSet<'tt>,
|
||||
@ -1082,7 +1070,7 @@ fn check_matcher_core<'tt>(
|
||||
token::CloseDelim(d.delim),
|
||||
span.close,
|
||||
));
|
||||
check_matcher_core(sess, def, first_sets, &d.tts, &my_suffix)?;
|
||||
check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?;
|
||||
// don't track non NT tokens
|
||||
last.replace_with_irrelevant();
|
||||
|
||||
@ -1114,7 +1102,7 @@ fn check_matcher_core<'tt>(
|
||||
// At this point, `suffix_first` is built, and
|
||||
// `my_suffix` is some TokenSet that we can use
|
||||
// for checking the interior of `seq_rep`.
|
||||
let next = check_matcher_core(sess, def, first_sets, &seq_rep.tts, my_suffix)?;
|
||||
let next = check_matcher_core(sess, node_id, first_sets, &seq_rep.tts, my_suffix)?;
|
||||
if next.maybe_empty {
|
||||
last.add_all(&next);
|
||||
} else {
|
||||
@ -1144,7 +1132,7 @@ fn check_matcher_core<'tt>(
|
||||
// macro. (See #86567.)
|
||||
// Macros defined in the current crate have a real node id,
|
||||
// whereas macros from an external crate have a dummy id.
|
||||
if def.id != DUMMY_NODE_ID
|
||||
if node_id != DUMMY_NODE_ID
|
||||
&& matches!(kind, NonterminalKind::Pat(PatParam { inferred: true }))
|
||||
&& matches!(
|
||||
next_token,
|
||||
|
@ -23,11 +23,11 @@ pub(crate) enum MetaVarExpr {
|
||||
/// Ignore a meta-variable for repetition without expansion.
|
||||
Ignore(Ident),
|
||||
|
||||
/// The index of the repetition at a particular depth, where 0 is the inner-most
|
||||
/// The index of the repetition at a particular depth, where 0 is the innermost
|
||||
/// repetition. The `usize` is the depth.
|
||||
Index(usize),
|
||||
|
||||
/// The length of the repetition at a particular depth, where 0 is the inner-most
|
||||
/// The length of the repetition at a particular depth, where 0 is the innermost
|
||||
/// repetition. The `usize` is the depth.
|
||||
Len(usize),
|
||||
}
|
||||
|
@ -119,16 +119,16 @@ pub(super) fn parse(
|
||||
result
|
||||
}
|
||||
|
||||
/// Asks for the `macro_metavar_expr` feature if it is not already declared
|
||||
/// Asks for the `macro_metavar_expr` feature if it is not enabled
|
||||
fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
|
||||
if !features.macro_metavar_expr {
|
||||
if !features.macro_metavar_expr() {
|
||||
let msg = "meta-variable expressions are unstable";
|
||||
feature_err(sess, sym::macro_metavar_expr, span, msg).emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) {
|
||||
if !features.macro_metavar_expr_concat {
|
||||
if !features.macro_metavar_expr_concat() {
|
||||
let msg = "the `concat` meta-variable expression is unstable";
|
||||
feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit();
|
||||
}
|
||||
|
@ -570,7 +570,7 @@ fn lockstep_iter_size(
|
||||
}
|
||||
}
|
||||
|
||||
/// Used solely by the `count` meta-variable expression, counts the outer-most repetitions at a
|
||||
/// Used solely by the `count` meta-variable expression, counts the outermost repetitions at a
|
||||
/// given optional nested depth.
|
||||
///
|
||||
/// For example, a macro parameter of `$( { $( $foo:ident ),* } )*` called with `{ a, b } { c }`:
|
||||
|
@ -9,7 +9,7 @@ macro_rules! declare_features {
|
||||
$(#[doc = $doc:tt])* (accepted, $feature:ident, $ver:expr, $issue:expr),
|
||||
)+) => {
|
||||
/// Formerly unstable features that have now been accepted (stabilized).
|
||||
pub const ACCEPTED_FEATURES: &[Feature] = &[
|
||||
pub const ACCEPTED_LANG_FEATURES: &[Feature] = &[
|
||||
$(Feature {
|
||||
name: sym::$feature,
|
||||
since: $ver,
|
||||
@ -364,6 +364,8 @@ declare_features! (
|
||||
(accepted, self_in_typedefs, "1.32.0", Some(49303)),
|
||||
/// Allows `Self` struct constructor (RFC 2302).
|
||||
(accepted, self_struct_ctor, "1.32.0", Some(51994)),
|
||||
/// Shortern the tail expression lifetime
|
||||
(accepted, shorter_tail_lifetimes, "CURRENT_RUSTC_VERSION", Some(123739)),
|
||||
/// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`.
|
||||
(accepted, slice_patterns, "1.42.0", Some(62254)),
|
||||
/// Allows use of `&foo[a..b]` as a slicing syntax.
|
||||
|
@ -12,33 +12,31 @@ use crate::{Features, Stability};
|
||||
|
||||
type GateFn = fn(&Features) -> bool;
|
||||
|
||||
macro_rules! cfg_fn {
|
||||
($field: ident) => {
|
||||
(|features| features.$field) as GateFn
|
||||
};
|
||||
}
|
||||
|
||||
pub type GatedCfg = (Symbol, Symbol, GateFn);
|
||||
|
||||
/// `cfg(...)`'s that are feature gated.
|
||||
const GATED_CFGS: &[GatedCfg] = &[
|
||||
// (name in cfg, feature, function to check if the feature is enabled)
|
||||
(sym::overflow_checks, sym::cfg_overflow_checks, cfg_fn!(cfg_overflow_checks)),
|
||||
(sym::ub_checks, sym::cfg_ub_checks, cfg_fn!(cfg_ub_checks)),
|
||||
(sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
|
||||
(sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
|
||||
(sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
|
||||
(sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
|
||||
(
|
||||
sym::target_has_atomic_equal_alignment,
|
||||
sym::cfg_target_has_atomic_equal_alignment,
|
||||
cfg_fn!(cfg_target_has_atomic_equal_alignment),
|
||||
Features::cfg_target_has_atomic_equal_alignment,
|
||||
),
|
||||
(sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
|
||||
(sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
|
||||
(sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
|
||||
(sym::relocation_model, sym::cfg_relocation_model, cfg_fn!(cfg_relocation_model)),
|
||||
(sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)),
|
||||
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)),
|
||||
(
|
||||
sym::target_has_atomic_load_store,
|
||||
sym::cfg_target_has_atomic,
|
||||
Features::cfg_target_has_atomic,
|
||||
),
|
||||
(sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize),
|
||||
(sym::version, sym::cfg_version, Features::cfg_version),
|
||||
(sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model),
|
||||
(sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
|
||||
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
|
||||
// this is consistent with naming of the compiler flag it's for
|
||||
(sym::fmt_debug, sym::fmt_debug, cfg_fn!(fmt_debug)),
|
||||
(sym::fmt_debug, sym::fmt_debug, Features::fmt_debug),
|
||||
];
|
||||
|
||||
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
|
||||
@ -220,7 +218,7 @@ macro_rules! gated {
|
||||
safety: AttributeSafety::Unsafe,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
|
||||
gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
|
||||
}
|
||||
};
|
||||
(unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
|
||||
@ -231,7 +229,7 @@ macro_rules! gated {
|
||||
safety: AttributeSafety::Unsafe,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
|
||||
gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
|
||||
}
|
||||
};
|
||||
($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
|
||||
@ -242,7 +240,7 @@ macro_rules! gated {
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
|
||||
gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
|
||||
}
|
||||
};
|
||||
($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
|
||||
@ -253,7 +251,7 @@ macro_rules! gated {
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
|
||||
gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -282,7 +280,7 @@ macro_rules! rustc_attr {
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)),
|
||||
gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, Features::rustc_attrs),
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -619,11 +617,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
DuplicatesOk, EncodeCrossCrate::Yes,
|
||||
"allow_internal_unstable side-steps feature gating and stability checks",
|
||||
),
|
||||
gated!(
|
||||
rustc_allow_const_fn_unstable, Normal,
|
||||
template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No,
|
||||
"rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
|
||||
),
|
||||
gated!(
|
||||
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
|
||||
@ -841,8 +834,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_runtime, Normal, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, INTERNAL_UNSTABLE
|
||||
rustc_const_stable_indirect, Normal,
|
||||
template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL,
|
||||
),
|
||||
gated!(
|
||||
rustc_allow_const_fn_unstable, Normal,
|
||||
template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No,
|
||||
"rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
@ -935,7 +933,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
Stability::Unstable,
|
||||
sym::rustc_attrs,
|
||||
"diagnostic items compiler internal support for linting",
|
||||
cfg_fn!(rustc_attrs),
|
||||
Features::rustc_attrs,
|
||||
),
|
||||
},
|
||||
gated!(
|
||||
@ -1193,7 +1191,7 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>>
|
||||
pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool {
|
||||
match sym {
|
||||
sym::on_unimplemented => true,
|
||||
sym::do_not_recommend => features.do_not_recommend,
|
||||
sym::do_not_recommend => features.do_not_recommend(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -94,13 +94,13 @@ impl UnstableFeatures {
|
||||
|
||||
fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> {
|
||||
// Search in all the feature lists.
|
||||
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| f.feature.name == feature) {
|
||||
return f.feature.issue;
|
||||
}
|
||||
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| f.name == feature) {
|
||||
if let Some(f) = UNSTABLE_LANG_FEATURES.iter().find(|f| f.name == feature) {
|
||||
return f.issue;
|
||||
}
|
||||
if let Some(f) = REMOVED_FEATURES.iter().find(|f| f.feature.name == feature) {
|
||||
if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature) {
|
||||
return f.issue;
|
||||
}
|
||||
if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) {
|
||||
return f.feature.issue;
|
||||
}
|
||||
panic!("feature `{feature}` is not declared anywhere");
|
||||
@ -127,12 +127,14 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
|
||||
}
|
||||
}
|
||||
|
||||
pub use accepted::ACCEPTED_FEATURES;
|
||||
pub use accepted::ACCEPTED_LANG_FEATURES;
|
||||
pub use builtin_attrs::{
|
||||
AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
|
||||
BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, deprecated_attributes,
|
||||
encode_cross_crate, find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute,
|
||||
is_valid_for_get_attr,
|
||||
};
|
||||
pub use removed::REMOVED_FEATURES;
|
||||
pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES};
|
||||
pub use removed::REMOVED_LANG_FEATURES;
|
||||
pub use unstable::{
|
||||
EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES,
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ macro_rules! declare_features {
|
||||
$(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, $reason:expr),
|
||||
)+) => {
|
||||
/// Formerly unstable features that have now been removed.
|
||||
pub const REMOVED_FEATURES: &[RemovedFeature] = &[
|
||||
pub const REMOVED_LANG_FEATURES: &[RemovedFeature] = &[
|
||||
$(RemovedFeature {
|
||||
feature: Feature {
|
||||
name: sym::$feature,
|
||||
@ -85,6 +85,8 @@ declare_features! (
|
||||
/// Allows default type parameters to influence type inference.
|
||||
(removed, default_type_parameter_fallback, "1.82.0", Some(27336),
|
||||
Some("never properly implemented; requires significant design work")),
|
||||
/// Allows deriving traits as per `SmartPointer` specification
|
||||
(removed, derive_smart_pointer, "1.79.0", Some(123430), Some("replaced by `CoercePointee`")),
|
||||
/// Allows using `#[doc(keyword = "...")]`.
|
||||
(removed, doc_keyword, "1.28.0", Some(51315),
|
||||
Some("merged into `#![feature(rustdoc_internals)]`")),
|
||||
|
@ -6,11 +6,6 @@ use rustc_span::symbol::{Symbol, sym};
|
||||
|
||||
use super::{Feature, to_nonzero};
|
||||
|
||||
pub struct UnstableFeature {
|
||||
pub feature: Feature,
|
||||
pub set_enabled: fn(&mut Features),
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum FeatureStatus {
|
||||
Default,
|
||||
@ -30,86 +25,98 @@ macro_rules! status_to_enum {
|
||||
};
|
||||
}
|
||||
|
||||
/// A set of features to be used by later passes.
|
||||
///
|
||||
/// There are two ways to check if a language feature `foo` is enabled:
|
||||
/// - Directly with the `foo` method, e.g. `if tcx.features().foo() { ... }`.
|
||||
/// - With the `enabled` method, e.g. `if tcx.features.enabled(sym::foo) { ... }`.
|
||||
///
|
||||
/// The former is preferred. `enabled` should only be used when the feature symbol is not a
|
||||
/// constant, e.g. a parameter, or when the feature is a library feature.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Features {
|
||||
/// `#![feature]` attrs for language features, for error reporting.
|
||||
enabled_lang_features: Vec<EnabledLangFeature>,
|
||||
/// `#![feature]` attrs for non-language (library) features.
|
||||
enabled_lib_features: Vec<EnabledLibFeature>,
|
||||
/// `enabled_lang_features` + `enabled_lib_features`.
|
||||
enabled_features: FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
/// Information about an enabled language feature.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EnabledLangFeature {
|
||||
/// Name of the feature gate guarding the language feature.
|
||||
pub gate_name: Symbol,
|
||||
/// Span of the `#[feature(...)]` attribute.
|
||||
pub attr_sp: Span,
|
||||
/// If the lang feature is stable, the version number when it was stabilized.
|
||||
pub stable_since: Option<Symbol>,
|
||||
}
|
||||
|
||||
/// Information abhout an enabled library feature.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EnabledLibFeature {
|
||||
pub gate_name: Symbol,
|
||||
pub attr_sp: Span,
|
||||
}
|
||||
|
||||
impl Features {
|
||||
/// `since` should be set for stable features that are nevertheless enabled with a `#[feature]`
|
||||
/// attribute, indicating since when they are stable.
|
||||
pub fn set_enabled_lang_feature(&mut self, lang_feat: EnabledLangFeature) {
|
||||
self.enabled_lang_features.push(lang_feat);
|
||||
self.enabled_features.insert(lang_feat.gate_name);
|
||||
}
|
||||
|
||||
pub fn set_enabled_lib_feature(&mut self, lib_feat: EnabledLibFeature) {
|
||||
self.enabled_lib_features.push(lib_feat);
|
||||
self.enabled_features.insert(lib_feat.gate_name);
|
||||
}
|
||||
|
||||
/// Returns a list of [`EnabledLangFeature`] with info about:
|
||||
///
|
||||
/// - Feature gate name.
|
||||
/// - The span of the `#[feature]` attribute.
|
||||
/// - For stable language features, version info for when it was stabilized.
|
||||
pub fn enabled_lang_features(&self) -> &Vec<EnabledLangFeature> {
|
||||
&self.enabled_lang_features
|
||||
}
|
||||
|
||||
pub fn enabled_lib_features(&self) -> &Vec<EnabledLibFeature> {
|
||||
&self.enabled_lib_features
|
||||
}
|
||||
|
||||
pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
|
||||
&self.enabled_features
|
||||
}
|
||||
|
||||
/// Is the given feature enabled (via `#[feature(...)]`)?
|
||||
pub fn enabled(&self, feature: Symbol) -> bool {
|
||||
self.enabled_features.contains(&feature)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! declare_features {
|
||||
($(
|
||||
$(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr),
|
||||
)+) => {
|
||||
/// Unstable language features that are being implemented or being
|
||||
/// considered for acceptance (stabilization) or removal.
|
||||
pub const UNSTABLE_FEATURES: &[UnstableFeature] = &[
|
||||
$(UnstableFeature {
|
||||
feature: Feature {
|
||||
pub const UNSTABLE_LANG_FEATURES: &[Feature] = &[
|
||||
$(Feature {
|
||||
name: sym::$feature,
|
||||
since: $ver,
|
||||
issue: to_nonzero($issue),
|
||||
},
|
||||
// Sets this feature's corresponding bool within `features`.
|
||||
set_enabled: |features| features.$feature = true,
|
||||
}),+
|
||||
];
|
||||
|
||||
const NUM_FEATURES: usize = UNSTABLE_FEATURES.len();
|
||||
|
||||
/// A set of features to be used by later passes.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Features {
|
||||
/// `#![feature]` attrs for language features, for error reporting.
|
||||
/// "declared" here means that the feature is actually enabled in the current crate.
|
||||
pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
|
||||
/// `#![feature]` attrs for non-language (library) features.
|
||||
/// "declared" here means that the feature is actually enabled in the current crate.
|
||||
pub declared_lib_features: Vec<(Symbol, Span)>,
|
||||
/// `declared_lang_features` + `declared_lib_features`.
|
||||
pub declared_features: FxHashSet<Symbol>,
|
||||
/// Active state of individual features (unstable only).
|
||||
$(
|
||||
$(#[doc = $doc])*
|
||||
pub $feature: bool
|
||||
),+
|
||||
}
|
||||
|
||||
impl Features {
|
||||
pub fn set_declared_lang_feature(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
span: Span,
|
||||
since: Option<Symbol>
|
||||
) {
|
||||
self.declared_lang_features.push((symbol, span, since));
|
||||
self.declared_features.insert(symbol);
|
||||
}
|
||||
|
||||
pub fn set_declared_lib_feature(&mut self, symbol: Symbol, span: Span) {
|
||||
self.declared_lib_features.push((symbol, span));
|
||||
self.declared_features.insert(symbol);
|
||||
}
|
||||
|
||||
/// This is intended for hashing the set of active features.
|
||||
///
|
||||
/// The expectation is that this produces much smaller code than other alternatives.
|
||||
///
|
||||
/// Note that the total feature count is pretty small, so this is not a huge array.
|
||||
#[inline]
|
||||
pub fn all_features(&self) -> [u8; NUM_FEATURES] {
|
||||
[$(self.$feature as u8),+]
|
||||
}
|
||||
|
||||
/// Is the given feature explicitly declared, i.e. named in a
|
||||
/// `#![feature(...)]` within the code?
|
||||
pub fn declared(&self, feature: Symbol) -> bool {
|
||||
self.declared_features.contains(&feature)
|
||||
}
|
||||
|
||||
/// Is the given feature active (enabled by the user)?
|
||||
///
|
||||
/// Panics if the symbol doesn't correspond to a declared feature.
|
||||
pub fn active(&self, feature: Symbol) -> bool {
|
||||
match feature {
|
||||
$( sym::$feature => self.$feature, )*
|
||||
|
||||
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
||||
}
|
||||
$(
|
||||
pub fn $feature(&self) -> bool {
|
||||
self.enabled_features.contains(&sym::$feature)
|
||||
}
|
||||
)*
|
||||
|
||||
/// Some features are known to be incomplete and using them is likely to have
|
||||
/// unanticipated results, such as compiler crashes. We warn the user about these
|
||||
@ -119,8 +126,11 @@ macro_rules! declare_features {
|
||||
$(
|
||||
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
|
||||
)*
|
||||
// Accepted/removed features aren't in this file but are never incomplete.
|
||||
_ if self.declared_features.contains(&feature) => false,
|
||||
_ if self.enabled_features.contains(&feature) => {
|
||||
// Accepted/removed features and library features aren't in this file but
|
||||
// are never incomplete.
|
||||
false
|
||||
}
|
||||
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
||||
}
|
||||
}
|
||||
@ -132,7 +142,7 @@ macro_rules! declare_features {
|
||||
$(
|
||||
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
|
||||
)*
|
||||
_ if self.declared_features.contains(&feature) => {
|
||||
_ if self.enabled_features.contains(&feature) => {
|
||||
// This could be accepted/removed, or a libs feature.
|
||||
// Accepted/removed features aren't in this file but are never internal
|
||||
// (a removed feature might have been internal, but that's now irrelevant).
|
||||
@ -440,8 +450,6 @@ declare_features! (
|
||||
(unstable, deprecated_suggestion, "1.61.0", Some(94785)),
|
||||
/// Allows deref patterns.
|
||||
(incomplete, deref_patterns, "1.79.0", Some(87121)),
|
||||
/// Allows deriving `SmartPointer` traits
|
||||
(unstable, derive_smart_pointer, "1.79.0", Some(123430)),
|
||||
/// Controls errors in trait implementations.
|
||||
(unstable, do_not_recommend, "1.67.0", Some(51992)),
|
||||
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
||||
@ -586,8 +594,6 @@ declare_features! (
|
||||
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
|
||||
/// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics
|
||||
(unstable, sha512_sm_x86, "1.82.0", Some(126624)),
|
||||
/// Shortern the tail expression lifetime
|
||||
(unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
(unstable, simd_ffi, "1.0.0", Some(27731)),
|
||||
/// Allows specialization of implementations (RFC 1210).
|
||||
|
@ -6,8 +6,8 @@ use rustc_ast::{
|
||||
LitKind, TraitObjectSyntax, UintTy,
|
||||
};
|
||||
pub use rustc_ast::{
|
||||
BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability,
|
||||
Mutability, UnOp,
|
||||
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
|
||||
ImplPolarity, IsAuto, Movability, Mutability, UnOp,
|
||||
};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
@ -502,19 +502,16 @@ pub enum GenericArgsParentheses {
|
||||
ParenSugar,
|
||||
}
|
||||
|
||||
/// A modifier on a trait bound.
|
||||
/// The modifiers on a trait bound.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
pub enum TraitBoundModifier {
|
||||
/// `Type: Trait`
|
||||
None,
|
||||
/// `Type: !Trait`
|
||||
Negative,
|
||||
/// `Type: ?Trait`
|
||||
Maybe,
|
||||
/// `Type: const Trait`
|
||||
Const,
|
||||
/// `Type: ~const Trait`
|
||||
MaybeConst,
|
||||
pub struct TraitBoundModifiers {
|
||||
pub constness: BoundConstness,
|
||||
pub polarity: BoundPolarity,
|
||||
}
|
||||
|
||||
impl TraitBoundModifiers {
|
||||
pub const NONE: Self =
|
||||
TraitBoundModifiers { constness: BoundConstness::Never, polarity: BoundPolarity::Positive };
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, HashStable_Generic)]
|
||||
@ -583,7 +580,6 @@ pub enum GenericParamKind<'hir> {
|
||||
ty: &'hir Ty<'hir>,
|
||||
/// Optional default value for the const generic param
|
||||
default: Option<&'hir ConstArg<'hir>>,
|
||||
is_host_effect: bool,
|
||||
synthetic: bool,
|
||||
},
|
||||
}
|
||||
@ -3180,7 +3176,7 @@ pub struct PolyTraitRef<'hir> {
|
||||
/// The constness and polarity of the trait ref.
|
||||
///
|
||||
/// The `async` modifier is lowered directly into a different trait for now.
|
||||
pub modifiers: TraitBoundModifier,
|
||||
pub modifiers: TraitBoundModifiers,
|
||||
|
||||
/// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`.
|
||||
pub trait_ref: TraitRef<'hir>,
|
||||
@ -4085,7 +4081,7 @@ mod size_asserts {
|
||||
static_assert_size!(ForeignItem<'_>, 88);
|
||||
static_assert_size!(ForeignItemKind<'_>, 56);
|
||||
static_assert_size!(GenericArg<'_>, 16);
|
||||
static_assert_size!(GenericBound<'_>, 48);
|
||||
static_assert_size!(GenericBound<'_>, 64);
|
||||
static_assert_size!(Generics<'_>, 56);
|
||||
static_assert_size!(Impl<'_>, 80);
|
||||
static_assert_size!(ImplItem<'_>, 88);
|
||||
|
@ -935,7 +935,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
|
||||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {}
|
||||
GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default),
|
||||
GenericParamKind::Const { ref ty, ref default, is_host_effect: _, synthetic: _ } => {
|
||||
GenericParamKind::Const { ref ty, ref default, synthetic: _ } => {
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
if let Some(ref default) = default {
|
||||
try_visit!(visitor.visit_const_param_default(param.hir_id, default));
|
||||
|
@ -241,7 +241,7 @@ language_item_table! {
|
||||
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefPure, sym::deref_pure, deref_pure_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
|
||||
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
LegacyReceiver, sym::legacy_receiver, legacy_receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
@ -415,14 +415,6 @@ language_item_table! {
|
||||
|
||||
String, sym::String, string, Target::Struct, GenericRequirement::None;
|
||||
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
||||
|
||||
EffectsRuntime, sym::EffectsRuntime, effects_runtime, Target::Struct, GenericRequirement::None;
|
||||
EffectsNoRuntime, sym::EffectsNoRuntime, effects_no_runtime, Target::Struct, GenericRequirement::None;
|
||||
EffectsMaybe, sym::EffectsMaybe, effects_maybe, Target::Struct, GenericRequirement::None;
|
||||
EffectsIntersection, sym::EffectsIntersection, effects_intersection, Target::Trait, GenericRequirement::None;
|
||||
EffectsIntersectionOutput, sym::EffectsIntersectionOutput, effects_intersection_output, Target::AssocTy, GenericRequirement::None;
|
||||
EffectsCompat, sym::EffectsCompat, effects_compat, Target::Trait, GenericRequirement::Exact(1);
|
||||
EffectsTyCompat, sym::EffectsTyCompat, effects_ty_compat, Target::Trait, GenericRequirement::Exact(1);
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
|
@ -1,15 +1,9 @@
|
||||
//! Bounds are restrictions applied to some types after they've been lowered from the HIR to the
|
||||
//! [`rustc_middle::ty`] form.
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::ty::fold::FnMutDelegate;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::DefId;
|
||||
|
||||
use crate::hir_ty_lowering::PredicateFilter;
|
||||
|
||||
/// Collects together a list of type bounds. These lists of bounds occur in many places
|
||||
/// in Rust's syntax:
|
||||
@ -30,7 +24,6 @@ use crate::hir_ty_lowering::PredicateFilter;
|
||||
#[derive(Default, PartialEq, Eq, Clone, Debug)]
|
||||
pub(crate) struct Bounds<'tcx> {
|
||||
clauses: Vec<(ty::Clause<'tcx>, Span)>,
|
||||
effects_min_tys: FxIndexMap<Ty<'tcx>, Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Bounds<'tcx> {
|
||||
@ -47,12 +40,9 @@ impl<'tcx> Bounds<'tcx> {
|
||||
pub(crate) fn push_trait_bound(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
defining_def_id: DefId,
|
||||
bound_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
span: Span,
|
||||
polarity: ty::PredicatePolarity,
|
||||
constness: Option<ty::BoundConstness>,
|
||||
predicate_filter: PredicateFilter,
|
||||
) {
|
||||
let clause = (
|
||||
bound_trait_ref
|
||||
@ -68,137 +58,6 @@ impl<'tcx> Bounds<'tcx> {
|
||||
} else {
|
||||
self.clauses.push(clause);
|
||||
}
|
||||
|
||||
// FIXME(effects): Lift this out of `push_trait_bound`, and move it somewhere else.
|
||||
// Perhaps moving this into `lower_poly_trait_ref`, just like we lower associated
|
||||
// type bounds.
|
||||
if !tcx.features().effects {
|
||||
return;
|
||||
}
|
||||
match predicate_filter {
|
||||
PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {
|
||||
return;
|
||||
}
|
||||
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
|
||||
// Ok.
|
||||
}
|
||||
}
|
||||
|
||||
// For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the
|
||||
// associated type of `<T as Tr>` and make sure that the effect is compatible.
|
||||
let compat_val = match (tcx.def_kind(defining_def_id), constness) {
|
||||
// FIXME(effects): revisit the correctness of this
|
||||
(_, Some(ty::BoundConstness::Const)) => tcx.consts.false_,
|
||||
// body owners that can have trait bounds
|
||||
(
|
||||
DefKind::Const | DefKind::Fn | DefKind::AssocFn,
|
||||
Some(ty::BoundConstness::ConstIfConst),
|
||||
) => tcx.expected_host_effect_param_for_body(defining_def_id),
|
||||
|
||||
(_, None) => {
|
||||
if !tcx.is_const_trait(bound_trait_ref.def_id()) {
|
||||
return;
|
||||
}
|
||||
tcx.consts.true_
|
||||
}
|
||||
(DefKind::Trait, Some(ty::BoundConstness::ConstIfConst)) => {
|
||||
// we are in a trait, where `bound_trait_ref` could be:
|
||||
// (1) a super trait `trait Foo: ~const Bar`.
|
||||
// - This generates `<Self as Foo>::Effects: TyCompat<<Self as Bar>::Effects>`
|
||||
//
|
||||
// (2) a where clause `where for<..> Something: ~const Bar`.
|
||||
// - This generates `for<..> <Self as Foo>::Effects: TyCompat<<Something as Bar>::Effects>`
|
||||
let Some(own_fx) = tcx.associated_type_for_effects(defining_def_id) else {
|
||||
tcx.dcx().span_delayed_bug(span, "should not have allowed `~const` on a trait that doesn't have `#[const_trait]`");
|
||||
return;
|
||||
};
|
||||
let own_fx_ty = Ty::new_projection(
|
||||
tcx,
|
||||
own_fx,
|
||||
ty::GenericArgs::identity_for_item(tcx, own_fx),
|
||||
);
|
||||
let Some(their_fx) = tcx.associated_type_for_effects(bound_trait_ref.def_id())
|
||||
else {
|
||||
tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc");
|
||||
return;
|
||||
};
|
||||
let their_fx_ty =
|
||||
Ty::new_projection(tcx, their_fx, bound_trait_ref.skip_binder().args);
|
||||
let compat = tcx.require_lang_item(LangItem::EffectsTyCompat, Some(span));
|
||||
let clause = bound_trait_ref
|
||||
.map_bound(|_| {
|
||||
let trait_ref = ty::TraitRef::new(tcx, compat, [own_fx_ty, their_fx_ty]);
|
||||
ty::ClauseKind::Trait(ty::TraitPredicate {
|
||||
trait_ref,
|
||||
polarity: ty::PredicatePolarity::Positive,
|
||||
})
|
||||
})
|
||||
.upcast(tcx);
|
||||
|
||||
self.clauses.push((clause, span));
|
||||
return;
|
||||
}
|
||||
|
||||
(DefKind::Impl { of_trait: true }, Some(ty::BoundConstness::ConstIfConst)) => {
|
||||
// this is a where clause on an impl header.
|
||||
// push `<T as Tr>::Effects` into the set for the `Min` bound.
|
||||
let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
|
||||
tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc");
|
||||
return;
|
||||
};
|
||||
|
||||
let ty = bound_trait_ref
|
||||
.map_bound(|trait_ref| Ty::new_projection(tcx, assoc, trait_ref.args));
|
||||
|
||||
// When the user has written `for<'a, T> X<'a, T>: ~const Foo`, replace the
|
||||
// binders to dummy ones i.e. `X<'static, ()>` so they can be referenced in
|
||||
// the `Min` associated type properly (which doesn't allow using `for<>`)
|
||||
// This should work for any bound variables as long as they don't have any
|
||||
// bounds e.g. `for<T: Trait>`.
|
||||
// FIXME(effects) reconsider this approach to allow compatibility with `for<T: Tr>`
|
||||
let ty = tcx.replace_bound_vars_uncached(ty, FnMutDelegate {
|
||||
regions: &mut |_| tcx.lifetimes.re_static,
|
||||
types: &mut |_| tcx.types.unit,
|
||||
consts: &mut |_| unimplemented!("`~const` does not support const binders"),
|
||||
});
|
||||
|
||||
self.effects_min_tys.insert(ty, span);
|
||||
return;
|
||||
}
|
||||
// for
|
||||
// ```
|
||||
// trait Foo { type Bar: ~const Trait }
|
||||
// ```
|
||||
// ensure that `<Self::Bar as Trait>::Effects: TyCompat<Self::Effects>`.
|
||||
//
|
||||
// FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor
|
||||
// that uses a `Bar` that implements `Trait` with `Maybe` effects.
|
||||
(DefKind::AssocTy, Some(ty::BoundConstness::ConstIfConst)) => {
|
||||
// FIXME(effects): implement this
|
||||
return;
|
||||
}
|
||||
// probably illegal in this position.
|
||||
(_, Some(ty::BoundConstness::ConstIfConst)) => {
|
||||
tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered");
|
||||
return;
|
||||
}
|
||||
};
|
||||
// create a new projection type `<T as Tr>::Effects`
|
||||
let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
|
||||
tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
"`~const` trait bound has no effect assoc yet no errors encountered?",
|
||||
);
|
||||
return;
|
||||
};
|
||||
let self_ty = Ty::new_projection(tcx, assoc, bound_trait_ref.skip_binder().args);
|
||||
// make `<T as Tr>::Effects: Compat<runtime>`
|
||||
let new_trait_ref =
|
||||
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::EffectsCompat, Some(span)), [
|
||||
ty::GenericArg::from(self_ty),
|
||||
compat_val.into(),
|
||||
]);
|
||||
self.clauses.push((bound_trait_ref.rebind(new_trait_ref).upcast(tcx), span));
|
||||
}
|
||||
|
||||
pub(crate) fn push_projection_bound(
|
||||
@ -220,15 +79,22 @@ impl<'tcx> Bounds<'tcx> {
|
||||
self.clauses.insert(0, (trait_ref.upcast(tcx), span));
|
||||
}
|
||||
|
||||
pub(crate) fn clauses(
|
||||
&self,
|
||||
// FIXME(effects): remove tcx
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
|
||||
self.clauses.iter().cloned()
|
||||
/// Push a `const` or `~const` bound as a `HostEffect` predicate.
|
||||
pub(crate) fn push_const_bound(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
bound_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
host: ty::HostPolarity,
|
||||
span: Span,
|
||||
) {
|
||||
if tcx.is_const_trait(bound_trait_ref.def_id()) {
|
||||
self.clauses.push((bound_trait_ref.to_host_effect_clause(tcx, host), span));
|
||||
} else {
|
||||
tcx.dcx().span_delayed_bug(span, "tried to lower {host:?} bound for non-const trait");
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn effects_min_tys(&self) -> impl Iterator<Item = Ty<'tcx>> + '_ {
|
||||
self.effects_min_tys.keys().copied()
|
||||
pub(crate) fn clauses(&self) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
|
||||
self.clauses.iter().cloned()
|
||||
}
|
||||
}
|
||||
|
@ -1166,7 +1166,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
|
||||
return;
|
||||
}
|
||||
|
||||
if adt.is_union() && !tcx.features().transparent_unions {
|
||||
if adt.is_union() && !tcx.features().transparent_unions() {
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::transparent_unions,
|
||||
@ -1301,7 +1301,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
|
||||
let repr_type_ty = def.repr().discr_type().to_ty(tcx);
|
||||
if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
|
||||
if !tcx.features().repr128 {
|
||||
if !tcx.features().repr128() {
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
sym::repr128,
|
||||
|
@ -43,14 +43,13 @@ mod refine;
|
||||
/// - `impl_m`: type of the method we are checking
|
||||
/// - `trait_m`: the method in the trait
|
||||
/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub(super) fn compare_impl_method<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_m: ty::AssocItem,
|
||||
trait_m: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) {
|
||||
debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
|
||||
|
||||
let _: Result<_, ErrorGuaranteed> = try {
|
||||
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
|
||||
compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
|
||||
@ -167,8 +166,6 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
trait_m: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let trait_to_impl_args = impl_trait_ref.args;
|
||||
|
||||
// This node-id should be used for the `body_id` field on each
|
||||
// `ObligationCause` (and the `FnCtxt`).
|
||||
//
|
||||
@ -183,27 +180,18 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
kind: impl_m.kind,
|
||||
});
|
||||
|
||||
// Create mapping from impl to placeholder.
|
||||
let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id);
|
||||
|
||||
// Create mapping from trait to placeholder.
|
||||
let trait_to_placeholder_args =
|
||||
impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args);
|
||||
debug!("compare_impl_method: trait_to_placeholder_args={:?}", trait_to_placeholder_args);
|
||||
// Create mapping from trait method to impl method.
|
||||
let impl_def_id = impl_m.container_id(tcx);
|
||||
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto(
|
||||
tcx,
|
||||
impl_m.container_id(tcx),
|
||||
impl_trait_ref.args,
|
||||
);
|
||||
debug!(?trait_to_impl_args);
|
||||
|
||||
let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
|
||||
let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
|
||||
|
||||
// Create obligations for each predicate declared by the impl
|
||||
// definition in the context of the trait's parameter
|
||||
// environment. We can't just use `impl_env.caller_bounds`,
|
||||
// however, because we want to replace all late-bound regions with
|
||||
// region variables.
|
||||
let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
|
||||
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
|
||||
|
||||
debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);
|
||||
|
||||
// This is the only tricky bit of the new way we check implementation methods
|
||||
// We need to build a set of predicates where only the method-level bounds
|
||||
// are from the trait and we assume all other bounds from the implementation
|
||||
@ -211,25 +199,43 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
//
|
||||
// We then register the obligations from the impl_m and check to see
|
||||
// if all constraints hold.
|
||||
hybrid_preds.predicates.extend(
|
||||
trait_m_predicates
|
||||
.instantiate_own(tcx, trait_to_placeholder_args)
|
||||
.map(|(predicate, _)| predicate),
|
||||
let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
|
||||
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
|
||||
hybrid_preds.extend(
|
||||
trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate),
|
||||
);
|
||||
|
||||
// Construct trait parameter environment and then shift it into the placeholder viewpoint.
|
||||
// The key step here is to update the caller_bounds's predicates to be
|
||||
// the new hybrid bounds we computed.
|
||||
// FIXME(effects): This should be replaced with a more dedicated method.
|
||||
let is_conditionally_const = tcx.is_conditionally_const(impl_def_id);
|
||||
if is_conditionally_const {
|
||||
// Augment the hybrid param-env with the const conditions
|
||||
// of the impl header and the trait method.
|
||||
hybrid_preds.extend(
|
||||
tcx.const_conditions(impl_def_id)
|
||||
.instantiate_identity(tcx)
|
||||
.into_iter()
|
||||
.chain(
|
||||
tcx.const_conditions(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args),
|
||||
)
|
||||
.map(|(trait_ref, _)| {
|
||||
trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
|
||||
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
|
||||
debug!(caller_bounds=?param_env.caller_bounds());
|
||||
|
||||
let infcx = &tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
|
||||
|
||||
debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
|
||||
|
||||
let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_args);
|
||||
// Create obligations for each predicate declared by the impl
|
||||
// definition in the context of the hybrid param-env. This makes
|
||||
// sure that the impl's method's where clauses are not more
|
||||
// restrictive than the trait's method (and the impl itself).
|
||||
let impl_m_own_bounds = impl_m_predicates.instantiate_own_identity();
|
||||
for (predicate, span) in impl_m_own_bounds {
|
||||
let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
|
||||
let predicate = ocx.normalize(&normalize_cause, param_env, predicate);
|
||||
@ -243,6 +249,34 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
|
||||
}
|
||||
|
||||
// If we're within a const implementation, we need to make sure that the method
|
||||
// does not assume stronger `~const` bounds than the trait definition.
|
||||
//
|
||||
// This registers the `~const` bounds of the impl method, which we will prove
|
||||
// using the hybrid param-env that we earlier augmented with the const conditions
|
||||
// from the impl header and trait method declaration.
|
||||
if is_conditionally_const {
|
||||
for (const_condition, span) in
|
||||
tcx.const_conditions(impl_m.def_id).instantiate_own_identity()
|
||||
{
|
||||
let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
|
||||
let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition);
|
||||
|
||||
let cause =
|
||||
ObligationCause::new(span, impl_m_def_id, ObligationCauseCode::CompareImplItem {
|
||||
impl_item_def_id: impl_m_def_id,
|
||||
trait_item_def_id: trait_m.def_id,
|
||||
kind: impl_m.kind,
|
||||
});
|
||||
ocx.register_obligation(traits::Obligation::new(
|
||||
tcx,
|
||||
cause,
|
||||
param_env,
|
||||
const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// We now need to check that the signature of the impl method is
|
||||
// compatible with that of the trait method. We do this by
|
||||
// checking that `impl_fty <: trait_fty`.
|
||||
@ -256,7 +290,6 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
// any associated types appearing in the fn arguments or return
|
||||
// type.
|
||||
|
||||
// Compute placeholder form of impl and trait method tys.
|
||||
let mut wf_tys = FxIndexSet::default();
|
||||
|
||||
let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars(
|
||||
@ -267,9 +300,9 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
|
||||
let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id);
|
||||
let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig);
|
||||
debug!("compare_impl_method: impl_fty={:?}", impl_sig);
|
||||
debug!(?impl_sig);
|
||||
|
||||
let trait_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_placeholder_args);
|
||||
let trait_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args);
|
||||
let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
|
||||
|
||||
// Next, add all inputs and output as well-formed tys. Importantly,
|
||||
@ -280,9 +313,7 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
// We also have to add the normalized trait signature
|
||||
// as we don't normalize during implied bounds computation.
|
||||
wf_tys.extend(trait_sig.inputs_and_output.iter());
|
||||
let trait_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(trait_sig));
|
||||
|
||||
debug!("compare_impl_method: trait_fty={:?}", trait_fty);
|
||||
debug!(?trait_sig);
|
||||
|
||||
// FIXME: We'd want to keep more accurate spans than "the method signature" when
|
||||
// processing the comparison between the trait and impl fn, but we sadly lose them
|
||||
@ -298,6 +329,7 @@ fn compare_method_predicate_entailment<'tcx>(
|
||||
let emitted = report_trait_method_mismatch(
|
||||
infcx,
|
||||
cause,
|
||||
param_env,
|
||||
terr,
|
||||
(trait_m, trait_sig),
|
||||
(impl_m, impl_sig),
|
||||
@ -455,8 +487,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
// just so we don't ICE during instantiation later.
|
||||
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?;
|
||||
|
||||
let trait_to_impl_args = impl_trait_ref.args;
|
||||
|
||||
let impl_m_hir_id = tcx.local_def_id_to_hir_id(impl_m_def_id);
|
||||
let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span();
|
||||
let cause =
|
||||
@ -466,18 +496,18 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
kind: impl_m.kind,
|
||||
});
|
||||
|
||||
// Create mapping from impl to placeholder.
|
||||
let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id);
|
||||
|
||||
// Create mapping from trait to placeholder.
|
||||
let trait_to_placeholder_args =
|
||||
impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args);
|
||||
// Create mapping from trait to impl (i.e. impl trait header + impl method identity args).
|
||||
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto(
|
||||
tcx,
|
||||
impl_m.container_id(tcx),
|
||||
impl_trait_ref.args,
|
||||
);
|
||||
|
||||
let hybrid_preds = tcx
|
||||
.predicates_of(impl_m.container_id(tcx))
|
||||
.instantiate_identity(tcx)
|
||||
.into_iter()
|
||||
.chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_placeholder_args))
|
||||
.chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args))
|
||||
.map(|(clause, _)| clause);
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing);
|
||||
let param_env = traits::normalize_param_env_or_error(
|
||||
@ -511,7 +541,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
.instantiate_binder_with_fresh_vars(
|
||||
return_span,
|
||||
infer::HigherRankedType,
|
||||
tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_placeholder_args),
|
||||
tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args),
|
||||
)
|
||||
.fold_with(&mut collector);
|
||||
|
||||
@ -581,7 +611,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
Err(terr) => {
|
||||
let mut diag = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
cause.span(),
|
||||
cause.span,
|
||||
E0053,
|
||||
"method `{}` has an incompatible return type for trait",
|
||||
trait_m.name
|
||||
@ -593,10 +623,10 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
hir.get_if_local(impl_m.def_id)
|
||||
.and_then(|node| node.fn_decl())
|
||||
.map(|decl| (decl.output.span(), Cow::from("return type in trait"), false)),
|
||||
Some(infer::ValuePairs::Terms(ExpectedFound {
|
||||
Some(param_env.and(infer::ValuePairs::Terms(ExpectedFound {
|
||||
expected: trait_return_ty.into(),
|
||||
found: impl_return_ty.into(),
|
||||
})),
|
||||
}))),
|
||||
terr,
|
||||
false,
|
||||
);
|
||||
@ -620,6 +650,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
let emitted = report_trait_method_mismatch(
|
||||
infcx,
|
||||
cause,
|
||||
param_env,
|
||||
terr,
|
||||
(trait_m, trait_sig),
|
||||
(impl_m, impl_sig),
|
||||
@ -705,7 +736,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||
// Also, we only need to account for a difference in trait and impl args,
|
||||
// since we previously enforce that the trait method and impl method have the
|
||||
// same generics.
|
||||
let num_trait_args = trait_to_impl_args.len();
|
||||
let num_trait_args = impl_trait_ref.args.len();
|
||||
let num_impl_args = tcx.generics_of(impl_m.container_id(tcx)).own_params.len();
|
||||
let ty = match ty.try_fold_with(&mut RemapHiddenTyRegions {
|
||||
tcx,
|
||||
@ -933,6 +964,7 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
|
||||
fn report_trait_method_mismatch<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
mut cause: ObligationCause<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
terr: TypeError<'tcx>,
|
||||
(trait_m, trait_sig): (ty::AssocItem, ty::FnSig<'tcx>),
|
||||
(impl_m, impl_sig): (ty::AssocItem, ty::FnSig<'tcx>),
|
||||
@ -1018,10 +1050,10 @@ fn report_trait_method_mismatch<'tcx>(
|
||||
&mut diag,
|
||||
&cause,
|
||||
trait_err_span.map(|sp| (sp, Cow::from("type in trait"), false)),
|
||||
Some(infer::ValuePairs::PolySigs(ExpectedFound {
|
||||
Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound {
|
||||
expected: ty::Binder::dummy(trait_sig),
|
||||
found: ty::Binder::dummy(impl_sig),
|
||||
})),
|
||||
}))),
|
||||
terr,
|
||||
false,
|
||||
);
|
||||
@ -1041,12 +1073,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
||||
let trait_generics = tcx.generics_of(trait_m.def_id);
|
||||
let trait_params = trait_generics.own_counts().lifetimes;
|
||||
|
||||
debug!(
|
||||
"check_region_bounds_on_impl_item: \
|
||||
trait_generics={:?} \
|
||||
impl_generics={:?}",
|
||||
trait_generics, impl_generics
|
||||
);
|
||||
debug!(?trait_generics, ?impl_generics);
|
||||
|
||||
// Must have same number of early-bound lifetime parameters.
|
||||
// Unfortunately, if the user screws up the bounds, then this
|
||||
@ -1142,7 +1169,7 @@ fn extract_spans_for_error_reporting<'tcx>(
|
||||
TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
|
||||
(impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
|
||||
}
|
||||
_ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
|
||||
_ => (cause.span, tcx.hir().span_if_local(trait_m.def_id)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1710,8 +1737,7 @@ pub(super) fn compare_impl_const_raw(
|
||||
let trait_const_item = tcx.associated_item(trait_const_item_def);
|
||||
let impl_trait_ref =
|
||||
tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity();
|
||||
|
||||
debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref);
|
||||
debug!(?impl_trait_ref);
|
||||
|
||||
compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
|
||||
compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
|
||||
@ -1722,6 +1748,7 @@ pub(super) fn compare_impl_const_raw(
|
||||
/// The equivalent of [compare_method_predicate_entailment], but for associated constants
|
||||
/// instead of associated functions.
|
||||
// FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`.
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
fn compare_const_predicate_entailment<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_ct: ty::AssocItem,
|
||||
@ -1736,13 +1763,14 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||
// because we shouldn't really have to deal with lifetimes or
|
||||
// predicates. In fact some of this should probably be put into
|
||||
// shared functions because of DRY violations...
|
||||
let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id);
|
||||
let trait_to_impl_args =
|
||||
impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args);
|
||||
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id).rebase_onto(
|
||||
tcx,
|
||||
impl_ct.container_id(tcx),
|
||||
impl_trait_ref.args,
|
||||
);
|
||||
|
||||
// Create a parameter environment that represents the implementation's
|
||||
// method.
|
||||
// Compute placeholder form of impl and trait const tys.
|
||||
// associated const.
|
||||
let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity();
|
||||
|
||||
let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args);
|
||||
@ -1759,14 +1787,14 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||
// The predicates declared by the impl definition, the trait and the
|
||||
// associated const in the trait are assumed.
|
||||
let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap());
|
||||
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
|
||||
hybrid_preds.predicates.extend(
|
||||
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
|
||||
hybrid_preds.extend(
|
||||
trait_ct_predicates
|
||||
.instantiate_own(tcx, trait_to_impl_args)
|
||||
.map(|(predicate, _)| predicate),
|
||||
);
|
||||
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
|
||||
let param_env = traits::normalize_param_env_or_error(
|
||||
tcx,
|
||||
param_env,
|
||||
@ -1776,7 +1804,7 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
|
||||
|
||||
let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args);
|
||||
let impl_ct_own_bounds = impl_ct_predicates.instantiate_own_identity();
|
||||
for (predicate, span) in impl_ct_own_bounds {
|
||||
let cause = ObligationCause::misc(span, impl_ct_def_id);
|
||||
let predicate = ocx.normalize(&cause, param_env, predicate);
|
||||
@ -1787,20 +1815,15 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||
|
||||
// There is no "body" here, so just pass dummy id.
|
||||
let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
|
||||
|
||||
debug!("compare_const_impl: impl_ty={:?}", impl_ty);
|
||||
debug!(?impl_ty);
|
||||
|
||||
let trait_ty = ocx.normalize(&cause, param_env, trait_ty);
|
||||
|
||||
debug!("compare_const_impl: trait_ty={:?}", trait_ty);
|
||||
debug!(?trait_ty);
|
||||
|
||||
let err = ocx.sup(&cause, param_env, trait_ty, impl_ty);
|
||||
|
||||
if let Err(terr) = err {
|
||||
debug!(
|
||||
"checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
|
||||
impl_ty, trait_ty
|
||||
);
|
||||
debug!(?impl_ty, ?trait_ty);
|
||||
|
||||
// Locate the Span containing just the type of the offending impl
|
||||
let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const();
|
||||
@ -1824,10 +1847,10 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||
&mut diag,
|
||||
&cause,
|
||||
trait_c_span.map(|span| (span, Cow::from("type in trait"), false)),
|
||||
Some(infer::ValuePairs::Terms(ExpectedFound {
|
||||
Some(param_env.and(infer::ValuePairs::Terms(ExpectedFound {
|
||||
expected: trait_ty.into(),
|
||||
found: impl_ty.into(),
|
||||
})),
|
||||
}))),
|
||||
terr,
|
||||
false,
|
||||
);
|
||||
@ -1845,14 +1868,13 @@ fn compare_const_predicate_entailment<'tcx>(
|
||||
ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub(super) fn compare_impl_ty<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_ty: ty::AssocItem,
|
||||
trait_ty: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) {
|
||||
debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
|
||||
|
||||
let _: Result<(), ErrorGuaranteed> = try {
|
||||
compare_number_of_generics(tcx, impl_ty, trait_ty, false)?;
|
||||
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
|
||||
@ -1864,20 +1886,25 @@ pub(super) fn compare_impl_ty<'tcx>(
|
||||
|
||||
/// The equivalent of [compare_method_predicate_entailment], but for associated types
|
||||
/// instead of associated functions.
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
fn compare_type_predicate_entailment<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_ty: ty::AssocItem,
|
||||
trait_ty: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id);
|
||||
let trait_to_impl_args =
|
||||
impl_args.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.args);
|
||||
let impl_def_id = impl_ty.container_id(tcx);
|
||||
let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id).rebase_onto(
|
||||
tcx,
|
||||
impl_def_id,
|
||||
impl_trait_ref.args,
|
||||
);
|
||||
|
||||
let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
|
||||
let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
|
||||
|
||||
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_args);
|
||||
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own_identity();
|
||||
// If there are no bounds, then there are no const conditions, so no need to check that here.
|
||||
if impl_ty_own_bounds.len() == 0 {
|
||||
// Nothing to check.
|
||||
return Ok(());
|
||||
@ -1887,29 +1914,46 @@ fn compare_type_predicate_entailment<'tcx>(
|
||||
// `ObligationCause` (and the `FnCtxt`). This is what
|
||||
// `regionck_item` expects.
|
||||
let impl_ty_def_id = impl_ty.def_id.expect_local();
|
||||
debug!("compare_type_predicate_entailment: trait_to_impl_args={:?}", trait_to_impl_args);
|
||||
debug!(?trait_to_impl_args);
|
||||
|
||||
// The predicates declared by the impl definition, the trait and the
|
||||
// associated type in the trait are assumed.
|
||||
let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
|
||||
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
|
||||
hybrid_preds.predicates.extend(
|
||||
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
|
||||
hybrid_preds.extend(
|
||||
trait_ty_predicates
|
||||
.instantiate_own(tcx, trait_to_impl_args)
|
||||
.map(|(predicate, _)| predicate),
|
||||
);
|
||||
|
||||
debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
|
||||
debug!(?hybrid_preds);
|
||||
|
||||
let impl_ty_span = tcx.def_span(impl_ty_def_id);
|
||||
let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
|
||||
|
||||
let is_conditionally_const = tcx.is_conditionally_const(impl_ty.def_id);
|
||||
if is_conditionally_const {
|
||||
// Augment the hybrid param-env with the const conditions
|
||||
// of the impl header and the trait assoc type.
|
||||
hybrid_preds.extend(
|
||||
tcx.const_conditions(impl_ty_predicates.parent.unwrap())
|
||||
.instantiate_identity(tcx)
|
||||
.into_iter()
|
||||
.chain(
|
||||
tcx.const_conditions(trait_ty.def_id).instantiate_own(tcx, trait_to_impl_args),
|
||||
)
|
||||
.map(|(trait_ref, _)| {
|
||||
trait_ref.to_host_effect_clause(tcx, ty::HostPolarity::Maybe)
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
|
||||
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
|
||||
debug!(caller_bounds=?param_env.caller_bounds());
|
||||
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
|
||||
|
||||
debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
|
||||
|
||||
for (predicate, span) in impl_ty_own_bounds {
|
||||
let cause = ObligationCause::misc(span, impl_ty_def_id);
|
||||
let predicate = ocx.normalize(&cause, param_env, predicate);
|
||||
@ -1923,6 +1967,29 @@ fn compare_type_predicate_entailment<'tcx>(
|
||||
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
|
||||
}
|
||||
|
||||
if is_conditionally_const {
|
||||
// Validate the const conditions of the impl associated type.
|
||||
let impl_ty_own_const_conditions =
|
||||
tcx.const_conditions(impl_ty.def_id).instantiate_own_identity();
|
||||
for (const_condition, span) in impl_ty_own_const_conditions {
|
||||
let normalize_cause = traits::ObligationCause::misc(span, impl_ty_def_id);
|
||||
let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition);
|
||||
|
||||
let cause =
|
||||
ObligationCause::new(span, impl_ty_def_id, ObligationCauseCode::CompareImplItem {
|
||||
impl_item_def_id: impl_ty_def_id,
|
||||
trait_item_def_id: trait_ty.def_id,
|
||||
kind: impl_ty.kind,
|
||||
});
|
||||
ocx.register_obligation(traits::Obligation::new(
|
||||
tcx,
|
||||
cause,
|
||||
param_env,
|
||||
const_condition.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
let errors = ocx.select_all_or_error();
|
||||
@ -2005,15 +2072,31 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
|
||||
};
|
||||
|
||||
let obligations: Vec<_> = tcx
|
||||
let mut obligations: Vec<_> = tcx
|
||||
.explicit_item_bounds(trait_ty.def_id)
|
||||
.iter_instantiated_copied(tcx, rebased_args)
|
||||
.map(|(concrete_ty_bound, span)| {
|
||||
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
|
||||
debug!(?concrete_ty_bound);
|
||||
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
|
||||
})
|
||||
.collect();
|
||||
debug!("check_type_bounds: item_bounds={:?}", obligations);
|
||||
|
||||
// Only in a const implementation do we need to check that the `~const` item bounds hold.
|
||||
if tcx.is_conditionally_const(impl_ty_def_id) {
|
||||
obligations.extend(
|
||||
tcx.implied_const_bounds(trait_ty.def_id)
|
||||
.iter_instantiated_copied(tcx, rebased_args)
|
||||
.map(|(c, span)| {
|
||||
traits::Obligation::new(
|
||||
tcx,
|
||||
mk_cause(span),
|
||||
param_env,
|
||||
c.to_host_effect_clause(tcx, ty::HostPolarity::Maybe),
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
debug!(item_bounds=?obligations);
|
||||
|
||||
// Normalize predicates with the assumption that the GAT may always normalize
|
||||
// to its definition type. This should be the param-env we use to *prove* the
|
||||
@ -2032,7 +2115,7 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||
} else {
|
||||
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate)
|
||||
};
|
||||
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
|
||||
debug!(?normalized_predicate);
|
||||
obligation.predicate = normalized_predicate;
|
||||
|
||||
ocx.register_obligation(obligation);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user