diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 996f5b1241263..fd848e60e3532 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -51,6 +51,7 @@ #![feature(thread_local)] #![feature(trace_macros)] #![feature(trusted_len)] +#![feature(try_trait)] #![feature(vec_remove_item)] #![feature(stmt_expr_attributes)] #![feature(integer_atomics)] diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 6a41b843e5794..806e2f7ee28c2 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -65,183 +65,237 @@ use syntax_pos::Span; // variant argument) that does not require visiting, as in // `is_cleanup` above. +/// `std::ops::Try` with an impl for `()`. +pub trait TryHarder { + type Ok; + type Error; + + fn into_result(self) -> Result; + fn from_error(v: Self::Error) -> Self; + fn from_ok(v: Self::Ok) -> Self; +} + +impl TryHarder for () { + type Ok = (); + type Error = !; + + fn into_result(self) -> Result { + Ok(()) + } + + fn from_error(_: Self::Error) -> Self { + unimplemented!() + } + + fn from_ok(v: Self::Ok) -> Self { + v + } +} + +impl TryHarder for Result { + type Ok = T; + type Error = E; + + fn into_result(self) -> Self { + self + } + + fn from_error(e: E) -> Self { + Err(e) + } + + fn from_ok(v: T) -> Self { + Ok(v) + } +} + +macro_rules! yrt { // try backwards + ($expr:expr) => { + match $expr.into_result() { + Ok(v) => R::from_ok(v), + Err(e) => return R::from_error(e), + } + } +} + macro_rules! make_mir_visitor { ($visitor_trait_name:ident, $($mutability:ident)?) => { - pub trait $visitor_trait_name<'tcx> { + pub trait $visitor_trait_name<'tcx, R: TryHarder = ()> { // Override these, and call `self.super_xxx` to revert back to the // default behavior. - fn visit_body(&mut self, body: & $($mutability)? Body<'tcx>) { - self.super_body(body); + fn visit_body(&mut self, body: & $($mutability)? Body<'tcx>) -> R { + self.super_body(body) } fn visit_basic_block_data(&mut self, block: BasicBlock, - data: & $($mutability)? BasicBlockData<'tcx>) { - self.super_basic_block_data(block, data); + data: & $($mutability)? BasicBlockData<'tcx>) -> R { + self.super_basic_block_data(block, data) } fn visit_source_scope_data(&mut self, - scope_data: & $($mutability)? SourceScopeData) { - self.super_source_scope_data(scope_data); + scope_data: & $($mutability)? SourceScopeData) -> R { + self.super_source_scope_data(scope_data) } fn visit_statement(&mut self, statement: & $($mutability)? Statement<'tcx>, - location: Location) { - self.super_statement(statement, location); + location: Location) -> R { + self.super_statement(statement, location) } fn visit_assign(&mut self, place: & $($mutability)? Place<'tcx>, rvalue: & $($mutability)? Rvalue<'tcx>, - location: Location) { - self.super_assign(place, rvalue, location); + location: Location) -> R { + self.super_assign(place, rvalue, location) } fn visit_terminator(&mut self, terminator: & $($mutability)? Terminator<'tcx>, - location: Location) { - self.super_terminator(terminator, location); + location: Location) -> R { + self.super_terminator(terminator, location) } fn visit_terminator_kind(&mut self, kind: & $($mutability)? TerminatorKind<'tcx>, - location: Location) { - self.super_terminator_kind(kind, location); + location: Location) -> R { + self.super_terminator_kind(kind, location) } fn visit_assert_message(&mut self, msg: & $($mutability)? AssertMessage<'tcx>, - location: Location) { - self.super_assert_message(msg, location); + location: Location) -> R { + self.super_assert_message(msg, location) } fn visit_rvalue(&mut self, rvalue: & $($mutability)? Rvalue<'tcx>, - location: Location) { - self.super_rvalue(rvalue, location); + location: Location) -> R { + self.super_rvalue(rvalue, location) } fn visit_operand(&mut self, operand: & $($mutability)? Operand<'tcx>, - location: Location) { - self.super_operand(operand, location); + location: Location) -> R { + self.super_operand(operand, location) } fn visit_ascribe_user_ty(&mut self, place: & $($mutability)? Place<'tcx>, variance: & $($mutability)? ty::Variance, user_ty: & $($mutability)? UserTypeProjection, - location: Location) { - self.super_ascribe_user_ty(place, variance, user_ty, location); + location: Location) -> R { + self.super_ascribe_user_ty(place, variance, user_ty, location) } fn visit_retag(&mut self, kind: & $($mutability)? RetagKind, place: & $($mutability)? Place<'tcx>, - location: Location) { - self.super_retag(kind, place, location); + location: Location) -> R { + self.super_retag(kind, place, location) } fn visit_place(&mut self, place: & $($mutability)? Place<'tcx>, context: PlaceContext, - location: Location) { - self.super_place(place, context, location); + location: Location) -> R { + self.super_place(place, context, location) } fn visit_place_base(&mut self, base: & $($mutability)? PlaceBase<'tcx>, context: PlaceContext, - location: Location) { - self.super_place_base(base, context, location); + location: Location) -> R { + self.super_place_base(base, context, location) } visit_place_fns!($($mutability)?); fn visit_constant(&mut self, constant: & $($mutability)? Constant<'tcx>, - location: Location) { - self.super_constant(constant, location); + location: Location) -> R { + self.super_constant(constant, location) } fn visit_span(&mut self, - span: & $($mutability)? Span) { - self.super_span(span); + span: & $($mutability)? Span) -> R { + self.super_span(span) } fn visit_source_info(&mut self, - source_info: & $($mutability)? SourceInfo) { - self.super_source_info(source_info); + source_info: & $($mutability)? SourceInfo) -> R { + self.super_source_info(source_info) } fn visit_ty(&mut self, ty: $(& $mutability)? Ty<'tcx>, - _: TyContext) { - self.super_ty(ty); + _: TyContext) -> R { + self.super_ty(ty) } fn visit_user_type_projection( &mut self, ty: & $($mutability)? UserTypeProjection, - ) { - self.super_user_type_projection(ty); + ) -> R { + self.super_user_type_projection(ty) } fn visit_user_type_annotation( &mut self, index: UserTypeAnnotationIndex, ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, - ) { - self.super_user_type_annotation(index, ty); + ) -> R { + self.super_user_type_annotation(index, ty) } fn visit_region(&mut self, region: & $($mutability)? ty::Region<'tcx>, - _: Location) { - self.super_region(region); + _: Location) -> R { + self.super_region(region) } fn visit_const(&mut self, constant: & $($mutability)? &'tcx ty::Const<'tcx>, - _: Location) { - self.super_const(constant); + _: Location) -> R { + self.super_const(constant) } fn visit_substs(&mut self, substs: & $($mutability)? SubstsRef<'tcx>, - _: Location) { - self.super_substs(substs); + _: Location) -> R { + self.super_substs(substs) } fn visit_local_decl(&mut self, local: Local, - local_decl: & $($mutability)? LocalDecl<'tcx>) { - self.super_local_decl(local, local_decl); + local_decl: & $($mutability)? LocalDecl<'tcx>) -> R { + self.super_local_decl(local, local_decl) } fn visit_local(&mut self, _local: & $($mutability)? Local, _context: PlaceContext, - _location: Location) { + _location: Location) -> R { + R::from_ok(()) } fn visit_source_scope(&mut self, - scope: & $($mutability)? SourceScope) { - self.super_source_scope(scope); + scope: & $($mutability)? SourceScope) -> R { + self.super_source_scope(scope) } // The `super_xxx` methods comprise the default behavior and are // not meant to be overridden. fn super_body(&mut self, - body: & $($mutability)? Body<'tcx>) { + body: & $($mutability)? Body<'tcx>) -> R { if let Some(yield_ty) = &$($mutability)? body.yield_ty { - self.visit_ty(yield_ty, TyContext::YieldTy(SourceInfo { + yrt!(self.visit_ty(yield_ty, TyContext::YieldTy(SourceInfo { span: body.span, scope: OUTERMOST_SOURCE_SCOPE, - })); + }))); } // for best performance, we want to use an iterator rather @@ -252,20 +306,23 @@ macro_rules! make_mir_visitor { () => (body.basic_blocks().iter_enumerated()); }; for (bb, data) in basic_blocks!($($mutability)?) { - self.visit_basic_block_data(bb, data); + yrt!(self.visit_basic_block_data(bb, data)); } for scope in &$($mutability)? body.source_scopes { - self.visit_source_scope_data(scope); + yrt!(self.visit_source_scope_data(scope)); } - self.visit_ty(&$($mutability)? body.return_ty(), TyContext::ReturnTy(SourceInfo { - span: body.span, - scope: OUTERMOST_SOURCE_SCOPE, - })); + yrt!(self.visit_ty( + &$($mutability)? body.return_ty(), + TyContext::ReturnTy(SourceInfo { + span: body.span, + scope: OUTERMOST_SOURCE_SCOPE, + }), + )); for local in body.local_decls.indices() { - self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); + yrt!(self.visit_local_decl(local, & $($mutability)? body.local_decls[local])); } macro_rules! type_annotations { @@ -274,17 +331,17 @@ macro_rules! make_mir_visitor { }; for (index, annotation) in type_annotations!($($mutability)?) { - self.visit_user_type_annotation( + yrt!(self.visit_user_type_annotation( index, annotation - ); + )); } - self.visit_span(&$($mutability)? body.span); + self.visit_span(&$($mutability)? body.span) } fn super_basic_block_data(&mut self, block: BasicBlock, - data: & $($mutability)? BasicBlockData<'tcx>) { + data: & $($mutability)? BasicBlockData<'tcx>) -> R { let BasicBlockData { statements, terminator, @@ -294,31 +351,38 @@ macro_rules! make_mir_visitor { let mut index = 0; for statement in statements { let location = Location { block: block, statement_index: index }; - self.visit_statement(statement, location); + yrt!(self.visit_statement(statement, location)); index += 1; } if let Some(terminator) = terminator { let location = Location { block: block, statement_index: index }; - self.visit_terminator(terminator, location); + yrt!(self.visit_terminator(terminator, location)); } + + R::from_ok(()) } - fn super_source_scope_data(&mut self, scope_data: & $($mutability)? SourceScopeData) { + fn super_source_scope_data( + &mut self, + scope_data: & $($mutability)? SourceScopeData, + ) -> R { let SourceScopeData { span, parent_scope, } = scope_data; - self.visit_span(span); + yrt!(self.visit_span(span)); if let Some(parent_scope) = parent_scope { - self.visit_source_scope(parent_scope); + yrt!(self.visit_source_scope(parent_scope)); } + + R::from_ok(()) } fn super_statement(&mut self, statement: & $($mutability)? Statement<'tcx>, - location: Location) { + location: Location) -> R { let Statement { source_info, kind, @@ -329,86 +393,88 @@ macro_rules! make_mir_visitor { StatementKind::Assign( box(ref $($mutability)? place, ref $($mutability)? rvalue) ) => { - self.visit_assign(place, rvalue, location); + yrt!(self.visit_assign(place, rvalue, location)); } StatementKind::FakeRead(_, place) => { - self.visit_place( + yrt!(self.visit_place( place, PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), location - ); + )); } StatementKind::SetDiscriminant { place, .. } => { - self.visit_place( + yrt!(self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Store), location - ); + )); } StatementKind::StorageLive(local) => { - self.visit_local( + yrt!(self.visit_local( local, PlaceContext::NonUse(NonUseContext::StorageLive), location - ); + )); } StatementKind::StorageDead(local) => { - self.visit_local( + yrt!(self.visit_local( local, PlaceContext::NonUse(NonUseContext::StorageDead), location - ); + )); } StatementKind::InlineAsm(asm) => { for output in & $($mutability)? asm.outputs[..] { - self.visit_place( + yrt!(self.visit_place( output, PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), location - ); + )); } for (span, input) in & $($mutability)? asm.inputs[..] { - self.visit_span(span); - self.visit_operand(input, location); + yrt!(self.visit_span(span)); + yrt!(self.visit_operand(input, location)); } } StatementKind::Retag(kind, place) => { - self.visit_retag(kind, place, location); + yrt!(self.visit_retag(kind, place, location)); } StatementKind::AscribeUserType( box(ref $($mutability)? place, ref $($mutability)? user_ty), variance ) => { - self.visit_ascribe_user_ty(place, variance, user_ty, location); + yrt!(self.visit_ascribe_user_ty(place, variance, user_ty, location)); } StatementKind::Nop => {} } + + R::from_ok(()) } fn super_assign(&mut self, place: &$($mutability)? Place<'tcx>, rvalue: &$($mutability)? Rvalue<'tcx>, - location: Location) { - self.visit_place( + location: Location) -> R { + yrt!(self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Store), location - ); - self.visit_rvalue(rvalue, location); + )); + self.visit_rvalue(rvalue, location) } fn super_terminator(&mut self, terminator: &$($mutability)? Terminator<'tcx>, - location: Location) { + location: Location) -> R { let Terminator { source_info, kind } = terminator; - self.visit_source_info(source_info); - self.visit_terminator_kind(kind, location); + yrt!(self.visit_source_info(source_info)); + self.visit_terminator_kind(kind, location) } fn super_terminator_kind(&mut self, kind: & $($mutability)? TerminatorKind<'tcx>, - source_location: Location) { + source_location: Location) -> R { match kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume | @@ -426,8 +492,8 @@ macro_rules! make_mir_visitor { values: _, targets: _ } => { - self.visit_operand(discr, source_location); - self.visit_ty(switch_ty, TyContext::Location(source_location)); + yrt!(self.visit_operand(discr, source_location)); + yrt!(self.visit_ty(switch_ty, TyContext::Location(source_location))); } TerminatorKind::Drop { @@ -435,11 +501,11 @@ macro_rules! make_mir_visitor { target: _, unwind: _, } => { - self.visit_place( + yrt!(self.visit_place( location, PlaceContext::MutatingUse(MutatingUseContext::Drop), source_location - ); + )); } TerminatorKind::DropAndReplace { @@ -448,12 +514,12 @@ macro_rules! make_mir_visitor { target: _, unwind: _, } => { - self.visit_place( + yrt!(self.visit_place( location, PlaceContext::MutatingUse(MutatingUseContext::Drop), source_location - ); - self.visit_operand(value, source_location); + )); + yrt!(self.visit_operand(value, source_location)); } TerminatorKind::Call { @@ -463,16 +529,16 @@ macro_rules! make_mir_visitor { cleanup: _, from_hir_call: _, } => { - self.visit_operand(func, source_location); + yrt!(self.visit_operand(func, source_location)); for arg in args { - self.visit_operand(arg, source_location); + yrt!(self.visit_operand(arg, source_location)); } if let Some((destination, _)) = destination { - self.visit_place( + yrt!(self.visit_place( destination, PlaceContext::MutatingUse(MutatingUseContext::Call), source_location - ); + )); } } @@ -483,8 +549,8 @@ macro_rules! make_mir_visitor { target: _, cleanup: _, } => { - self.visit_operand(cond, source_location); - self.visit_assert_message(msg, source_location); + yrt!(self.visit_operand(cond, source_location)); + yrt!(self.visit_assert_message(msg, source_location)); } TerminatorKind::Yield { @@ -492,42 +558,45 @@ macro_rules! make_mir_visitor { resume: _, drop: _, } => { - self.visit_operand(value, source_location); + yrt!(self.visit_operand(value, source_location)); } - } + + R::from_ok(()) } fn super_assert_message(&mut self, msg: & $($mutability)? AssertMessage<'tcx>, - location: Location) { + location: Location) -> R { use crate::mir::interpret::PanicInfo::*; match msg { BoundsCheck { len, index } => { - self.visit_operand(len, location); - self.visit_operand(index, location); + yrt!(self.visit_operand(len, location)); + yrt!(self.visit_operand(index, location)); } Panic { .. } | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero | GeneratorResumedAfterReturn | GeneratorResumedAfterPanic => { // Nothing to visit } } + + R::from_ok(()) } fn super_rvalue(&mut self, rvalue: & $($mutability)? Rvalue<'tcx>, - location: Location) { + location: Location) -> R { match rvalue { Rvalue::Use(operand) => { - self.visit_operand(operand, location); + yrt!(self.visit_operand(operand, location)); } Rvalue::Repeat(value, _) => { - self.visit_operand(value, location); + yrt!(self.visit_operand(value, location)); } Rvalue::Ref(r, bk, path) => { - self.visit_region(r, location); + yrt!(self.visit_region(r, location)); let ctx = match bk { BorrowKind::Shared => PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow @@ -541,49 +610,49 @@ macro_rules! make_mir_visitor { BorrowKind::Mut { .. } => PlaceContext::MutatingUse(MutatingUseContext::Borrow), }; - self.visit_place(path, ctx, location); + yrt!(self.visit_place(path, ctx, location)); } Rvalue::Len(path) => { - self.visit_place( + yrt!(self.visit_place( path, PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), location - ); + )); } Rvalue::Cast(_cast_kind, operand, ty) => { - self.visit_operand(operand, location); - self.visit_ty(ty, TyContext::Location(location)); + yrt!(self.visit_operand(operand, location)); + yrt!(self.visit_ty(ty, TyContext::Location(location))); } Rvalue::BinaryOp(_bin_op, lhs, rhs) | Rvalue::CheckedBinaryOp(_bin_op, lhs, rhs) => { - self.visit_operand(lhs, location); - self.visit_operand(rhs, location); + yrt!(self.visit_operand(lhs, location)); + yrt!(self.visit_operand(rhs, location)); } Rvalue::UnaryOp(_un_op, op) => { - self.visit_operand(op, location); + yrt!(self.visit_operand(op, location)); } Rvalue::Discriminant(place) => { - self.visit_place( + yrt!(self.visit_place( place, PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), location - ); + )); } Rvalue::NullaryOp(_op, ty) => { - self.visit_ty(ty, TyContext::Location(location)); + yrt!(self.visit_ty(ty, TyContext::Location(location))); } Rvalue::Aggregate(kind, operands) => { let kind = &$($mutability)? **kind; match kind { AggregateKind::Array(ty) => { - self.visit_ty(ty, TyContext::Location(location)); + yrt!(self.visit_ty(ty, TyContext::Location(location))); } AggregateKind::Tuple => { } @@ -594,50 +663,52 @@ macro_rules! make_mir_visitor { _user_substs, _active_field_index ) => { - self.visit_substs(substs, location); + yrt!(self.visit_substs(substs, location)); } AggregateKind::Closure( _, closure_substs ) => { - self.visit_substs(closure_substs, location); + yrt!(self.visit_substs(closure_substs, location)); } AggregateKind::Generator( _, generator_substs, _movability, ) => { - self.visit_substs(generator_substs, location); + yrt!(self.visit_substs(generator_substs, location)); } } for operand in operands { - self.visit_operand(operand, location); + yrt!(self.visit_operand(operand, location)); } } } + + R::from_ok(()) } fn super_operand(&mut self, operand: & $($mutability)? Operand<'tcx>, - location: Location) { + location: Location) -> R { match operand { Operand::Copy(place) => { self.visit_place( place, PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), location - ); + ) } Operand::Move(place) => { self.visit_place( place, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), location - ); + ) } Operand::Constant(constant) => { - self.visit_constant(constant, location); + self.visit_constant(constant, location) } } } @@ -646,43 +717,43 @@ macro_rules! make_mir_visitor { place: & $($mutability)? Place<'tcx>, _variance: & $($mutability)? ty::Variance, user_ty: & $($mutability)? UserTypeProjection, - location: Location) { - self.visit_place( + location: Location) -> R { + yrt!(self.visit_place( place, PlaceContext::NonUse(NonUseContext::AscribeUserTy), location - ); - self.visit_user_type_projection(user_ty); + )); + self.visit_user_type_projection(user_ty) } fn super_retag(&mut self, _kind: & $($mutability)? RetagKind, place: & $($mutability)? Place<'tcx>, - location: Location) { + location: Location) -> R { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Retag), location, - ); + ) } fn super_place_base(&mut self, place_base: & $($mutability)? PlaceBase<'tcx>, context: PlaceContext, - location: Location) { + location: Location) -> R { match place_base { PlaceBase::Local(local) => { - self.visit_local(local, context, location); + self.visit_local(local, context, location) } PlaceBase::Static(box Static { kind: _, ty, def_id: _ }) => { - self.visit_ty(& $($mutability)? *ty, TyContext::Location(location)); + self.visit_ty(& $($mutability)? *ty, TyContext::Location(location)) } } } fn super_local_decl(&mut self, local: Local, - local_decl: & $($mutability)? LocalDecl<'tcx>) { + local_decl: & $($mutability)? LocalDecl<'tcx>) -> R { let LocalDecl { mutability: _, ty, @@ -695,82 +766,95 @@ macro_rules! make_mir_visitor { is_block_tail: _, } = local_decl; - self.visit_ty(ty, TyContext::LocalDecl { + yrt!(self.visit_ty(ty, TyContext::LocalDecl { local, source_info: *source_info, - }); + })); for (user_ty, _) in & $($mutability)? user_ty.contents { - self.visit_user_type_projection(user_ty); + yrt!(self.visit_user_type_projection(user_ty)); } - self.visit_source_info(source_info); - self.visit_source_scope(visibility_scope); + yrt!(self.visit_source_info(source_info)); + self.visit_source_scope(visibility_scope) } fn super_source_scope(&mut self, - _scope: & $($mutability)? SourceScope) { + _scope: & $($mutability)? SourceScope) -> R { + R::from_ok(()) } fn super_constant(&mut self, constant: & $($mutability)? Constant<'tcx>, - location: Location) { + location: Location) -> R { let Constant { span, user_ty, literal, } = constant; - self.visit_span(span); + yrt!(self.visit_span(span)); drop(user_ty); // no visit method for this - self.visit_const(literal, location); + self.visit_const(literal, location) } - fn super_span(&mut self, _span: & $($mutability)? Span) { + fn super_span(&mut self, _span: & $($mutability)? Span) -> R { + R::from_ok(()) } - fn super_source_info(&mut self, source_info: & $($mutability)? SourceInfo) { + fn super_source_info(&mut self, source_info: & $($mutability)? SourceInfo) -> R { let SourceInfo { span, scope, } = source_info; - self.visit_span(span); - self.visit_source_scope(scope); + yrt!(self.visit_span(span)); + self.visit_source_scope(scope) } fn super_user_type_projection( &mut self, _ty: & $($mutability)? UserTypeProjection, - ) { + ) -> R { + R::from_ok(()) } fn super_user_type_annotation( &mut self, _index: UserTypeAnnotationIndex, ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, - ) { - self.visit_span(& $($mutability)? ty.span); - self.visit_ty(& $($mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)); + ) -> R { + yrt!(self.visit_span(& $($mutability)? ty.span)); + self.visit_ty(& $($mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)) } - fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) { + fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) -> R { + R::from_ok(()) } - fn super_region(&mut self, _region: & $($mutability)? ty::Region<'tcx>) { + fn super_region(&mut self, _region: & $($mutability)? ty::Region<'tcx>) -> R { + R::from_ok(()) } - fn super_const(&mut self, _const: & $($mutability)? &'tcx ty::Const<'tcx>) { + fn super_const(&mut self, _const: & $($mutability)? &'tcx ty::Const<'tcx>) -> R { + R::from_ok(()) } - fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) { + fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) -> R { + R::from_ok(()) } // Convenience methods - fn visit_location(&mut self, body: & $($mutability)? Body<'tcx>, location: Location) { + fn visit_location( + &mut self, + body: & $($mutability)? Body<'tcx>, + location: Location, + ) -> R { let basic_block = & $($mutability)? body[location.block]; if basic_block.statements.len() == location.statement_index { if let Some(ref $($mutability)? terminator) = basic_block.terminator { self.visit_terminator(terminator, location) + } else { + R::from_ok(()) } } else { let statement = & $($mutability)? @@ -791,12 +875,14 @@ macro_rules! visit_place_fns { place: &mut Place<'tcx>, context: PlaceContext, location: Location, - ) { - self.visit_place_base(&mut place.base, context, location); + ) -> R { + yrt!(self.visit_place_base(&mut place.base, context, location)); if let Some(new_projection) = self.process_projection(&place.projection) { place.projection = self.tcx().intern_place_elems(&new_projection); } + + R::from_ok(()) } fn process_projection( @@ -837,8 +923,8 @@ macro_rules! visit_place_fns { projection: &[PlaceElem<'tcx>], context: PlaceContext, location: Location, - ) { - self.super_projection(base, projection, context, location); + ) -> R { + self.super_projection(base, projection, context, location) } fn visit_projection_elem( @@ -848,8 +934,8 @@ macro_rules! visit_place_fns { elem: &PlaceElem<'tcx>, context: PlaceContext, location: Location, - ) { - self.super_projection_elem(base, proj_base, elem, context, location); + ) -> R { + self.super_projection_elem(base, proj_base, elem, context, location) } fn super_place( @@ -857,7 +943,7 @@ macro_rules! visit_place_fns { place: &Place<'tcx>, context: PlaceContext, location: Location, - ) { + ) -> R { let mut context = context; if !place.projection.is_empty() { @@ -868,12 +954,12 @@ macro_rules! visit_place_fns { }; } - self.visit_place_base(&place.base, context, location); + yrt!(self.visit_place_base(&place.base, context, location)); self.visit_projection(&place.base, &place.projection, context, - location); + location) } fn super_projection( @@ -882,12 +968,14 @@ macro_rules! visit_place_fns { projection: &[PlaceElem<'tcx>], context: PlaceContext, location: Location, - ) { + ) -> R { let mut cursor = projection; while let [proj_base @ .., elem] = cursor { cursor = proj_base; - self.visit_projection_elem(base, cursor, elem, context, location); + yrt!(self.visit_projection_elem(base, cursor, elem, context, location)); } + + R::from_ok(()) } fn super_projection_elem( @@ -897,17 +985,17 @@ macro_rules! visit_place_fns { elem: &PlaceElem<'tcx>, _context: PlaceContext, location: Location, - ) { + ) -> R { match elem { ProjectionElem::Field(_field, ty) => { - self.visit_ty(ty, TyContext::Location(location)); + self.visit_ty(ty, TyContext::Location(location)) } ProjectionElem::Index(local) => { self.visit_local( local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), location - ); + ) } ProjectionElem::Deref | ProjectionElem::Subslice { from: _, to: _ } | @@ -915,6 +1003,7 @@ macro_rules! visit_place_fns { min_length: _, from_end: _ } | ProjectionElem::Downcast(_, _) => { + R::from_ok(()) } } } diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index c4e44091bc90d..49e2aee0dee98 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -1,6 +1,7 @@ use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::*; +use rustc::mir::visit::{Visitor, TyContext, PlaceContext}; use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}}; use rustc_target::spec::abi; use std::borrow::Cow; @@ -57,217 +58,226 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - } } - for local in &body.local_decls { - check_ty(tcx, local.ty, local.source_info.span, def_id)?; - } - // impl trait is gone in MIR, so check the return type manually - check_ty( + let mut visitor = IsMinConstFn { tcx, - tcx.fn_sig(def_id).output().skip_binder(), - body.local_decls.iter().next().unwrap().source_info.span, + body, def_id, - )?; + span: body.span, + }; - for bb in body.basic_blocks() { - check_terminator(tcx, body, def_id, bb.terminator())?; - for stmt in &bb.statements { - check_statement(tcx, body, def_id, stmt)?; + // We could do most of this simply by calling `visit_body`. However, we change the order since + // we want to check each parameter **before** the function body, and the terminator **before** + // the statements. + for (local, decl) in body.local_decls.iter_enumerated() { + visitor.visit_local_decl(local, decl)?; + } + + // impl trait is gone in MIR, so check the return type manually + visitor.span = body.local_decls[RETURN_PLACE].source_info.span; + let ret_ty_ctx = TyContext::ReturnTy(body.local_decls.iter().next().unwrap().source_info); + visitor.visit_ty(tcx.fn_sig(def_id).output().skip_binder(), ret_ty_ctx)?; + + for (bb, block) in body.basic_blocks().iter_enumerated() { + let mut loc = body.terminator_loc(bb); + visitor.visit_terminator(block.terminator(), loc)?; + for (idx, stmt) in block.statements.iter().enumerate() { + loc.statement_index = idx; + visitor.visit_statement(stmt, loc)?; } } + Ok(()) } -fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult { - for ty in ty.walk() { - match ty.kind { - ty::Ref(_, _, hir::Mutability::MutMutable) => return Err(( - span, - "mutable references in const fn are unstable".into(), - )), - ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), - ty::FnPtr(..) => { - if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) { - return Err((span, "function pointers in const fn are unstable".into())) +struct IsMinConstFn<'mir, 'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + span: Span, + body: &'mir Body<'tcx>, +} + +impl Visitor<'tcx, McfResult> for IsMinConstFn<'mir, 'tcx> { + fn visit_local_decl( + &mut self, + local: Local, + decl: &LocalDecl<'tcx>, + ) -> McfResult { + self.span = decl.source_info.span; + self.super_local_decl(local, decl) + } + + fn visit_source_info(&mut self, info: &SourceInfo) -> McfResult { + self.span = info.span; + Ok(()) + } + + fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) -> McfResult { + let IsMinConstFn { tcx, def_id: fn_def_id, span, .. } = *self; + for ty in ty.walk() { + match ty.kind { + ty::Ref(_, _, hir::Mutability::MutMutable) => return Err(( + span, + "mutable references in const fn are unstable".into(), + )), + ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), + ty::FnPtr(..) => { + if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) { + return Err((span, "function pointers in const fn are unstable".into())) + } } - } - ty::Dynamic(preds, _) => { - for pred in preds.iter() { - match pred.skip_binder() { - | ty::ExistentialPredicate::AutoTrait(_) - | ty::ExistentialPredicate::Projection(_) => { - return Err(( - span, - "trait bounds other than `Sized` \ - on const fn parameters are unstable" - .into(), - )) - } - ty::ExistentialPredicate::Trait(trait_ref) => { - if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { + ty::Dynamic(preds, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + | ty::ExistentialPredicate::AutoTrait(_) + | ty::ExistentialPredicate::Projection(_) => { return Err(( span, "trait bounds other than `Sized` \ on const fn parameters are unstable" .into(), - )); + )) + } + ty::ExistentialPredicate::Trait(trait_ref) => { + if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { + return Err(( + span, + "trait bounds other than `Sized` \ + on const fn parameters are unstable" + .into(), + )); + } } } } } + _ => {} } - _ => {} } + + self.super_ty(ty) } - Ok(()) -} -fn check_rvalue( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - rvalue: &Rvalue<'tcx>, - span: Span, -) -> McfResult { - match rvalue { - Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { - check_operand(tcx, operand, span, def_id, body) - } - Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => { - check_place(tcx, place, span, def_id, body) - } - Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { - use rustc::ty::cast::CastTy; - let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - match (cast_in, cast_out) { - (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err(( - span, - "casting pointers to ints is unstable in const fn".into(), - )), - (CastTy::RPtr(_), CastTy::Float) => bug!(), - (CastTy::RPtr(_), CastTy::Int(_)) => bug!(), - (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(), - _ => check_operand(tcx, operand, span, def_id, body), - } - } - Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => { - check_operand(tcx, operand, span, def_id, body) - } - Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) | - Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) | - Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => Err(( - span, - "function pointer casts are not allowed in const fn".into(), - )), - Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => Err(( - span, - "unsizing casts are not allowed in const fn".into(), - )), - // binops are fine on integers - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { - check_operand(tcx, lhs, span, def_id, body)?; - check_operand(tcx, rhs, span, def_id, body)?; - let ty = lhs.ty(body, tcx); - if ty.is_integral() || ty.is_bool() || ty.is_char() { - Ok(()) - } else { - Err(( - span, - "only int, `bool` and `char` operations are stable in const fn".into(), - )) + fn visit_rvalue( + &mut self, + rvalue: &Rvalue<'tcx>, + location: Location, + ) -> McfResult { + let IsMinConstFn { tcx, span, body, .. } = *self; + match rvalue { + Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { + use rustc::ty::cast::CastTy; + let cast_in = CastTy::from_ty(operand.ty(body, tcx)) + .expect("bad input type for cast"); + let cast_out = CastTy::from_ty(cast_ty) + .expect("bad output type for cast"); + match (cast_in, cast_out) { + | (CastTy::Ptr(_), CastTy::Int(_)) + | (CastTy::FnPtr, CastTy::Int(_)) + => return Err(( + span, + "casting pointers to ints is unstable in const fn".into(), + )), + (CastTy::RPtr(_), CastTy::Float) => bug!(), + (CastTy::RPtr(_), CastTy::Int(_)) => bug!(), + (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(), + _ => (), + } } - } - Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()), - Rvalue::NullaryOp(NullOp::Box, _) => Err(( - span, - "heap allocations are not allowed in const fn".into(), - )), - Rvalue::UnaryOp(_, operand) => { - let ty = operand.ty(body, tcx); - if ty.is_integral() || ty.is_bool() { - check_operand(tcx, operand, span, def_id, body) - } else { - Err(( - span, - "only int and `bool` operations are stable in const fn".into(), - )) + | Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) + | Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) + | Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) + => return Err(( + span, + "function pointer casts are not allowed in const fn".into(), + )), + Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => return Err(( + span, + "unsizing casts are not allowed in const fn".into(), + )), + // binops are fine on integers + Rvalue::BinaryOp(_, lhs, _) | Rvalue::CheckedBinaryOp(_, lhs, _) => { + // FIXME: do we need to type check `rhs`? + let ty = lhs.ty(body, tcx); + if !(ty.is_integral() || ty.is_bool() || ty.is_char()) { + return Err(( + span, + "only int, `bool` and `char` operations are stable in const fn".into(), + )) + } } - } - Rvalue::Aggregate(_, operands) => { - for operand in operands { - check_operand(tcx, operand, span, def_id, body)?; + Rvalue::NullaryOp(NullOp::Box, _) => return Err(( + span, + "heap allocations are not allowed in const fn".into(), + )), + Rvalue::UnaryOp(_, operand) => { + let ty = operand.ty(body, tcx); + if !(ty.is_integral() || ty.is_bool()) { + return Err(( + span, + "only int and `bool` operations are stable in const fn".into(), + )) + } } - Ok(()) + + _ => {} } + + self.super_rvalue(rvalue, location) } -} -fn check_statement( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - statement: &Statement<'tcx>, -) -> McfResult { - let span = statement.source_info.span; - match &statement.kind { - StatementKind::Assign(box(place, rval)) => { - check_place(tcx, place, span, def_id, body)?; - check_rvalue(tcx, body, def_id, rval, span) - } + fn visit_statement( + &mut self, + statement: &Statement<'tcx>, + location: Location, + ) -> McfResult { + self.span = statement.source_info.span; + let IsMinConstFn { span, .. } = *self; - StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { - Err((span, "loops and conditional expressions are not stable in const fn".into())) - } + match &statement.kind { + StatementKind::Assign(_) => { + self.super_statement(statement, location) + } - StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body), + StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => { + Err((span, "loops and conditional expressions are not stable in const fn".into())) + } - // just an assignment - StatementKind::SetDiscriminant { .. } => Ok(()), + StatementKind::FakeRead(_, _) => { + self.super_statement(statement, location) + } - | StatementKind::InlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + // just an assignment + StatementKind::SetDiscriminant { .. } => Ok(()), - // These are all NOPs - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Retag { .. } - | StatementKind::AscribeUserType(..) - | StatementKind::Nop => Ok(()), - } -} + | StatementKind::InlineAsm { .. } => { + Err((span, "cannot use inline assembly in const fn".into())) + } -fn check_operand( - tcx: TyCtxt<'tcx>, - operand: &Operand<'tcx>, - span: Span, - def_id: DefId, - body: &Body<'tcx> -) -> McfResult { - match operand { - Operand::Move(place) | Operand::Copy(place) => { - check_place(tcx, place, span, def_id, body) + // These are all NOPs + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag { .. } + | StatementKind::AscribeUserType(..) + | StatementKind::Nop => Ok(()), } - Operand::Constant(_) => Ok(()), } -} -fn check_place( - tcx: TyCtxt<'tcx>, - place: &Place<'tcx>, - span: Span, - def_id: DefId, - body: &Body<'tcx> -) -> McfResult { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; + fn visit_projection_elem( + &mut self, + base: &PlaceBase<'tcx>, + proj_base: &[PlaceElem<'tcx>], + elem: &PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) -> McfResult { + let IsMinConstFn { tcx, def_id, span, body } = *self; + match elem { ProjectionElem::Downcast(..) => { return Err((span, "`match` or `if let` in `const fn` is unstable".into())); } ProjectionElem::Field(..) => { - let base_ty = Place::ty_from(&place.base, &proj_base, body, tcx).ty; + let base_ty = Place::ty_from(base, &proj_base, body, tcx).ty; if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { @@ -282,14 +292,105 @@ fn check_place( | ProjectionElem::Deref | ProjectionElem::Index(_) => {} } + + self.super_projection_elem(base, proj_base, elem, context, location) + } + + fn visit_place_base( + &mut self, + base: &PlaceBase<'tcx>, + _context: PlaceContext, + _location: Location, + ) -> McfResult { + let IsMinConstFn { span, .. } = *self; + + // FIXME: should this call `check_ty` for each place? + // self.super_place_base(base, context, location)?; + + match base { + PlaceBase::Static(box Static { kind: StaticKind::Static, .. }) => { + Err((span, "cannot access `static` items in const fn".into())) + } + PlaceBase::Local(_) + | PlaceBase::Static(box Static { kind: StaticKind::Promoted(_, _), .. }) => Ok(()), + } } - match place.base { - PlaceBase::Static(box Static { kind: StaticKind::Static, .. }) => { - Err((span, "cannot access `static` items in const fn".into())) + fn visit_terminator_kind( + &mut self, + kind: &TerminatorKind<'tcx>, + location: Location, + ) -> McfResult { + let IsMinConstFn { tcx, span, body, .. } = *self; + match kind { + | TerminatorKind::Goto { .. } + | TerminatorKind::Return + | TerminatorKind::Resume + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Assert { .. } + => self.super_terminator_kind(kind, location), + + TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err(( + span, + "loops and conditional expressions are not stable in const fn".into(), + )), + | TerminatorKind::Abort | TerminatorKind::Unreachable => { + Err((span, "const fn with unreachable code is not stable".into())) + } + | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { + Err((span, "const fn generators are unstable".into())) + } + + TerminatorKind::Call { + func, + args: _, + from_hir_call: _, + destination: _, + cleanup: _, + } => { + let fn_ty = func.ty(body, tcx); + if let ty::FnDef(def_id, _) = fn_ty.kind { + + // some intrinsics are waved through if called inside the + // standard library. Users never need to call them directly + match tcx.fn_sig(def_id).abi() { + abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) { + return Err(( + span, + "can only call a curated list of intrinsics \ + in `min_const_fn`".into(), + )) + }, + abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {}, + abi::Abi::Rust => return Err(( + span, + format!( + "can only call other `const fn` within a `const fn`, \ + but `{:?}` is not stable as `const fn`", + func, + ) + .into(), + )), + abi => return Err(( + span, + format!( + "cannot call functions with `{}` abi in `min_const_fn`", + abi, + ).into(), + )), + } + + self.super_terminator_kind(kind, location) + } else { + Err((span, "can only call other const fns within const fn".into())) + } + } + + TerminatorKind::FalseUnwind { .. } => { + Err((span, "loops are not allowed in const fn".into())) + }, } - PlaceBase::Local(_) - | PlaceBase::Static(box Static { kind: StaticKind::Promoted(_, _), .. }) => Ok(()), } } @@ -303,100 +404,6 @@ fn feature_allowed( .map_or(false, |mut features| features.any(|name| name == feature_gate)) } -fn check_terminator( - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - def_id: DefId, - terminator: &Terminator<'tcx>, -) -> McfResult { - let span = terminator.source_info.span; - match &terminator.kind { - | TerminatorKind::Goto { .. } - | TerminatorKind::Return - | TerminatorKind::Resume => Ok(()), - - TerminatorKind::Drop { location, .. } => { - check_place(tcx, location, span, def_id, body) - } - TerminatorKind::DropAndReplace { location, value, .. } => { - check_place(tcx, location, span, def_id, body)?; - check_operand(tcx, value, span, def_id, body) - }, - - TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err(( - span, - "loops and conditional expressions are not stable in const fn".into(), - )), - | TerminatorKind::Abort | TerminatorKind::Unreachable => { - Err((span, "const fn with unreachable code is not stable".into())) - } - | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { - Err((span, "const fn generators are unstable".into())) - } - - TerminatorKind::Call { - func, - args, - from_hir_call: _, - destination: _, - cleanup: _, - } => { - let fn_ty = func.ty(body, tcx); - if let ty::FnDef(def_id, _) = fn_ty.kind { - - // some intrinsics are waved through if called inside the - // standard library. Users never need to call them directly - match tcx.fn_sig(def_id).abi() { - abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) { - return Err(( - span, - "can only call a curated list of intrinsics in `min_const_fn`".into(), - )) - }, - abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {}, - abi::Abi::Rust => return Err(( - span, - format!( - "can only call other `const fn` within a `const fn`, \ - but `{:?}` is not stable as `const fn`", - func, - ) - .into(), - )), - abi => return Err(( - span, - format!( - "cannot call functions with `{}` abi in `min_const_fn`", - abi, - ).into(), - )), - } - - check_operand(tcx, func, span, def_id, body)?; - - for arg in args { - check_operand(tcx, arg, span, def_id, body)?; - } - Ok(()) - } else { - Err((span, "can only call other const fns within const fn".into())) - } - } - - TerminatorKind::Assert { - cond, - expected: _, - msg: _, - target: _, - cleanup: _, - } => check_operand(tcx, cond, span, def_id, body), - - TerminatorKind::FalseUnwind { .. } => { - Err((span, "loops are not allowed in const fn".into())) - }, - } -} - /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted /// for being called from stable `const fn`s (`min_const_fn`). ///