From 30ff1ef3d000c7f76a1906522b0efff4c8b27201 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Fri, 11 Sep 2020 21:16:16 +0200 Subject: [PATCH] support const_evaluatable_checked across crate boundaries --- compiler/rustc_metadata/src/rmeta/decoder.rs | 19 ++++++++++ .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 11 ++++++ compiler/rustc_metadata/src/rmeta/mod.rs | 1 + .../rustc_middle/src/mir/abstract_const.rs | 2 +- compiler/rustc_middle/src/ty/codec.rs | 20 +++++++++++ .../src/ty/query/on_disk_cache.rs | 6 ++++ compiler/rustc_privacy/src/lib.rs | 9 +++++ .../src/traits/const_evaluatable.rs | 15 ++++++++ .../rustc_trait_selection/src/traits/mod.rs | 2 +- .../auxiliary/const_evaluatable_lib.rs | 9 +++++ .../const_evaluatable_checked/cross_crate.rs | 15 ++++++++ .../cross_crate_predicate.rs | 13 +++++++ .../cross_crate_predicate.stderr | 36 +++++++++++++++++++ 14 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/auxiliary/const_evaluatable_lib.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/cross_crate.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 43d76e9fdb4..a2e2cf1ca02 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -562,6 +562,12 @@ impl<'a, 'tcx> Decodable> for Span { } } +impl<'a, 'tcx> Decodable> for &'tcx [mir::abstract_const::Node<'tcx>] { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + ty::codec::RefDecodable::decode(d) + } +} + impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { ty::codec::RefDecodable::decode(d) @@ -1191,6 +1197,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } + fn get_mir_abstract_const( + &self, + tcx: TyCtxt<'tcx>, + id: DefIndex, + ) -> Option<&'tcx [mir::abstract_const::Node<'tcx>]> { + self.root + .tables + .mir_abstract_consts + .get(self, id) + .filter(|_| !self.is_proc_macro(id)) + .map_or(None, |v| Some(v.decode((self, tcx)))) + } + fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet { self.root .tables diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 94abfac19c6..d4f577a7d1b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -112,6 +112,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) } promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } + mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) } unused_generic_params => { cdata.get_unused_generic_params(def_id.index) } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 1e313b7edfc..eb091d86b82 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -321,6 +321,12 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> Encodable> for &'tcx [mir::abstract_const::Node<'tcx>] { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + (**self).encode(s) + } +} + impl<'a, 'tcx> Encodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { (**self).encode(s) @@ -1109,6 +1115,11 @@ impl EncodeContext<'a, 'tcx> { if !unused.is_empty() { record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused); } + + let abstract_const = self.tcx.mir_abstract_const(def_id); + if let Some(abstract_const) = abstract_const { + record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const); + } } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1ba5962d119..ba540c94411 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -284,6 +284,7 @@ define_tables! { super_predicates: Table)>, mir: Table)>, promoted_mir: Table>)>, + mir_abstract_consts: Table])>, unused_generic_params: Table>>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire diff --git a/compiler/rustc_middle/src/mir/abstract_const.rs b/compiler/rustc_middle/src/mir/abstract_const.rs index 8d215c5a521..b85f1e6e5de 100644 --- a/compiler/rustc_middle/src/mir/abstract_const.rs +++ b/compiler/rustc_middle/src/mir/abstract_const.rs @@ -11,7 +11,7 @@ rustc_index::newtype_index! { } /// A node of an `AbstractConst`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub enum Node<'tcx> { Leaf(&'tcx ty::Const<'tcx>), Binop(mir::BinOp, NodeId, NodeId), diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index e2e5f08462f..8ea34f9161a 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -357,6 +357,26 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>, } } +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::Node<'tcx>] { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::, _>>()?, + )) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::NodeId] { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::, _>>()?, + )) + } +} + impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index dcfb8d31430..b0c48a860eb 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -760,6 +760,12 @@ impl<'a, 'tcx> Decodable> } } +impl<'a, 'tcx> Decodable> for &'tcx [mir::abstract_const::Node<'tcx>] { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + RefDecodable::decode(d) + } +} + impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { RefDecodable::decode(d) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 03cc718b899..1a95992ed83 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -97,6 +97,15 @@ where ty.visit_with(self) } ty::PredicateAtom::RegionOutlives(..) => false, + ty::PredicateAtom::ConstEvaluatable(..) + if self.def_id_visitor.tcx().features().const_evaluatable_checked => + { + // FIXME(const_evaluatable_checked): If the constant used here depends on a + // private function we may have to do something here... + // + // For now, let's just pretend that everything is fine. + false + } _ => bug!("unexpected predicate: {:?}", predicate), } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 50cc2afb89c..1a5139d34d3 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -142,6 +142,12 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { return None; } + // We don't have to look at concrete constants, as we + // can just evaluate them. + if !body.is_polymorphic { + return None; + } + Some(AbstractConstBuilder { tcx, body, @@ -304,6 +310,15 @@ pub(super) fn mir_abstract_const<'tcx>( def: ty::WithOptConstParam, ) -> Option<&'tcx [Node<'tcx>]> { if tcx.features().const_evaluatable_checked { + match tcx.def_kind(def.did) { + // FIXME(const_evaluatable_checked): We currently only do this for anonymous constants, + // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether + // we want to look into them or treat them as opaque projections. + // + // Right now we do neither of that and simply always fail to unify them. + DefKind::AnonConst => (), + _ => return None, + } let body = tcx.mir_const(def).borrow(); AbstractConstBuilder::new(tcx, &body)?.build() } else { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 098336453bc..79495ba7f9b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -553,7 +553,7 @@ pub fn provide(providers: &mut ty::query::Providers) { type_implements_trait, subst_and_check_impossible_predicates, mir_abstract_const: |tcx, def_id| { - let def_id = def_id.as_local()?; // We do not store failed AbstractConst's. + let def_id = def_id.expect_local(); if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { tcx.mir_abstract_const_of_const_arg(def) } else { diff --git a/src/test/ui/const-generics/const_evaluatable_checked/auxiliary/const_evaluatable_lib.rs b/src/test/ui/const-generics/const_evaluatable_checked/auxiliary/const_evaluatable_lib.rs new file mode 100644 index 00000000000..9745dfed460 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/auxiliary/const_evaluatable_lib.rs @@ -0,0 +1,9 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +pub fn test1() -> [u8; std::mem::size_of::() - 1] +where + [u8; std::mem::size_of::() - 1]: Sized, +{ + [0; std::mem::size_of::() - 1] +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate.rs b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate.rs new file mode 100644 index 00000000000..53b23784387 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate.rs @@ -0,0 +1,15 @@ +// aux-build:const_evaluatable_lib.rs +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +extern crate const_evaluatable_lib; + +fn user() where [u8; std::mem::size_of::() - 1]: Sized { + assert_eq!(const_evaluatable_lib::test1::(), [0; std::mem::size_of::() - 1]); +} + +fn main() { + assert_eq!(const_evaluatable_lib::test1::(), [0; 3]); + user::(); + user::(); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs new file mode 100644 index 00000000000..22369923329 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs @@ -0,0 +1,13 @@ +// aux-build:const_evaluatable_lib.rs +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +extern crate const_evaluatable_lib; + +fn user() { + let _ = const_evaluatable_lib::test1::(); + //~^ ERROR constant expression depends + //~| ERROR constant expression depends + //~| ERROR constant expression depends +} + +fn main() {} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr new file mode 100644 index 00000000000..63abb782b93 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr @@ -0,0 +1,36 @@ +error: constant expression depends on a generic parameter + --> $DIR/cross_crate_predicate.rs:7:13 + | +LL | let _ = const_evaluatable_lib::test1::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41 + | +LL | [u8; std::mem::size_of::() - 1]: Sized, + | ----- required by this bound in `test1` + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/cross_crate_predicate.rs:7:13 + | +LL | let _ = const_evaluatable_lib::test1::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41 + | +LL | [u8; std::mem::size_of::() - 1]: Sized, + | ----- required by this bound in `test1::{{constant}}#1` + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/cross_crate_predicate.rs:7:13 + | +LL | let _ = const_evaluatable_lib::test1::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 3 previous errors +