diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs index 9e90a7519cf..99ffb0edce9 100644 --- a/compiler/rustc_mir/src/transform/check_consts/ops.rs +++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs @@ -72,7 +72,7 @@ impl NonConstOp for FnCallIndirect { /// A function call where the callee is not marked as `const`. #[derive(Debug)] -pub struct FnCallNonConst(pub DefId); +pub struct FnCallNonConst; impl NonConstOp for FnCallNonConst { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 3d8192649a0..08d969b27be 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -789,10 +789,10 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } } + #[instrument(skip(self))] fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { use rustc_target::spec::abi::Abi::RustIntrinsic; - trace!("visit_terminator: terminator={:?} location={:?}", terminator, location); self.super_terminator(terminator, location); match &terminator.kind { @@ -816,8 +816,9 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // Attempting to call a trait method? if let Some(trait_id) = tcx.trait_of_item(callee) { + trace!("attempting to call a trait method"); if !self.tcx.features().const_trait_impl { - self.check_op(ops::FnCallNonConst(callee)); + self.check_op(ops::FnCallNonConst); return; } @@ -871,25 +872,26 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { return; } + let is_intrinsic = tcx.fn_sig(callee).abi() == RustIntrinsic; + // HACK: This is to "unstabilize" the `transmute` intrinsic // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(callee).abi() == RustIntrinsic - && tcx.item_name(callee) == sym::transmute - { + if is_intrinsic && tcx.item_name(callee) == sym::transmute { self.check_op(ops::Transmute); return; } if !tcx.is_const_fn_raw(callee) { - self.check_op(ops::FnCallNonConst(callee)); + self.check_op(ops::FnCallNonConst); 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) = is_unstable_const_fn(tcx, callee) { + trace!(?gate, "calling unstable const fn"); if self.span.allows_unstable(gate) { return; } @@ -904,12 +906,14 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // 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; } @@ -923,10 +927,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); if callee_is_unstable_unmarked { - if self.ccx.is_const_stable_const_fn() { + trace!("callee_is_unstable_unmarked"); + // We do not use `const` modifiers for intrinsic "functions", as intrinsics are + // `extern` funtions, 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() || is_intrinsic { self.check_op(ops::FnCallUnstable(callee, None)); + return; } } + trace!("permitting call"); } // Forbid all `Drop` terminators unless the place being dropped is a local with no diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 4a3d6ecf8cc..b70cec25dfb 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -55,6 +55,21 @@ impl InheritDeprecation { } } +/// Whether to inherit const stability flags for nested items. In most cases, we do not want to +/// inherit const stability: just because an enclosing `fn` is const-stable does not mean +/// all `extern` imports declared in it should be const-stable! However, trait methods +/// inherit const stability attributes from their parent and do not have their own. +enum InheritConstStability { + Yes, + No, +} + +impl InheritConstStability { + fn yes(&self) -> bool { + matches!(self, InheritConstStability::Yes) + } +} + // A private tree-walker for producing an Index. struct Annotator<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -75,6 +90,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { item_sp: Span, kind: AnnotationKind, inherit_deprecation: InheritDeprecation, + inherit_const_stability: InheritConstStability, visit_children: F, ) where F: FnOnce(&mut Self), @@ -140,6 +156,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { const_stab }); + // `impl const Trait for Type` items forward their const stability to their + // immediate children. if const_stab.is_none() { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { @@ -228,7 +246,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.recurse_with_stability_attrs( depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)), stab, - const_stab, + if inherit_const_stability.yes() { const_stab } else { None }, visit_children, ); } @@ -325,6 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_item(&mut self, i: &'tcx Item<'tcx>) { let orig_in_trait_impl = self.in_trait_impl; let mut kind = AnnotationKind::Required; + let mut const_stab_inherit = InheritConstStability::No; match i.kind { // Inherent impls and foreign modules serve only as containers for other items, // they don't have their own stability. They still can be annotated as unstable @@ -338,6 +357,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => { self.in_trait_impl = true; kind = AnnotationKind::DeprecationProhibited; + const_stab_inherit = InheritConstStability::Yes; } hir::ItemKind::Struct(ref sd, _) => { if let Some(ctor_hir_id) = sd.ctor_hir_id() { @@ -347,6 +367,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { i.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |_| {}, ) } @@ -354,9 +375,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { _ => {} } - self.annotate(i.hir_id, &i.attrs, i.span, kind, InheritDeprecation::Yes, |v| { - intravisit::walk_item(v, i) - }); + self.annotate( + i.hir_id, + &i.attrs, + i.span, + kind, + InheritDeprecation::Yes, + const_stab_inherit, + |v| intravisit::walk_item(v, i), + ); self.in_trait_impl = orig_in_trait_impl; } @@ -367,6 +394,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ti.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { intravisit::walk_trait_item(v, ti); }, @@ -376,9 +404,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { let kind = if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required }; - self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, InheritDeprecation::Yes, |v| { - intravisit::walk_impl_item(v, ii); - }); + self.annotate( + ii.hir_id, + &ii.attrs, + ii.span, + kind, + InheritDeprecation::Yes, + InheritConstStability::No, + |v| { + intravisit::walk_impl_item(v, ii); + }, + ); } fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { @@ -388,6 +424,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { var.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { if let Some(ctor_hir_id) = var.data.ctor_hir_id() { v.annotate( @@ -396,6 +433,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { var.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |_| {}, ); } @@ -412,6 +450,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { s.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { intravisit::walk_struct_field(v, s); }, @@ -425,6 +464,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { i.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { intravisit::walk_foreign_item(v, i); }, @@ -438,6 +478,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { md.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |_| {}, ); } @@ -451,9 +492,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { _ => AnnotationKind::Prohibited, }; - self.annotate(p.hir_id, &p.attrs, p.span, kind, InheritDeprecation::No, |v| { - intravisit::walk_generic_param(v, p); - }); + self.annotate( + p.hir_id, + &p.attrs, + p.span, + kind, + InheritDeprecation::No, + InheritConstStability::No, + |v| { + intravisit::walk_generic_param(v, p); + }, + ); } } @@ -618,6 +667,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { krate.item.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| intravisit::walk_crate(v, krate), ); } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index fa5f0d50400..7c1a9b82f99 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1842,6 +1842,7 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - #[inline] pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } @@ -1926,6 +1927,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us #[inline] pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] fn copy(src: *const T, dst: *mut T, count: usize); } diff --git a/src/test/ui/consts/intrinsic_without_const_stab.rs b/src/test/ui/consts/intrinsic_without_const_stab.rs new file mode 100644 index 00000000000..810158a2957 --- /dev/null +++ b/src/test/ui/consts/intrinsic_without_const_stab.rs @@ -0,0 +1,17 @@ +#![feature(intrinsics, staged_api, const_intrinsic_copy)] +#![stable(feature = "core", since = "1.6.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] +#[inline] +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { + // Const stability attributes are not inherited from parent items. + extern "rust-intrinsic" { + fn copy(src: *const T, dst: *mut T, count: usize); + } + + unsafe { copy(src, dst, count) } + //~^ ERROR calls in constant functions are limited to constant functions +} + +fn main() {} diff --git a/src/test/ui/consts/intrinsic_without_const_stab.stderr b/src/test/ui/consts/intrinsic_without_const_stab.stderr new file mode 100644 index 00000000000..5a42823a605 --- /dev/null +++ b/src/test/ui/consts/intrinsic_without_const_stab.stderr @@ -0,0 +1,9 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/intrinsic_without_const_stab.rs:13:14 + | +LL | unsafe { copy(src, dst, count) } + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0015`. diff --git a/src/test/ui/consts/intrinsic_without_const_stab_fail.rs b/src/test/ui/consts/intrinsic_without_const_stab_fail.rs new file mode 100644 index 00000000000..bf2c44169d4 --- /dev/null +++ b/src/test/ui/consts/intrinsic_without_const_stab_fail.rs @@ -0,0 +1,15 @@ +#![feature(intrinsics, staged_api, const_intrinsic_copy)] +#![stable(feature = "core", since = "1.6.0")] + +extern "rust-intrinsic" { + fn copy(src: *const T, dst: *mut T, count: usize); +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] +#[inline] +pub const unsafe fn stuff(src: *const T, dst: *mut T, count: usize) { + unsafe { copy(src, dst, count) } //~ ERROR calls in constant functions are limited +} + +fn main() {} diff --git a/src/test/ui/consts/intrinsic_without_const_stab_fail.stderr b/src/test/ui/consts/intrinsic_without_const_stab_fail.stderr new file mode 100644 index 00000000000..d4a2989e785 --- /dev/null +++ b/src/test/ui/consts/intrinsic_without_const_stab_fail.stderr @@ -0,0 +1,9 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/intrinsic_without_const_stab_fail.rs:12:14 + | +LL | unsafe { copy(src, dst, count) } + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0015`.