about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_attr/src/builtin.rs10
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs17
-rw-r--r--compiler/rustc_const_eval/messages.ftl2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs21
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs4
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs12
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs19
-rw-r--r--compiler/rustc_middle/src/ty/intrinsic.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs1
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs28
-rw-r--r--compiler/rustc_passes/src/stability.rs15
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs18
14 files changed, 108 insertions, 46 deletions
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 6af75bc94bb..2753ac529d1 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -273,8 +273,7 @@ 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!
+/// `is_const_fn` indicates whether this is a function marked as `const`.
 pub fn find_const_stability(
     sess: &Session,
     attrs: &[Attribute],
@@ -330,7 +329,7 @@ pub fn find_const_stability(
         }
     }
 
-    // Merge promotable and not_exposed_on_stable into stability info
+    // Merge promotable and const_stable_indirect into stability info
     if promotable {
         match &mut const_stab {
             Some((stab, _)) => stab.promotable = promotable,
@@ -352,10 +351,7 @@ pub fn find_const_stability(
                     })
                 }
             }
-            _ => {
-                // 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
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 3871816777c..15cc9c20ab7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -10,6 +10,7 @@ use rustc_middle::ty::{self, Ty};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
 use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
+use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::debug;
 
 use crate::MirBorrowckCtxt;
@@ -267,6 +268,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 kind,
                 self.is_upvar_field_projection(original_path.as_ref())
             );
+            if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
+                // If the type may implement Copy, skip the error.
+                // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
+                self.dcx().span_delayed_bug(
+                    span,
+                    "Type may implement copy, but there is no other error.",
+                );
+                return;
+            }
             (
                 match kind {
                     &IllegalMoveOriginKind::BorrowedContent { target_place } => self
@@ -291,6 +301,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         self.buffer_error(err);
     }
 
+    fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool {
+        let Some(copy_trait_def) = self.infcx.tcx.lang_items().copy_trait() else { return false };
+        // This is only going to be ambiguous if there are incoherent impls, because otherwise
+        // ambiguity should never happen in MIR.
+        self.infcx.type_implements_trait(copy_trait_def, [ty], self.param_env).may_apply()
+    }
+
     fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> {
         let description = if place.projection.len() == 1 {
             format!("static item {}", self.describe_any_place(place.as_ref()))
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 2bc5adb2dce..826e34930ea 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -399,7 +399,7 @@ const_eval_uninhabited_enum_variant_written =
 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)
+    .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_const_stable_intrinsic]` (but this requires team approval)
 
 const_eval_unreachable = entering unreachable code
 const_eval_unreachable_unwind =
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 8cb7e02036f..aea3d5bd3e7 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -736,16 +736,25 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
                 // Intrinsics are language primitives, not regular calls, so treat them separately.
                 if let Some(intrinsic) = tcx.intrinsic(callee) {
+                    // We use `intrinsic.const_stable` to determine if this can be safely exposed to
+                    // stable code, rather than `const_stable_indirect`. This is to make
+                    // `#[rustc_const_stable_indirect]` an attribute that is always safe to add.
+                    // We also ask is_safe_to_expose_on_stable_const_fn; this determines whether the intrinsic
+                    // fallback body is safe to expose on stable.
+                    let is_const_stable = intrinsic.const_stable
+                        || (!intrinsic.must_be_overridden
+                            && tcx.is_const_fn(callee)
+                            && is_safe_to_expose_on_stable_const_fn(tcx, 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, .. }) => {
+                        Some(ConstStability { feature: None, .. }) => {
                             // 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() {
+                            if !is_const_stable && self.enforce_recursive_const_stability() {
                                 self.dcx().emit_err(errors::UnmarkedIntrinsicExposed {
                                     span: self.span,
                                     def_path: self.tcx.def_path_str(callee),
@@ -755,17 +764,19 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                         Some(ConstStability {
                             feature: Some(feature),
                             level: StabilityLevel::Unstable { .. },
-                            const_stable_indirect,
                             ..
                         }) => {
                             self.check_op(ops::IntrinsicUnstable {
                                 name: intrinsic.name,
                                 feature,
-                                const_stable_indirect,
+                                const_stable: is_const_stable,
                             });
                         }
                         Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => {
-                            // All good.
+                            // All good. Note that a `#[rustc_const_stable]` intrinsic (meaning it
+                            // can be *directly* invoked from stable const code) does not always
+                            // have the `#[rustc_const_stable_intrinsic]` attribute (which controls
+                            // exposing an intrinsic indirectly); we accept this call anyway.
                         }
                     }
                     // This completes the checks for intrinsics.
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index d264cab1511..2931159842f 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -354,14 +354,14 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
 pub(crate) struct IntrinsicUnstable {
     pub name: Symbol,
     pub feature: Symbol,
-    pub const_stable_indirect: bool,
+    pub const_stable: 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,
+            safe_to_expose_on_stable: self.const_stable,
             // We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`,
             // that's not a trivial change!
             is_function_call: false,
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index 037fdcbcf9b..beff0cd99fc 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -25,15 +25,9 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
             hir::Constness::Const
         }
         hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness,
-        hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
-            // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
-            // foreign items cannot be evaluated at compile-time.
-            let is_const = if tcx.intrinsic(def_id).is_some() {
-                tcx.lookup_const_stability(def_id).is_some()
-            } else {
-                false
-            };
-            if is_const { hir::Constness::Const } else { hir::Constness::NotConst }
+        hir::Node::ForeignItem(_) => {
+            // Foreign items cannot be evaluated at compile-time.
+            hir::Constness::NotConst
         }
         hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness,
         _ => {
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 0069b07ad62..cc0bdec7019 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -837,6 +837,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_const_stable_indirect, Normal,
         template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL,
     ),
+    rustc_attr!(
+        rustc_const_stable_intrinsic, Normal,
+        template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL,
+    ),
     gated!(
         rustc_allow_const_fn_unstable, Normal,
         template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No,
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 03590b6a455..2a8ed26aa2b 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -2071,6 +2071,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 s = pluralize!(len),
                 them = if len == 1 { "it" } else { "them" },
             ),
+            format!(
+                "{}{}{}{}",
+                prefix,
+                unmentioned_fields
+                    .iter()
+                    .map(|(_, name)| {
+                        let field_name = name.to_string();
+                        format!("{field_name}: _")
+                    })
+                    .collect::<Vec<_>>()
+                    .join(", "),
+                if have_inaccessible_fields { ", .." } else { "" },
+                postfix,
+            ),
+            Applicability::MachineApplicable,
+        );
+        err.span_suggestion(
+            sp,
+            "or always ignore missing fields here",
             format!("{prefix}..{postfix}"),
             Applicability::MachineApplicable,
         );
diff --git a/compiler/rustc_middle/src/ty/intrinsic.rs b/compiler/rustc_middle/src/ty/intrinsic.rs
index ed0fb37d3b8..6a3ddacb424 100644
--- a/compiler/rustc_middle/src/ty/intrinsic.rs
+++ b/compiler/rustc_middle/src/ty/intrinsic.rs
@@ -9,6 +9,8 @@ pub struct IntrinsicDef {
     pub name: Symbol,
     /// Whether the intrinsic has no meaningful body and all backends need to shim all calls to it.
     pub must_be_overridden: bool,
+    /// Whether the intrinsic can be invoked from stable const fn
+    pub const_stable: bool,
 }
 
 impl TyCtxt<'_> {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 83276808a28..6ffd149ef14 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1789,6 +1789,7 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Intrinsi
         Some(ty::IntrinsicDef {
             name: tcx.item_name(def_id.into()),
             must_be_overridden: tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden),
+            const_stable: tcx.has_attr(def_id, sym::rustc_const_stable_intrinsic),
         })
     } else {
         None
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 8bd615e6d79..c85d0bd05cb 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -136,8 +136,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
                 node_replacements.array_windows()
             {
                 assert!(
-                    node_range.0.end <= next_node_range.0.start,
-                    "Node ranges should be disjoint: ({:?}, {:?}) ({:?}, {:?})",
+                    node_range.0.end <= next_node_range.0.start
+                        || node_range.0.end >= next_node_range.0.end,
+                    "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
                     node_range,
                     tokens,
                     next_node_range,
@@ -145,8 +146,20 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
                 );
             }
 
-            // Process the replace ranges.
-            for (node_range, target) in node_replacements.into_iter() {
+            // Process the replace ranges, starting from the highest start
+            // position and working our way back. If have tokens like:
+            //
+            // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
+            //
+            // Then we will generate replace ranges for both
+            // the `#[cfg(FALSE)] field: bool` and the entire
+            // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
+            //
+            // By starting processing from the replace range with the greatest
+            // start position, we ensure that any (outer) replace range which
+            // encloses another (inner) replace range will fully overwrite the
+            // inner range's replacement.
+            for (node_range, target) in node_replacements.into_iter().rev() {
                 assert!(
                     !node_range.0.is_empty(),
                     "Cannot replace an empty node range: {:?}",
@@ -383,9 +396,10 @@ impl<'a> Parser<'a> {
             // from `ParserRange` form to `NodeRange` form. We will perform the actual
             // replacement only when we convert the `LazyAttrTokenStream` to an
             // `AttrTokenStream`.
-            self.capture_state
-                .parser_replacements
-                .drain(parser_replacements_start..parser_replacements_end)
+            self.capture_state.parser_replacements
+                [parser_replacements_start..parser_replacements_end]
+                .iter()
+                .cloned()
                 .chain(inner_attr_parser_replacements)
                 .map(|(parser_range, data)| {
                     (NodeRange::new(parser_range, collect_pos.start_pos), data)
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 737e163efce..cd47c8ece60 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -106,7 +106,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         def_id: LocalDefId,
         item_sp: Span,
         fn_sig: Option<&'tcx hir::FnSig<'tcx>>,
-        is_foreign_item: bool,
         kind: AnnotationKind,
         inherit_deprecation: InheritDeprecation,
         inherit_const_stability: InheritConstStability,
@@ -175,11 +174,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         // implied), check if the function/method is const or the parent impl block is const.
         if let Some(fn_sig) = fn_sig
             && !fn_sig.header.is_const()
-            // We have to exclude foreign items as they might be intrinsics. Sadly we can't check
-            // their ABI; `fn_sig.abi` is *not* correct for foreign functions.
-            && !is_foreign_item
             && const_stab.is_some()
-            && (!self.in_trait_impl || !self.tcx.is_const_fn(def_id.to_def_id()))
         {
             self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
         }
@@ -398,7 +393,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
                         ctor_def_id,
                         i.span,
                         None,
-                        /* is_foreign_item */ false,
                         AnnotationKind::Required,
                         InheritDeprecation::Yes,
                         InheritConstStability::No,
@@ -417,7 +411,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             i.owner_id.def_id,
             i.span,
             fn_sig,
-            /* is_foreign_item */ false,
             kind,
             InheritDeprecation::Yes,
             const_stab_inherit,
@@ -437,7 +430,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             ti.owner_id.def_id,
             ti.span,
             fn_sig,
-            /* is_foreign_item */ false,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
             InheritConstStability::No,
@@ -461,7 +453,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             ii.owner_id.def_id,
             ii.span,
             fn_sig,
-            /* is_foreign_item */ false,
             kind,
             InheritDeprecation::Yes,
             InheritConstStability::No,
@@ -477,7 +468,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             var.def_id,
             var.span,
             None,
-            /* is_foreign_item */ false,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
             InheritConstStability::No,
@@ -488,7 +478,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
                         ctor_def_id,
                         var.span,
                         None,
-                        /* is_foreign_item */ false,
                         AnnotationKind::Required,
                         InheritDeprecation::Yes,
                         InheritConstStability::No,
@@ -507,7 +496,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             s.def_id,
             s.span,
             None,
-            /* is_foreign_item */ false,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
             InheritConstStability::No,
@@ -527,7 +515,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             i.owner_id.def_id,
             i.span,
             fn_sig,
-            /* is_foreign_item */ true,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
             InheritConstStability::No,
@@ -550,7 +537,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             p.def_id,
             p.span,
             None,
-            /* is_foreign_item */ false,
             kind,
             InheritDeprecation::No,
             InheritConstStability::No,
@@ -712,7 +698,6 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
             CRATE_DEF_ID,
             tcx.hir().span(CRATE_HIR_ID),
             None,
-            /* is_foreign_item */ false,
             AnnotationKind::Required,
             InheritDeprecation::Yes,
             InheritConstStability::No,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index fac2180d63b..21a74bd4020 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1663,6 +1663,7 @@ symbols! {
         rustc_const_panic_str,
         rustc_const_stable,
         rustc_const_stable_indirect,
+        rustc_const_stable_intrinsic,
         rustc_const_unstable,
         rustc_conversion_suggestion,
         rustc_deallocator,
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index bacb3b1b1b8..8f1c8a29663 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -60,6 +60,24 @@ impl<'tcx> InferCtxt<'tcx> {
     ///
     /// Invokes `evaluate_obligation`, so in the event that evaluating
     /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned.
+    ///
+    /// `type_implements_trait` is a convenience function for simple cases like
+    ///
+    /// ```ignore (illustrative)
+    /// let copy_trait = infcx.tcx.require_lang_item(LangItem::Copy, span);
+    /// let implements_copy = infcx.type_implements_trait(copy_trait, [ty], param_env)
+    /// .must_apply_modulo_regions();
+    /// ```
+    ///
+    /// In most cases you should instead create an [Obligation] and check whether
+    ///  it holds via [`evaluate_obligation`] or one of its helper functions like
+    /// [`predicate_must_hold_modulo_regions`], because it properly handles higher ranked traits
+    /// and it is more convenient and safer when your `params` are inside a [`Binder`].
+    ///
+    /// [Obligation]: traits::Obligation
+    /// [`evaluate_obligation`]: crate::traits::query::evaluate_obligation::InferCtxtExt::evaluate_obligation
+    /// [`predicate_must_hold_modulo_regions`]: crate::traits::query::evaluate_obligation::InferCtxtExt::predicate_must_hold_modulo_regions
+    /// [`Binder`]: ty::Binder
     #[instrument(level = "debug", skip(self, params), ret)]
     fn type_implements_trait(
         &self,