Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make tuple constructors real const fns #61209

Merged
merged 3 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions src/librustc/ty/constness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,38 @@ 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;

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<Symbol> {
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
Expand Down Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 0 additions & 28 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: ?Sized>(Q);
// ```
// MIR of this struct constructor looks something like this:
// ```rust
// fn Qey(_1: Q) -> Qey<Q>{
// let mut _0: Qey<Q>; // 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)
Expand Down
35 changes: 0 additions & 35 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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<'_>, 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

Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
82 changes: 51 additions & 31 deletions src/librustc_mir/shim.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -873,34 +870,45 @@ 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,
}),
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
Expand All @@ -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)
}
68 changes: 10 additions & 58 deletions src/librustc_mir/transform/deaggregator.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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),
Expand All @@ -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,
))
});
}
}
Expand Down
Loading