From 6c9a018b60d1222217645ef4015764fd33b14a3f Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 26 May 2019 09:55:50 +0100 Subject: [PATCH 1/3] Don't run MIR passes on constructor shims --- src/librustc_mir/borrow_check/mod.rs | 28 -------- src/librustc_mir/build/mod.rs | 35 --------- src/librustc_mir/lib.rs | 1 + src/librustc_mir/shim.rs | 82 ++++++++++++++-------- src/librustc_mir/transform/deaggregator.rs | 68 +++--------------- src/librustc_mir/transform/mod.rs | 12 +++- src/librustc_mir/util/aggregate.rs | 76 ++++++++++++++++++++ src/librustc_mir/util/mod.rs | 2 + src/test/mir-opt/unusual-item-types.rs | 3 +- 9 files changed, 152 insertions(+), 155 deletions(-) create mode 100644 src/librustc_mir/util/aggregate.rs diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 4ae4d039d6034..74b2faa7a4ce8 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -91,34 +91,6 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> BorrowC let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.def_path_str(def_id)); - // We are not borrow checking the automatically generated struct/variant constructors - // because we want to accept structs such as this (taken from the `linked-hash-map` - // crate): - // ```rust - // struct Qey(Q); - // ``` - // MIR of this struct constructor looks something like this: - // ```rust - // fn Qey(_1: Q) -> Qey{ - // let mut _0: Qey; // return place - // - // bb0: { - // (_0.0: Q) = move _1; // bb0[0]: scope 0 at src/main.rs:1:1: 1:26 - // return; // bb0[1]: scope 0 at src/main.rs:1:1: 1:26 - // } - // } - // ``` - // The problem here is that `(_0.0: Q) = move _1;` is valid only if `Q` is - // of statically known size, which is not known to be true because of the - // `Q: ?Sized` constraint. However, it is true because the constructor can be - // called only when `Q` is of statically known size. - if tcx.is_constructor(def_id) { - return BorrowCheckResult { - closure_requirements: None, - used_mut_upvars: SmallVec::new(), - }; - } - let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { let input_mir: &Body<'_> = &input_mir.borrow(); do_mir_borrowck(&infcx, input_mir, def_id) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index c8a31ecffb84d..6bde349390ee8 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -2,7 +2,6 @@ use crate::build; use crate::build::scope::DropKind; use crate::hair::cx::Cx; use crate::hair::{LintLevel, BindingMode, PatternKind}; -use crate::shim; use crate::transform::MirSource; use crate::util as mir_util; use rustc::hir; @@ -31,8 +30,6 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Body<' // Figure out what primary body this item has. let (body_id, return_ty_span) = match tcx.hir().get_by_hir_id(id) { - Node::Ctor(ctor) => return create_constructor_shim(tcx, id, ctor), - Node::Expr(hir::Expr { node: hir::ExprKind::Closure(_, decl, body_id, _, _), .. }) | Node::Item(hir::Item { node: hir::ItemKind::Fn(decl, _, _, body_id), .. }) | Node::ImplItem( @@ -234,38 +231,6 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { } } -fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - ctor_id: hir::HirId, - v: &'tcx hir::VariantData) - -> Body<'tcx> -{ - let span = tcx.hir().span_by_hir_id(ctor_id); - if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { - tcx.infer_ctxt().enter(|infcx| { - let mut mir = shim::build_adt_ctor(&infcx, ctor_id, fields, span); - - // Convert the `mir::Body` to global types. - let tcx = infcx.tcx.global_tcx(); - let mut globalizer = GlobalizeMir { - tcx, - span: mir.span - }; - globalizer.visit_body(&mut mir); - let mir = unsafe { - mem::transmute::, Body<'tcx>>(mir) - }; - - mir_util::dump_mir(tcx, None, "mir_map", &0, - MirSource::item(tcx.hir().local_def_id_from_hir_id(ctor_id)), - &mir, |_, _| Ok(()) ); - - mir - }) - } else { - span_bug!(span, "attempting to create MIR for non-tuple variant {:?}", v); - } -} - /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 9213a009ea740..53302810b4052 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -22,6 +22,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(unicode_internals)] #![feature(step_trait)] #![feature(slice_concat_ext)] +#![feature(trusted_len)] #![feature(try_blocks)] #![recursion_limit="256"] diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 0cefc8c3a92ab..169e426c1d3c6 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -1,6 +1,5 @@ use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::infer; use rustc::mir::*; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::VariantIdx; @@ -21,6 +20,7 @@ use crate::transform::{ }; use crate::util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode}; use crate::util::patch::MirPatch; +use crate::util::expand_aggregate; pub fn provide(providers: &mut Providers<'_>) { providers.mir_shims = make_shim; @@ -842,29 +842,26 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir } -pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, - ctor_id: hir::HirId, - fields: &[hir::StructField], - span: Span) - -> Body<'tcx> -{ - let tcx = infcx.tcx; - let gcx = tcx.global_tcx(); - let def_id = tcx.hir().local_def_id_from_hir_id(ctor_id); - let param_env = gcx.param_env(def_id); +pub fn build_adt_ctor<'gcx>(tcx: TyCtxt<'_, 'gcx, 'gcx>, ctor_id: DefId) -> &'gcx Body<'gcx> { + debug_assert!(tcx.is_constructor(ctor_id)); + + let span = tcx.hir().span_if_local(ctor_id) + .unwrap_or_else(|| bug!("no span for ctor {:?}", ctor_id)); + + let param_env = tcx.param_env(ctor_id); // Normalize the sig. - let sig = gcx.fn_sig(def_id) + let sig = tcx.fn_sig(ctor_id) .no_bound_vars() .expect("LBR in ADT constructor signature"); - let sig = gcx.normalize_erasing_regions(param_env, sig); + let sig = tcx.normalize_erasing_regions(param_env, sig); let (adt_def, substs) = match sig.output().sty { ty::Adt(adt_def, substs) => (adt_def, substs), _ => bug!("unexpected type for ADT ctor {:?}", sig.output()) }; - debug!("build_ctor: def_id={:?} sig={:?} fields={:?}", def_id, sig, fields); + debug!("build_ctor: ctor_id={:?} sig={:?}", ctor_id, sig); let local_decls = local_decls_for_sig(&sig, span); @@ -873,26 +870,37 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, scope: OUTERMOST_SOURCE_SCOPE }; - let variant_no = if adt_def.is_enum() { - adt_def.variant_index_with_ctor_id(def_id) + let variant_index = if adt_def.is_enum() { + adt_def.variant_index_with_ctor_id(ctor_id) } else { VariantIdx::new(0) }; - // return = ADT(arg0, arg1, ...); return + // Generate the following MIR: + // + // (return as Variant).field0 = arg0; + // (return as Variant).field1 = arg1; + // + // return; + debug!("build_ctor: variant_index={:?}", variant_index); + + let statements = expand_aggregate( + Place::RETURN_PLACE, + adt_def + .variants[variant_index] + .fields + .iter() + .enumerate() + .map(|(idx, field_def)| ( + Operand::Move(Place::Base(PlaceBase::Local(Local::new(idx + 1)))), + field_def.ty(tcx, substs), + )), + AggregateKind::Adt(adt_def, variant_index, substs, None, None), + source_info, + ).collect(); + let start_block = BasicBlockData { - statements: vec![Statement { - source_info, - kind: StatementKind::Assign( - Place::RETURN_PLACE, - box Rvalue::Aggregate( - box AggregateKind::Adt(adt_def, variant_no, substs, None, None), - (1..sig.inputs().len()+1).map(|i| { - Operand::Move(Place::Base(PlaceBase::Local(Local::new(i)))) - }).collect() - ) - ) - }], + statements, terminator: Some(Terminator { source_info, kind: TerminatorKind::Return, @@ -900,7 +908,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, is_cleanup: false }; - Body::new( + let body = Body::new( IndexVec::from_elem_n(start_block, 1), IndexVec::from_elem_n( SourceScopeData { span: span, parent_scope: None }, 1 @@ -914,5 +922,17 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, vec![], span, vec![], - ) + ); + + crate::util::dump_mir( + tcx, + None, + "mir_map", + &0, + crate::transform::MirSource::item(ctor_id), + &body, + |_, _| Ok(()), + ); + + tcx.arena.alloc(body) } diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs index 7da37f956cedd..286c412622dd5 100644 --- a/src/librustc_mir/transform/deaggregator.rs +++ b/src/librustc_mir/transform/deaggregator.rs @@ -1,8 +1,7 @@ use rustc::mir::*; use rustc::ty::TyCtxt; -use rustc::ty::layout::VariantIdx; -use rustc_data_structures::indexed_vec::Idx; use crate::transform::{MirPass, MirSource}; +use crate::util::expand_aggregate; pub struct Deaggregator; @@ -31,7 +30,7 @@ impl MirPass for Deaggregator { let stmt = stmt.replace_nop(); let source_info = stmt.source_info; - let (mut lhs, kind, operands) = match stmt.kind { + let (lhs, kind, operands) = match stmt.kind { StatementKind::Assign(lhs, box rvalue) => { match rvalue { Rvalue::Aggregate(kind, operands) => (lhs, kind, operands), @@ -41,62 +40,15 @@ impl MirPass for Deaggregator { _ => bug!() }; - let mut set_discriminant = None; - let active_field_index = match *kind { - AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => { - if adt_def.is_enum() { - set_discriminant = Some(Statement { - kind: StatementKind::SetDiscriminant { - place: lhs.clone(), - variant_index, - }, - source_info, - }); - lhs = lhs.downcast(adt_def, variant_index); - } - active_field_index - } - AggregateKind::Generator(..) => { - // Right now we only support initializing generators to - // variant 0 (Unresumed). - let variant_index = VariantIdx::new(0); - set_discriminant = Some(Statement { - kind: StatementKind::SetDiscriminant { - place: lhs.clone(), - variant_index, - }, - source_info, - }); - - // Operands are upvars stored on the base place, so no - // downcast is necessary. - - None - } - _ => None - }; - - Some(operands.into_iter().enumerate().map(move |(i, op)| { - let lhs_field = if let AggregateKind::Array(_) = *kind { - // FIXME(eddyb) `offset` should be u64. - let offset = i as u32; - assert_eq!(offset as usize, i); - lhs.clone().elem(ProjectionElem::ConstantIndex { - offset, - // FIXME(eddyb) `min_length` doesn't appear to be used. - min_length: offset + 1, - from_end: false - }) - } else { + Some(expand_aggregate( + lhs, + operands.into_iter().map(|op| { let ty = op.ty(local_decls, tcx); - let field = Field::new(active_field_index.unwrap_or(i)); - lhs.clone().field(field, ty) - }; - Statement { - source_info, - kind: StatementKind::Assign(lhs_field, box Rvalue::Use(op)), - } - }).chain(set_discriminant)) + (op, ty) + }), + *kind, + source_info, + )) }); } } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 82193d98655d6..cc8aaa1c97fb3 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,4 +1,4 @@ -use crate::build; +use crate::{build, shim}; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Body, MirPhase, Promoted}; use rustc::ty::{TyCtxt, InstanceDef}; @@ -228,7 +228,15 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx } fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Body<'tcx> { - // `mir_borrowck` uses `mir_validated`, so we have to force it to + if tcx.is_constructor(def_id) { + // There's no reason to run all of the MIR passes on constructors when + // we can just output the MIR we want directly. This also saves const + // qualification and borrow checking the trouble of special casing + // constructors. + return shim::build_adt_ctor(tcx, def_id); + } + + // (Mir-)Borrowck uses `mir_validated`, so we have to force it to // execute before we can steal. tcx.ensure().mir_borrowck(def_id); diff --git a/src/librustc_mir/util/aggregate.rs b/src/librustc_mir/util/aggregate.rs new file mode 100644 index 0000000000000..98e70671ab715 --- /dev/null +++ b/src/librustc_mir/util/aggregate.rs @@ -0,0 +1,76 @@ +use rustc::mir::*; +use rustc::ty::Ty; +use rustc::ty::layout::VariantIdx; +use rustc_data_structures::indexed_vec::Idx; + +use std::iter::TrustedLen; + +/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields. +/// +/// Produces something like +/// +/// (lhs as Variant).field0 = arg0; // We only have a downcast if this is an enum +/// (lhs as Variant).field1 = arg1; +/// discriminant(lhs) = variant_index; // If lhs is an enum or generator. +pub fn expand_aggregate<'tcx>( + mut lhs: Place<'tcx>, + operands: impl Iterator, Ty<'tcx>)> + TrustedLen, + kind: AggregateKind<'tcx>, + source_info: SourceInfo, +) -> impl Iterator> + TrustedLen { + let mut set_discriminant = None; + let active_field_index = match kind { + AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => { + if adt_def.is_enum() { + set_discriminant = Some(Statement { + kind: StatementKind::SetDiscriminant { + place: lhs.clone(), + variant_index, + }, + source_info, + }); + lhs = lhs.downcast(adt_def, variant_index); + } + active_field_index + } + AggregateKind::Generator(..) => { + // Right now we only support initializing generators to + // variant 0 (Unresumed). + let variant_index = VariantIdx::new(0); + set_discriminant = Some(Statement { + kind: StatementKind::SetDiscriminant { + place: lhs.clone(), + variant_index, + }, + source_info, + }); + + // Operands are upvars stored on the base place, so no + // downcast is necessary. + + None + } + _ => None + }; + + operands.into_iter().enumerate().map(move |(i, (op, ty))| { + let lhs_field = if let AggregateKind::Array(_) = kind { + // FIXME(eddyb) `offset` should be u64. + let offset = i as u32; + assert_eq!(offset as usize, i); + lhs.clone().elem(ProjectionElem::ConstantIndex { + offset, + // FIXME(eddyb) `min_length` doesn't appear to be used. + min_length: offset + 1, + from_end: false + }) + } else { + let field = Field::new(active_field_index.unwrap_or(i)); + lhs.clone().field(field, ty) + }; + Statement { + source_info, + kind: StatementKind::Assign(lhs_field, box Rvalue::Use(op)), + } + }).chain(set_discriminant) +} diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index 0e7f473a3e70d..e340029434d81 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -2,6 +2,7 @@ use core::unicode::property::Pattern_White_Space; use rustc::ty::TyCtxt; use syntax_pos::Span; +pub mod aggregate; pub mod borrowck_errors; pub mod elaborate_drops; pub mod def_use; @@ -13,6 +14,7 @@ pub(crate) mod pretty; pub mod liveness; pub mod collect_writes; +pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; diff --git a/src/test/mir-opt/unusual-item-types.rs b/src/test/mir-opt/unusual-item-types.rs index 67a55101d829f..f4d848dfc7ad1 100644 --- a/src/test/mir-opt/unusual-item-types.rs +++ b/src/test/mir-opt/unusual-item-types.rs @@ -78,7 +78,8 @@ fn main() { // let mut _0: Test; // // bb0: { -// _0 = Test::X(move _1,); +// ((_0 as X).0: usize) = move _1; +// discriminant(_0) = 0; // return; // } // } From 0d75ab2293a106eb674ac01860910cfc1580837e Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 26 May 2019 09:58:06 +0100 Subject: [PATCH 2/3] Make constructors actually be const functions --- src/librustc/ty/constness.rs | 32 +++-- src/librustc_metadata/decoder.rs | 1 + src/libsyntax/feature_gate.rs | 4 + src/libsyntax_pos/symbol.rs | 1 + .../const_constructor/const-construct-call.rs | 116 ++++++++++++++++++ ...ure-gate-const_constructor.const_fn.stderr | 34 +++++ ...gate-const_constructor.min_const_fn.stderr | 34 +++++ .../feature-gate-const_constructor.rs | 28 +++++ 8 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/consts/const_constructor/const-construct-call.rs create mode 100644 src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr create mode 100644 src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr create mode 100644 src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index 7298b548f3197..56f75e800f255 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -2,7 +2,7 @@ use crate::ty::query::Providers; use crate::hir::def_id::DefId; use crate::hir; use crate::ty::TyCtxt; -use syntax_pos::symbol::Symbol; +use syntax_pos::symbol::{sym, Symbol}; use crate::hir::map::blocks::FnLikeNode; use syntax::attr; @@ -10,27 +10,30 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { /// Whether the `def_id` counts as const fn in your current crate, considering all active /// feature gates pub fn is_const_fn(self, def_id: DefId) -> bool { - self.is_const_fn_raw(def_id) && match self.lookup_stability(def_id) { - Some(stab) => match stab.const_stability { + self.is_const_fn_raw(def_id) && match self.is_unstable_const_fn(def_id) { + Some(feature_name) => { // has a `rustc_const_unstable` attribute, check whether the user enabled the - // corresponding feature gate - Some(feature_name) => self.features() + // corresponding feature gate, const_constructor is not a lib feature, so has + // to be checked separately. + self.features() .declared_lib_features .iter() - .any(|&(sym, _)| sym == feature_name), - // the function has no stability attribute, it is stable as const fn or the user - // needs to use feature gates to use the function at all - None => true, + .any(|&(sym, _)| sym == feature_name) + || (feature_name == sym::const_constructor + && self.features().const_constructor) }, - // functions without stability are either stable user written const fn or the user is - // using feature gates and we thus don't care what they do + // functions without const stability are either stable user written + // const fn or the user is using feature gates and we thus don't + // care what they do None => true, } } /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(self, def_id: DefId) -> Option { - if self.is_const_fn_raw(def_id) { + if self.is_constructor(def_id) { + Some(sym::const_constructor) + } else if self.is_const_fn_raw(def_id) { self.lookup_stability(def_id)?.const_stability } else { None @@ -70,8 +73,11 @@ pub fn provide<'tcx>(providers: &mut Providers<'tcx>) { let hir_id = tcx.hir().as_local_hir_id(def_id) .expect("Non-local call to local provider is_const_fn"); - if let Some(fn_like) = FnLikeNode::from_node(tcx.hir().get_by_hir_id(hir_id)) { + let node = tcx.hir().get_by_hir_id(hir_id); + if let Some(fn_like) = FnLikeNode::from_node(node) { fn_like.constness() == hir::Constness::Const + } else if let hir::Node::Ctor(_) = node { + true } else { false } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 10ff606b013a7..e3e327d0a5bd0 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -1167,6 +1167,7 @@ impl<'a, 'tcx> CrateMetadata { let constness = match self.entry(id).kind { EntryKind::Method(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, + EntryKind::Variant(..) | EntryKind::Struct(..) => hir::Constness::Const, _ => hir::Constness::NotConst, }; constness == hir::Constness::Const diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 4a95b6f69a161..0043b8a1c47c4 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -560,6 +560,10 @@ declare_features! ( // Allows the user of associated type bounds. (active, associated_type_bounds, "1.34.0", Some(52662), None), + // Allows calling constructor functions in `const fn` + // FIXME Create issue + (active, const_constructor, "1.37.0", Some(61456), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 4e080d115d2a8..6fac343581427 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -185,6 +185,7 @@ symbols! { conservative_impl_trait, console, const_compare_raw_pointers, + const_constructor, const_fn, const_fn_union, const_generics, diff --git a/src/test/ui/consts/const_constructor/const-construct-call.rs b/src/test/ui/consts/const_constructor/const-construct-call.rs new file mode 100644 index 0000000000000..f2d2bda53c053 --- /dev/null +++ b/src/test/ui/consts/const_constructor/const-construct-call.rs @@ -0,0 +1,116 @@ +// Test that constructors are considered to be const fns with the required feature. + +// run-pass + +// revisions: min_const_fn const_fn + +#![cfg_attr(const_fn, feature(const_fn))] + +#![feature(const_constructor)] + +// Ctor(..) is transformed to Ctor { 0: ... } in HAIR lowering, so directly +// calling constructors doesn't require them to be const. + +type ExternalType = std::panic::AssertUnwindSafe<(Option, Result)>; + +const fn call_external_constructors_in_local_vars() -> ExternalType { + let f = Some; + let g = Err; + let h = std::panic::AssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +} + +const CALL_EXTERNAL_CONSTRUCTORS_IN_LOCAL_VARS: ExternalType = { + let f = Some; + let g = Err; + let h = std::panic::AssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +}; + +const fn call_external_constructors_in_temps() -> ExternalType { + let x = { Some }(5); + let y = (*&Err)(false); + let z = [std::panic::AssertUnwindSafe][0]((x, y)); + z +} + +const CALL_EXTERNAL_CONSTRUCTORS_IN_TEMPS: ExternalType = { + let x = { Some }(5); + let y = (*&Err)(false); + let z = [std::panic::AssertUnwindSafe][0]((x, y)); + z +}; + +#[derive(Debug, PartialEq)] +enum LocalOption { + Some(T), + _None, +} + +#[derive(Debug, PartialEq)] +enum LocalResult { + _Ok(T), + Err(E), +} + +#[derive(Debug, PartialEq)] +struct LocalAssertUnwindSafe(T); + +type LocalType = LocalAssertUnwindSafe<(LocalOption, LocalResult)>; + +const fn call_local_constructors_in_local_vars() -> LocalType { + let f = LocalOption::Some; + let g = LocalResult::Err; + let h = LocalAssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +} + +const CALL_LOCAL_CONSTRUCTORS_IN_LOCAL_VARS: LocalType = { + let f = LocalOption::Some; + let g = LocalResult::Err; + let h = LocalAssertUnwindSafe; + let x = f(5); + let y = g(false); + let z = h((x, y)); + z +}; + +const fn call_local_constructors_in_temps() -> LocalType { + let x = { LocalOption::Some }(5); + let y = (*&LocalResult::Err)(false); + let z = [LocalAssertUnwindSafe][0]((x, y)); + z +} + +const CALL_LOCAL_CONSTRUCTORS_IN_TEMPS: LocalType = { + let x = { LocalOption::Some }(5); + let y = (*&LocalResult::Err)(false); + let z = [LocalAssertUnwindSafe][0]((x, y)); + z +}; + +fn main() { + assert_eq!( + ( + call_external_constructors_in_local_vars().0, + call_external_constructors_in_temps().0, + call_local_constructors_in_local_vars(), + call_local_constructors_in_temps(), + ), + ( + CALL_EXTERNAL_CONSTRUCTORS_IN_LOCAL_VARS.0, + CALL_EXTERNAL_CONSTRUCTORS_IN_TEMPS.0, + CALL_LOCAL_CONSTRUCTORS_IN_LOCAL_VARS, + CALL_LOCAL_CONSTRUCTORS_IN_TEMPS, + ) + ); +} diff --git a/src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr new file mode 100644 index 0000000000000..fa4f83ed01e15 --- /dev/null +++ b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.const_fn.stderr @@ -0,0 +1,34 @@ +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:9:37 + | +LL | const EXTERNAL_CONST: Option = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:12:24 + | +LL | const LOCAL_CONST: E = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:17:13 + | +LL | let _ = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:23:13 + | +LL | let _ = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr new file mode 100644 index 0000000000000..fa4f83ed01e15 --- /dev/null +++ b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.min_const_fn.stderr @@ -0,0 +1,34 @@ +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:9:37 + | +LL | const EXTERNAL_CONST: Option = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:12:24 + | +LL | const LOCAL_CONST: E = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `std::prelude::v1::Some` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:17:13 + | +LL | let _ = {Some}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: `E::V` is not yet stable as a const fn + --> $DIR/feature-gate-const_constructor.rs:23:13 + | +LL | let _ = {E::V}(1); + | ^^^^^^^^^ + | + = help: add `#![feature(const_constructor)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs new file mode 100644 index 0000000000000..b37fd2fd243d1 --- /dev/null +++ b/src/test/ui/consts/const_constructor/feature-gate-const_constructor.rs @@ -0,0 +1,28 @@ +// revisions: min_const_fn const_fn + +#![cfg_attr(const_fn, feature(const_fn))] + +enum E { + V(i32), +} + +const EXTERNAL_CONST: Option = {Some}(1); +//[min_const_fn]~^ ERROR is not yet stable as a const fn +//[const_fn]~^^ ERROR is not yet stable as a const fn +const LOCAL_CONST: E = {E::V}(1); +//[min_const_fn]~^ ERROR is not yet stable as a const fn +//[const_fn]~^^ ERROR is not yet stable as a const fn + +const fn external_fn() { + let _ = {Some}(1); + //[min_const_fn]~^ ERROR is not yet stable as a const fn + //[const_fn]~^^ ERROR is not yet stable as a const fn +} + +const fn local_fn() { + let _ = {E::V}(1); + //[min_const_fn]~^ ERROR is not yet stable as a const fn + //[const_fn]~^^ ERROR is not yet stable as a const fn +} + +fn main() {} From bcf836567560c2f31dac79aa0379c0f0e2740081 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 26 May 2019 10:43:30 +0100 Subject: [PATCH 3/3] Make sure constructors functions are type checked correctly --- .../adt-tuple-struct-calls.rs | 71 +++++++++++++++++++ .../adt-tuple-struct-calls.stderr | 56 +++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs create mode 100644 src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs new file mode 100644 index 0000000000000..116583223925a --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs @@ -0,0 +1,71 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +struct SomeStruct(T); + +fn no_annot() { + let c = 66; + let f = SomeStruct; + f(&c); +} + +fn annot_underscore() { + let c = 66; + let f = SomeStruct::<_>; + f(&c); +} + +fn annot_reference_any_lifetime() { + let c = 66; + let f = SomeStruct::<&u32>; + f(&c); +} + +fn annot_reference_static_lifetime() { + let c = 66; + let f = SomeStruct::<&'static u32>; + f(&c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + let f = SomeStruct::<&'a u32>; + f(&c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + let f = SomeStruct::<&'a u32>; + f(c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + let f = SomeStruct::<&'a u32>; + f(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_across_closure<'a>(_: &'a u32) { + let f = SomeStruct::<&'a u32>; + let _closure = || { + let c = 66; + f(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + let f = SomeStruct::<&'a u32>; + f(c); + }; +} + +fn annot_reference_named_lifetime_across_closure_ok<'a>(c: &'a u32) { + let f = SomeStruct::<&'a u32>; + let _closure = || { + f(c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr new file mode 100644 index 0000000000000..9664fb9f54831 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr @@ -0,0 +1,56 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:27:7 + | +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:33:7 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:45:11 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:53:11 + | +LL | let f = SomeStruct::<&'a u32>; + | - lifetime `'1` appears in the type of `f` +... +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'1` +LL | }; + | - `c` dropped here while still borrowed + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0597`.