Merge pull request #18431 from lnicola/sync-from-rust

minor: Sync from downstream
This commit is contained in:
Laurențiu Nicola 2024-10-29 06:54:19 +00:00 committed by GitHub
commit eae9d7ad8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1216 changed files with 14634 additions and 10363 deletions

View File

@ -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",
]

View File

@ -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)

View File

@ -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();

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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`

View File

@ -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())

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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(&param.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, &param.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 &param.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),
};

View File

@ -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(

View File

@ -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

View File

@ -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,
};

View File

@ -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 {

View File

@ -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],

View File

@ -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) {

View File

@ -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();
}

View File

@ -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,
);
}

View File

@ -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

View File

@ -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"]

View File

@ -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 {

View File

@ -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
}

View File

@ -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,
}
};

View File

@ -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,

View File

@ -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

View File

@ -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: ..."

View File

@ -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));
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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),
])

View File

@ -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 {}

View File

@ -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",

View File

@ -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();

View File

@ -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,
);

View File

@ -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 {}

View File

@ -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;
}

View File

@ -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

View File

@ -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>) -> ! {

View File

@ -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> {

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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,
}

View File

@ -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);
}

View File

@ -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")
}

View File

@ -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);

View File

@ -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,
);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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")),

View File

@ -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;

View File

@ -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 {

View File

@ -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,

View File

@ -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(())

View File

@ -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
{

View File

@ -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) {

View File

@ -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 {

View File

@ -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,

View File

@ -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

View File

@ -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,
})
}

View File

@ -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())
}

View File

@ -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
}

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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() {}
```

View File

@ -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() {}

View File

@ -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
```

View File

@ -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()
}

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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),
}
}
}

View File

@ -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(

View File

@ -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 });
}
}

View File

@ -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,

View File

@ -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),
}

View File

@ -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();
}

View File

@ -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 }`:

View File

@ -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.

View File

@ -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,
}
}

View File

@ -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,
};

View File

@ -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)]`")),

View File

@ -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).

View File

@ -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);

View File

@ -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));

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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,

View File

@ -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