ord_impl! { char usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
+ // Note: This macro is a temporary hack that can be remove once we are building with a compiler
+ // that supports `!`
+ macro_rules! not_stage0 {
+ () => {
+ #[unstable(feature = "never_type", issue = "35121")]
+ impl PartialEq for ! {
+ fn eq(&self, _: &!) -> bool {
+ *self
+ }
+ }
+
+ #[unstable(feature = "never_type", issue = "35121")]
+ impl Eq for ! {}
+
+ #[unstable(feature = "never_type", issue = "35121")]
+ impl PartialOrd for ! {
+ fn partial_cmp(&self, _: &!) -> Option<Ordering> {
+ *self
+ }
+ }
+
+ #[unstable(feature = "never_type", issue = "35121")]
+ impl Ord for ! {
+ fn cmp(&self, _: &!) -> Ordering {
+ *self
+ }
+ }
+ }
+ }
+
+ #[cfg(not(stage0))]
+ not_stage0!();
+
// & pointers
#[stable(feature = "rust1", since = "1.0.0")]
fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp }
+// Note: This macro is a temporary hack that can be remove once we are building with a compiler
+// that supports `!`
+macro_rules! not_stage0 {
+ () => {
+ #[unstable(feature = "never_type", issue = "35121")]
+ impl Debug for ! {
+ fn fmt(&self, _: &mut Formatter) -> Result {
+ *self
+ }
+ }
+
+ #[unstable(feature = "never_type", issue = "35121")]
+ impl Display for ! {
+ fn fmt(&self, _: &mut Formatter) -> Result {
+ *self
+ }
+ }
+ }
+}
+
+#[cfg(not(stage0))]
+not_stage0!();
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for bool {
fn fmt(&self, f: &mut Formatter) -> Result {
#![feature(unboxed_closures)]
#![feature(question_mark)]
+// NOTE: remove the cfg_attr next snapshot
+#![cfg_attr(not(stage0), feature(never_type))]
+
#[macro_use]
mod macros;
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
- if fn_ty.fn_ret().diverges() {
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited.
+ if fn_ty.fn_ret().0.is_never() {
self.add_unreachable_node()
} else {
ret
}
}))
}
+ TyNever => node,
TyTup(tys) => TyTup(tys.move_map(|ty| fld.fold_ty(ty))),
TyPath(qself, path) => {
let qself = qself.map(|QSelf { ty, position }| {
output: match output {
Return(ty) => Return(fld.fold_ty(ty)),
DefaultReturn(span) => DefaultReturn(span),
- NoReturn(span) => NoReturn(span),
},
variadic: variadic,
}
walk_list!(visitor, visit_lifetime, opt_lifetime);
visitor.visit_ty(&mutable_type.ty)
}
+ TyNever => {},
TyTup(ref tuple_element_types) => {
walk_list!(visitor, visit_ty, tuple_element_types);
}
decl: self.lower_fn_decl(&f.decl),
}))
}
+ Never => hir::TyNever,
Tup(ref tys) => hir::TyTup(tys.iter().map(|ty| self.lower_ty(ty)).collect()),
Paren(ref ty) => {
return self.lower_ty(ty);
output: match decl.output {
FunctionRetTy::Ty(ref ty) => hir::Return(self.lower_ty(ty)),
FunctionRetTy::Default(span) => hir::DefaultReturn(span),
- FunctionRetTy::None(span) => hir::NoReturn(span),
},
variadic: decl.variadic,
})
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
/// The different kinds of types recognized by the compiler
pub enum Ty_ {
+ /// A variable length array (`[T]`)
TyVec(P<Ty>),
/// A fixed length array (`[T; n]`)
TyFixedLengthVec(P<Ty>, P<Expr>),
TyRptr(Option<Lifetime>, MutTy),
/// A bare function (e.g. `fn(usize) -> bool`)
TyBareFn(P<BareFnTy>),
+ /// The never type (`!`)
+ TyNever,
/// A tuple (`(A, B, C, D,...)`)
TyTup(HirVec<P<Ty>>),
/// A path (`module::module::...::Type`), optionally
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum FunctionRetTy {
- /// Functions with return type `!`that always
- /// raise an error or exit (i.e. never return to the caller)
- NoReturn(Span),
/// Return type is not specified.
///
/// Functions default to `()` and
impl FunctionRetTy {
pub fn span(&self) -> Span {
match *self {
- NoReturn(span) => span,
DefaultReturn(span) => span,
Return(ref ty) => ty.span,
}
self.print_opt_lifetime(lifetime)?;
self.print_mt(mt)?;
}
+ hir::TyNever => {
+ word(&mut self.s, "!")?;
+ },
hir::TyTup(ref elts) => {
self.popen()?;
self.commasep(Inconsistent, &elts[..], |s, ty| s.print_type(&ty))?;
self.maybe_print_comment(ty.span.lo)
}
hir::DefaultReturn(..) => unreachable!(),
- hir::NoReturn(span) => {
- self.word_nbsp("!")?;
- self.maybe_print_comment(span.lo)
- }
}
}
self.ibox(indent_unit)?;
self.word_space("->")?;
match decl.output {
- hir::NoReturn(_) => self.word_nbsp("!")?,
hir::DefaultReturn(..) => unreachable!(),
hir::Return(ref ty) => self.print_type(&ty)?,
}
self.rebuild_arg_ty_or_output(&ret_ty, lifetime, anon_nums, region_names)
),
hir::DefaultReturn(span) => hir::DefaultReturn(span),
- hir::NoReturn(span) => hir::NoReturn(span)
}
}
ty::TyTrait(..) |
ty::TyStruct(..) |
ty::TyClosure(..) |
+ ty::TyNever |
ty::TyTuple(..) |
ty::TyProjection(..) |
ty::TyParam(..) |
Ty<'gcx>,
&'gcx Substs<'gcx>,
ty::FnSig<'gcx>,
- ty::FnOutput<'gcx>,
&'gcx ty::BareFnTy<'gcx>,
ty::ClosureSubsts<'gcx>,
ty::PolyTraitRef<'gcx>
let adj = infcx.adjustments().get(&expr.id).map(|x| x.clone());
if let Some(adjustment) = adj {
match adjustment {
+ adjustment::AdjustNeverToAny(..) |
adjustment::AdjustReifyFnPointer |
adjustment::AdjustUnsafeFnPointer |
adjustment::AdjustMutToConstPointer => {
let typ = self.infcx.tcx.node_id_to_type(expr.id);
match typ.sty {
ty::TyFnDef(_, _, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
- if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
- let from = bare_fn_ty.sig.0.inputs[0];
- self.check_transmute(expr.span, from, to, expr.id);
- }
+ let from = bare_fn_ty.sig.0.inputs[0];
+ let to = bare_fn_ty.sig.0.output;
+ self.check_transmute(expr.span, from, to, expr.id);
}
_ => {
span_bug!(expr.span, "transmute wasn't a bare fn?!");
use dep_graph::DepNode;
use hir::def::*;
use hir::pat_util;
-use ty::{self, TyCtxt, ParameterEnvironment};
+use ty::{self, Ty, TyCtxt, ParameterEnvironment};
use traits::{self, Reveal};
use ty::subst::Subst;
use lint;
}
hir::ExprCall(ref f, ref args) => {
+ // FIXME(canndrew): This is_never should really be an is_uninhabited
let diverges = !self.ir.tcx.is_method_call(expr.id) &&
- self.ir.tcx.expr_ty_adjusted(&f).fn_ret().diverges();
+ self.ir.tcx.expr_ty_adjusted(&f).fn_ret().0.is_never();
let succ = if diverges {
self.s.exit_ln
} else {
hir::ExprMethodCall(_, _, ref args) => {
let method_call = ty::MethodCall::expr(expr.id);
let method_ty = self.ir.tcx.tables.borrow().method_map[&method_call].ty;
- let succ = if method_ty.fn_ret().diverges() {
+ // FIXME(canndrew): This is_never should really be an is_uninhabited
+ let succ = if method_ty.fn_ret().0.is_never() {
self.s.exit_ln
} else {
succ
}
impl<'a, 'tcx> Liveness<'a, 'tcx> {
- fn fn_ret(&self, id: NodeId) -> ty::PolyFnOutput<'tcx> {
+ fn fn_ret(&self, id: NodeId) -> ty::Binder<Ty<'tcx>> {
let fn_ty = self.ir.tcx.node_id_to_type(id);
match fn_ty.sty {
ty::TyClosure(closure_def_id, substs) =>
self.ir.tcx.region_maps.call_site_extent(id, body.id),
&self.fn_ret(id));
- match fn_ret {
- ty::FnConverging(t_ret)
- if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
-
- let param_env = ParameterEnvironment::for_item(self.ir.tcx, id);
- let t_ret_subst = t_ret.subst(self.ir.tcx, ¶m_env.free_substs);
- let is_nil = self.ir.tcx.infer_ctxt(None, Some(param_env),
- Reveal::All).enter(|infcx| {
- let cause = traits::ObligationCause::dummy();
- traits::fully_normalize(&infcx, cause, &t_ret_subst).unwrap().is_nil()
- });
-
- // for nil return types, it is ok to not return a value expl.
- if !is_nil {
- let ends_with_stmt = match body.expr {
- None if !body.stmts.is_empty() =>
- match body.stmts.last().unwrap().node {
- hir::StmtSemi(ref e, _) => {
- self.ir.tcx.expr_ty(&e) == t_ret
- },
- _ => false
+ if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
+ let param_env = ParameterEnvironment::for_item(self.ir.tcx, id);
+ let t_ret_subst = fn_ret.subst(self.ir.tcx, ¶m_env.free_substs);
+ let is_nil = self.ir.tcx.infer_ctxt(None, Some(param_env),
+ Reveal::All).enter(|infcx| {
+ let cause = traits::ObligationCause::dummy();
+ traits::fully_normalize(&infcx, cause, &t_ret_subst).unwrap().is_nil()
+ });
+
+ // for nil return types, it is ok to not return a value expl.
+ if !is_nil {
+ let ends_with_stmt = match body.expr {
+ None if !body.stmts.is_empty() =>
+ match body.stmts.last().unwrap().node {
+ hir::StmtSemi(ref e, _) => {
+ self.ir.tcx.expr_ty(&e) == fn_ret
},
- _ => false
+ _ => false
+ },
+ _ => false
+ };
+ let mut err = struct_span_err!(self.ir.tcx.sess,
+ sp,
+ E0269,
+ "not all control paths return a value");
+ if ends_with_stmt {
+ let last_stmt = body.stmts.last().unwrap();
+ let original_span = original_sp(self.ir.tcx.sess.codemap(),
+ last_stmt.span, sp);
+ let span_semicolon = Span {
+ lo: original_span.hi - BytePos(1),
+ hi: original_span.hi,
+ expn_id: original_span.expn_id
};
- let mut err = struct_span_err!(self.ir.tcx.sess,
- sp,
- E0269,
- "not all control paths return a value");
- if ends_with_stmt {
- let last_stmt = body.stmts.last().unwrap();
- let original_span = original_sp(self.ir.tcx.sess.codemap(),
- last_stmt.span, sp);
- let span_semicolon = Span {
- lo: original_span.hi - BytePos(1),
- hi: original_span.hi,
- expn_id: original_span.expn_id
- };
- err.span_help(span_semicolon, "consider removing this semicolon:");
- }
- err.emit();
+ err.span_help(span_semicolon, "consider removing this semicolon:");
}
+ err.emit();
}
- ty::FnDiverging
- if self.live_on_entry(entry_ln, self.s.clean_exit_var).is_some() => {
- span_err!(self.ir.tcx.sess, sp, E0270,
- "computation may converge in a function marked as diverging");
- }
-
- _ => {}
}
}
self.cat_expr_autoderefd(expr, autoderefs)
}
+ adjustment::AdjustNeverToAny(..) |
adjustment::AdjustReifyFnPointer |
adjustment::AdjustUnsafeFnPointer |
adjustment::AdjustMutToConstPointer |
let base_cmt = match method_ty {
Some(method_ty) => {
let ref_ty =
- self.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap();
+ self.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap();
self.cat_rvalue_node(node.id(), node.span(), ref_ty)
}
None => base_cmt
// to skip past the binder.
self.tcx().no_late_bound_regions(&method_ty.fn_ret())
.unwrap()
- .unwrap() // overloaded ops do not diverge, either
}
}
use rustc_data_structures::control_flow_graph::ControlFlowGraph;
use hir::def_id::DefId;
use ty::subst::Substs;
-use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
+use ty::{self, AdtDef, ClosureSubsts, Region, Ty};
use util::ppaux;
use rustc_back::slice;
use hir::InlineAsm;
pub promoted: IndexVec<Promoted, Mir<'tcx>>,
/// Return type of the function.
- pub return_ty: FnOutput<'tcx>,
+ pub return_ty: Ty<'tcx>,
/// Variables: these are stack slots corresponding to user variables. They may be
/// assigned many times.
pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
promoted: IndexVec<Promoted, Mir<'tcx>>,
- return_ty: FnOutput<'tcx>,
+ return_ty: Ty<'tcx>,
var_decls: IndexVec<Var, VarDecl<'tcx>>,
arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
&Lvalue::Static(def_id) =>
LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty },
&Lvalue::ReturnPointer =>
- LvalueTy::Ty { ty: mir.return_ty.unwrap() },
+ LvalueTy::Ty { ty: mir.return_ty },
&Lvalue::Projection(ref proj) =>
proj.base.ty(mir, tcx).projection_ty(tcx, &proj.elem),
}
use middle::const_val::ConstVal;
use hir::def_id::DefId;
use ty::subst::Substs;
-use ty::{ClosureSubsts, FnOutput, Region, Ty};
+use ty::{ClosureSubsts, Region, Ty};
use mir::repr::*;
use rustc_const_math::ConstUsize;
use rustc_data_structures::tuple_slice::TupleSlice;
//
// For the most part, we do not destructure things external to the
// MIR, e.g. types, spans, etc, but simply visit them and stop. This
-// avoids duplication with other visitors like `TypeFoldable`. But
-// there is one exception: we do destructure the `FnOutput` to reach
-// the type within. Just because.
+// avoids duplication with other visitors like `TypeFoldable`.
//
// ## Updating
//
self.super_source_info(source_info);
}
- fn visit_fn_output(&mut self,
- fn_output: & $($mutability)* FnOutput<'tcx>) {
- self.super_fn_output(fn_output);
- }
-
fn visit_ty(&mut self,
ty: & $($mutability)* Ty<'tcx>) {
self.super_ty(ty);
self.visit_visibility_scope_data(scope);
}
- self.visit_fn_output(&$($mutability)* mir.return_ty);
+ self.visit_ty(&$($mutability)* mir.return_ty);
for var_decl in &$($mutability)* mir.var_decls {
self.visit_var_decl(var_decl);
self.visit_visibility_scope(scope);
}
- fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) {
- match *fn_output {
- FnOutput::FnConverging(ref $($mutability)* ty) => {
- self.visit_ty(ty);
- }
- FnOutput::FnDiverging => {
- }
- }
- }
-
fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) {
}
ty::TySlice(..) |
ty::TyRawPtr(..) |
ty::TyRef(..) |
+ ty::TyNever |
ty::TyTuple(..) |
ty::TyParam(..) |
ty::TyProjection(..) => {
ty::TyProjection(..) => Some(13),
ty::TyParam(..) => Some(14),
ty::TyAnon(..) => Some(15),
+ ty::TyNever => Some(16),
ty::TyInfer(..) | ty::TyError => None
}
}
return Some(MethodViolationCode::ReferencesSelf);
}
}
- if let ty::FnConverging(result_type) = sig.0.output {
- if self.contains_illegal_self_type_reference(trait_def_id, result_type) {
- return Some(MethodViolationCode::ReferencesSelf);
- }
+ if self.contains_illegal_self_type_reference(trait_def_id, sig.0.output) {
+ return Some(MethodViolationCode::ReferencesSelf);
}
// We can't monomorphize things like `fn foo<A>(...)`.
abi: Abi::Rust,
sig: ty::Binder(ty::FnSig {
inputs: _,
- output: ty::FnConverging(_),
+ output: _,
variadic: false
})
}) |
abi: Abi::Rust,
sig: ty::Binder(ty::FnSig {
inputs: _,
- output: ty::FnConverging(_),
+ output: _,
variadic: false
})
}) => {
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) |
ty::TyChar | ty::TyBox(_) | ty::TyRef(..) |
- ty::TyArray(..) | ty::TyClosure(..) |
+ ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever |
ty::TyError => {
// safe for everything
Where(ty::Binder(Vec::new()))
ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar |
- ty::TyRawPtr(..) | ty::TyError |
+ ty::TyRawPtr(..) | ty::TyError | ty::TyNever |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
Where(ty::Binder(Vec::new()))
}
ty::TyError |
ty::TyInfer(ty::IntVar(_)) |
ty::TyInfer(ty::FloatVar(_)) |
+ ty::TyNever |
ty::TyChar => {
Vec::new()
}
def_id: fn_trait_def_id,
substs: self.mk_substs(trait_substs),
};
- ty::Binder((trait_ref, sig.0.output.unwrap_or(self.mk_nil())))
+ ty::Binder((trait_ref, sig.0.output))
}
}
#[derive(Copy, Clone)]
pub enum AutoAdjustment<'tcx> {
- AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
- AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer
- AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer
+ AdjustNeverToAny(Ty<'tcx>), // go from ! to any type
+ AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
+ AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer
+ AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer
AdjustDerefRef(AutoDerefRef<'tcx>),
}
impl<'tcx> AutoAdjustment<'tcx> {
pub fn is_identity(&self) -> bool {
match *self {
+ AdjustNeverToAny(ty) => ty.is_never(),
AdjustReifyFnPointer |
AdjustUnsafeFnPointer |
AdjustMutToConstPointer => false,
return match adjustment {
Some(adjustment) => {
match *adjustment {
+ AdjustNeverToAny(ref ty) => ty,
+
AdjustReifyFnPointer => {
match self.sty {
ty::TyFnDef(_, _, f) => tcx.mk_fn_ptr(f),
if let Some(method_ty) = method_type(method_call) {
// Method calls always have all late-bound regions
// fully instantiated.
- let fn_ret = tcx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
- adjusted_ty = fn_ret.unwrap();
+ adjusted_ty = tcx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
}
match adjusted_ty.builtin_deref(true, NoPreference) {
Some(mt) => mt.ty,
// Scalar and unique types are sendable, and durable
ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) |
- ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) |
+ ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar => {
TC::None
}
pub u64: Ty<'tcx>,
pub f32: Ty<'tcx>,
pub f64: Ty<'tcx>,
+ pub never: Ty<'tcx>,
pub err: Ty<'tcx>,
}
CommonTypes {
bool: mk(TyBool),
char: mk(TyChar),
+ never: mk(TyNever),
err: mk(TyError),
isize: mk(TyInt(ast::IntTy::Is)),
i8: mk(TyInt(ast::IntTy::I8)),
for &Interned(t) in tcx.interners.type_.borrow().iter() {
let variant = match t.sty {
ty::TyBool | ty::TyChar | ty::TyInt(..) | ty::TyUint(..) |
- ty::TyFloat(..) | ty::TyStr => continue,
+ ty::TyFloat(..) | ty::TyStr | ty::TyNever => continue,
ty::TyError => /* unimportant */ continue,
$(ty::$variant(..) => &mut $variant,)*
};
self.mk_tup(Vec::new())
}
+ pub fn mk_diverging_default(self) -> Ty<'tcx> {
+ if self.sess.features.borrow().never_type {
+ self.types.never
+ } else {
+ self.mk_nil()
+ }
+ }
+
pub fn mk_bool(self) -> Ty<'tcx> {
self.mk_ty(TyBool)
}
fn sort_string(&self, tcx: TyCtxt<'a, 'gcx, 'lcx>) -> String {
match self.sty {
ty::TyBool | ty::TyChar | ty::TyInt(_) |
- ty::TyUint(_) | ty::TyFloat(_) | ty::TyStr => self.to_string(),
+ ty::TyUint(_) | ty::TyFloat(_) | ty::TyStr | ty::TyNever => self.to_string(),
ty::TyTuple(ref tys) if tys.is_empty() => self.to_string(),
ty::TyEnum(def, _) => format!("enum `{}`", tcx.item_path_str(def.did)),
StrSimplifiedType,
VecSimplifiedType,
PtrSimplifiedType,
+ NeverSimplifiedType,
TupleSimplifiedType(usize),
TraitSimplifiedType(DefId),
StructSimplifiedType(DefId),
ty::TyClosure(def_id, _) => {
Some(ClosureSimplifiedType(def_id))
}
+ ty::TyNever => Some(NeverSimplifiedType),
ty::TyTuple(ref tys) => {
Some(TupleSimplifiedType(tys.len()))
}
&ty::TyInt(_) |
&ty::TyFloat(_) |
&ty::TyUint(_) |
+ &ty::TyNever |
&ty::TyStr => {
}
let mut computation = FlagComputation::new();
computation.add_tys(&fn_sig.0.inputs);
-
- if let ty::FnConverging(output) = fn_sig.0.output {
- computation.add_ty(output);
- }
+ computation.add_ty(fn_sig.0.output);
self.add_bound_computation(&computation);
}
sig.super_fold_with(self)
}
- fn fold_output(&mut self,
- output: &ty::FnOutput<'tcx>)
- -> ty::FnOutput<'tcx> {
- output.super_fold_with(self)
- }
-
fn fold_bare_fn_ty(&mut self,
fty: &'tcx ty::BareFnTy<'tcx>)
-> &'tcx ty::BareFnTy<'tcx>
ty::TyAnon(..) |
ty::TyInfer(_) |
ty::TyError |
+ ty::TyNever |
ty::TyFloat(_) => None,
}
}
ty::TyFloat(FloatTy::F64) => Scalar { value: F64, non_zero: false },
ty::TyFnPtr(_) => Scalar { value: Pointer, non_zero: true },
+ // The never type.
+ ty::TyNever => Univariant { variant: Struct::new(dl, false), non_zero: false },
+
// Potentially-fat pointers.
ty::TyBox(pointee) |
ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) |
pub use self::sty::{Binder, DebruijnIndex};
pub use self::sty::{BuiltinBound, BuiltinBounds, ExistentialBounds};
-pub use self::sty::{BareFnTy, FnSig, PolyFnSig, FnOutput, PolyFnOutput};
+pub use self::sty::{BareFnTy, FnSig, PolyFnSig};
pub use self::sty::{ClosureTy, InferTy, ParamTy, ProjectionTy, TraitTy};
pub use self::sty::{ClosureSubsts, TypeAndMut};
pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
pub use self::sty::Issue32330;
pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid, SkolemizedRegionVid};
pub use self::sty::BoundRegion::*;
-pub use self::sty::FnOutput::*;
pub use self::sty::InferTy::*;
pub use self::sty::Region::*;
pub use self::sty::TypeVariants::*;
let result = match ty.sty {
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
TyBox(..) | TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
- TyArray(..) | TyClosure(..) => {
+ TyArray(..) | TyClosure(..) | TyNever => {
vec![]
}
ty::TyInt(..) | // OutlivesScalar
ty::TyUint(..) | // OutlivesScalar
ty::TyFloat(..) | // OutlivesScalar
+ ty::TyNever | // ...
ty::TyEnum(..) | // OutlivesNominalType
ty::TyStruct(..) | // OutlivesNominalType
ty::TyBox(..) | // OutlivesNominalType (ish)
let inputs = relate_arg_vecs(relation,
&a.inputs,
&b.inputs)?;
+ let output = relation.relate(&a.output, &b.output)?;
- let output = match (a.output, b.output) {
- (ty::FnConverging(a_ty), ty::FnConverging(b_ty)) =>
- Ok(ty::FnConverging(relation.relate(&a_ty, &b_ty)?)),
- (ty::FnDiverging, ty::FnDiverging) =>
- Ok(ty::FnDiverging),
- (a, b) =>
- Err(TypeError::ConvergenceMismatch(
- expected_found(relation, &(a != ty::FnDiverging), &(b != ty::FnDiverging)))),
- }?;
-
- return Ok(ty::FnSig {inputs: inputs,
- output: output,
- variadic: a.variadic});
+ Ok(ty::FnSig {inputs: inputs,
+ output: output,
+ variadic: a.variadic})
}
}
Ok(tcx.types.err)
}
+ (&ty::TyNever, _) |
(&ty::TyChar, _) |
(&ty::TyBool, _) |
(&ty::TyInt(_), _) |
}
}
-impl<'a, 'tcx> Lift<'tcx> for ty::FnOutput<'a> {
- type Lifted = ty::FnOutput<'tcx>;
- fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
- match *self {
- ty::FnConverging(ty) => {
- tcx.lift(&ty).map(ty::FnConverging)
- }
- ty::FnDiverging => Some(ty::FnDiverging)
- }
- }
-}
-
impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> {
type Lifted = ty::FnSig<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)),
ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) |
ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) |
- ty::TyParam(..) => self.sty.clone(),
+ ty::TyParam(..) | ty::TyNever => self.sty.clone(),
};
folder.tcx().mk_ty(sty)
}
ty::TyAnon(_, ref substs) => substs.visit_with(visitor),
ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) |
ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) |
- ty::TyParam(..) => false,
+ ty::TyParam(..) | ty::TyNever => false,
}
}
}
}
-impl<'tcx> TypeFoldable<'tcx> for ty::FnOutput<'tcx> {
- fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
- match *self {
- ty::FnConverging(ref ty) => ty::FnConverging(ty.fold_with(folder)),
- ty::FnDiverging => ty::FnDiverging
- }
- }
-
- fn fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
- folder.fold_output(self)
- }
-
- fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
- match *self {
- ty::FnConverging(ref ty) => ty.visit_with(visitor),
- ty::FnDiverging => false,
- }
- }
-}
-
impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::FnSig { inputs: self.inputs.fold_with(folder),
use hir;
-use self::FnOutput::*;
use self::InferTy::*;
use self::TypeVariants::*;
/// `|a| a`.
TyClosure(DefId, ClosureSubsts<'tcx>),
+ /// The never type `!`
+ TyNever,
+
/// A tuple type. For example, `(i32, bool)`.
TyTuple(&'tcx [Ty<'tcx>]),
pub sig: PolyFnSig<'tcx>,
}
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
-pub enum FnOutput<'tcx> {
- FnConverging(Ty<'tcx>),
- FnDiverging
-}
-
-impl<'tcx> FnOutput<'tcx> {
- pub fn diverges(&self) -> bool {
- *self == FnDiverging
- }
-
- pub fn unwrap(self) -> Ty<'tcx> {
- match self {
- ty::FnConverging(t) => t,
- ty::FnDiverging => bug!()
- }
- }
-
- pub fn unwrap_or(self, def: Ty<'tcx>) -> Ty<'tcx> {
- match self {
- ty::FnConverging(t) => t,
- ty::FnDiverging => def
- }
- }
-
- pub fn maybe_converging(self) -> Option<Ty<'tcx>> {
- match self {
- ty::FnConverging(t) => Some(t),
- ty::FnDiverging => None
- }
- }
-}
-
-pub type PolyFnOutput<'tcx> = Binder<FnOutput<'tcx>>;
-
-impl<'tcx> PolyFnOutput<'tcx> {
- pub fn diverges(&self) -> bool {
- self.0.diverges()
- }
-}
-
/// Signature of a function type, which I have arbitrarily
/// decided to use to refer to the input/output types.
///
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FnSig<'tcx> {
pub inputs: Vec<Ty<'tcx>>,
- pub output: FnOutput<'tcx>,
+ pub output: Ty<'tcx>,
pub variadic: bool
}
pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.inputs[index])
}
- pub fn output(&self) -> ty::Binder<FnOutput<'tcx>> {
+ pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.output.clone())
}
pub fn variadic(&self) -> bool {
}
}
- pub fn is_empty(&self, _cx: TyCtxt) -> bool {
- // FIXME(#24885): be smarter here
+ pub fn is_never(&self) -> bool {
+ match self.sty {
+ TyNever => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_uninhabited(&self, _cx: TyCtxt) -> bool {
+ // FIXME(#24885): be smarter here, the AdtDefData::is_empty method could easily be made
+ // more complete.
match self.sty {
TyEnum(def, _) | TyStruct(def, _) => def.is_empty(),
- _ => false
+
+ // FIXME(canndrew): There's no reason why these can't be uncommented, they're tested
+ // and they don't break anything. But I'm keeping my changes small for now.
+ //TyNever => true,
+ //TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited(cx)),
+
+ // FIXME(canndrew): this line breaks core::fmt
+ //TyRef(_, ref tm) => tm.ty.is_uninhabited(cx),
+ _ => false,
}
}
self.fn_sig().inputs()
}
- pub fn fn_ret(&self) -> Binder<FnOutput<'tcx>> {
+ pub fn fn_ret(&self) -> Binder<Ty<'tcx>> {
self.fn_sig().output()
}
TyArray(_, _) |
TySlice(_) |
TyRawPtr(_) |
+ TyNever |
TyTuple(_) |
TyParam(_) |
TyInfer(_) |
self.def_id(data.trait_ref.def_id);
self.hash(data.item_name.as_str());
}
+ TyNever |
TyBool |
TyChar |
TyStr |
// Fast-path for primitive types
let result = match self.sty {
- TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
+ TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) | TyNever |
TyRawPtr(..) | TyFnDef(..) | TyFnPtr(_) | TyRef(_, TypeAndMut {
mutbl: hir::MutImmutable, ..
}) => Some(false),
let result = match self.sty {
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
TyBox(..) | TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
- TyArray(..) | TyTuple(..) | TyClosure(..) => Some(true),
+ TyArray(..) | TyTuple(..) | TyClosure(..) | TyNever => Some(true),
TyStr | TyTrait(..) | TySlice(_) => Some(false),
fn push_subtypes<'tcx>(stack: &mut Vec<Ty<'tcx>>, parent_ty: Ty<'tcx>) {
match parent_ty.sty {
ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) |
- ty::TyStr | ty::TyInfer(_) | ty::TyParam(_) | ty::TyError => {
+ ty::TyStr | ty::TyInfer(_) | ty::TyParam(_) | ty::TyNever | ty::TyError => {
}
ty::TyBox(ty) | ty::TyArray(ty, _) | ty::TySlice(ty) => {
stack.push(ty);
}
fn push_sig_subtypes<'tcx>(stack: &mut Vec<Ty<'tcx>>, sig: &ty::PolyFnSig<'tcx>) {
- match sig.0.output {
- ty::FnConverging(output) => { stack.push(output); }
- ty::FnDiverging => { }
- }
+ stack.push(sig.0.output);
push_reversed(stack, &sig.0.inputs);
}
ty::TyFloat(..) |
ty::TyError |
ty::TyStr |
+ ty::TyNever |
ty::TyParam(_) => {
// WfScalar, WfParameter, etc
}
use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
use ty::{TyBool, TyChar, TyStruct, TyEnum};
use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
-use ty::{TyParam, TyRawPtr, TyRef, TyTuple};
+use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple};
use ty::TyClosure;
use ty::{TyBox, TyTrait, TyInt, TyUint, TyInfer};
use ty::{self, Ty, TyCtxt, TypeFoldable};
fn fn_sig(f: &mut fmt::Formatter,
inputs: &[Ty],
variadic: bool,
- output: ty::FnOutput)
+ output: Ty)
-> fmt::Result {
write!(f, "(")?;
let mut inputs = inputs.iter();
}
}
write!(f, ")")?;
-
- match output {
- ty::FnConverging(ty) => {
- if !ty.is_nil() {
- write!(f, " -> {}", ty)?;
- }
- Ok(())
- }
- ty::FnDiverging => {
- write!(f, " -> !")
- }
+ if !output.is_nil() {
+ write!(f, " -> {}", output)?;
}
+
+ Ok(())
}
/// Namespace of the path given to parameterized to print.
if !verbose && fn_trait_kind.is_some() && projections.len() == 1 {
let projection_ty = projections[0].ty;
if let TyTuple(ref args) = substs.types.get_slice(subst::TypeSpace)[0].sty {
- return fn_sig(f, args, false, ty::FnConverging(projection_ty));
+ return fn_sig(f, args, false, projection_ty);
}
}
impl<'tcx> fmt::Debug for ty::adjustment::AutoAdjustment<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
+ ty::adjustment::AdjustNeverToAny(ref target) => {
+ write!(f, "AdjustNeverToAny({:?})", target)
+ }
ty::adjustment::AdjustReifyFnPointer => {
write!(f, "AdjustReifyFnPointer")
}
}
write!(f, "{}", tm)
}
+ TyNever => write!(f, "!"),
TyTuple(ref tys) => {
write!(f, "(")?;
let mut tys = tys.iter();
// except according to those terms.
-use rustc::ty::{FnOutput, TyCtxt};
+use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::util::nodemap::FnvHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
}
}
-struct MovePathDataBuilder<'a, 'tcx: 'a> {
- mir: &'a Mir<'tcx>,
+struct MovePathDataBuilder<'tcx> {
pre_move_paths: Vec<PreMovePath<'tcx>>,
rev_lookup: MovePathLookup<'tcx>,
}
}
}
-impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
+impl<'tcx> MovePathDataBuilder<'tcx> {
fn lookup(&mut self, lval: &Lvalue<'tcx>) -> Lookup<MovePathIndex> {
let proj = match *lval {
Lvalue::Var(var_idx) =>
// BlockContexts constructed on each iteration. (Moving is more
// straight-forward than mutable borrows in this instance.)
let mut builder = MovePathDataBuilder {
- mir: mir,
pre_move_paths: Vec::new(),
rev_lookup: MovePathLookup::new(mir),
};
TerminatorKind::Return => {
let source = Location { block: bb,
index: bb_data.statements.len() };
- if let FnOutput::FnConverging(_) = bb_ctxt.builder.mir.return_ty {
- debug!("gather_moves Return on_move_out_lval return {:?}", source);
- bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source);
- } else {
- debug!("gather_moves Return on_move_out_lval \
- assuming unreachable return {:?}", source);
- }
+ debug!("gather_moves Return on_move_out_lval return {:?}", source);
+ bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source);
}
TerminatorKind::If { ref cond, targets: _ } => {
}
}
-struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
+struct BlockContext<'b, 'tcx: 'b> {
_tcx: TyCtxt<'b, 'tcx, 'tcx>,
moves: &'b mut Vec<MoveOut>,
- builder: MovePathDataBuilder<'a, 'tcx>,
+ builder: MovePathDataBuilder<'tcx>,
path_map: &'b mut Vec<Vec<MoveOutIndex>>,
loc_map_bb: &'b mut Vec<Vec<MoveOutIndex>>,
}
-impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
+impl<'b, 'tcx: 'b> BlockContext<'b, 'tcx> {
fn on_move_out_lval(&mut self,
stmt_kind: StmtKind,
lval: &Lvalue<'tcx>,
// Check for empty enum, because is_useful only works on inhabited types.
let pat_ty = cx.tcx.node_id_to_type(scrut.id);
if inlined_arms.is_empty() {
- if !pat_ty.is_empty(cx.tcx) {
+ if !pat_ty.is_uninhabited(cx.tcx) {
// We know the type is inhabited, so this must be wrong
let mut err = struct_span_err!(cx.tcx.sess, ex.span, E0002,
"non-exhaustive patterns: type {} is non-empty",
possibly adding wildcards or more match arms.");
err.emit();
}
- // If the type *is* empty, it's vacuously exhaustive
+ // If the type *is* uninhabited, it's vacuously exhaustive
return;
}
abi: Abi::Rust,
sig: ty::Binder(ty::FnSig {
inputs: input_args,
- output: ty::FnConverging(output_ty),
+ output: output_ty,
variadic: false,
}),
}))
let typ = cx.tcx.node_id_to_type(expr.id);
match typ.sty {
ty::TyFnDef(_, _, ref bare_fn) if bare_fn.abi == RustIntrinsic => {
- if let ty::FnConverging(to) = bare_fn.sig.0.output {
- let from = bare_fn.sig.0.inputs[0];
- return Some((&from.sty, &to.sty));
- }
+ let from = bare_fn.sig.0.inputs[0];
+ let to = bare_fn.sig.0.output;
+ return Some((&from.sty, &to.sty));
},
_ => ()
}
// Primitive types with a stable representation.
ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
- ty::TyFloat(..) => FfiSafe,
+ ty::TyFloat(..) | ty::TyNever => FfiSafe,
ty::TyBox(..) => {
FfiUnsafe("found Rust type Box<_> in foreign module, \
}
let sig = cx.erase_late_bound_regions(&bare_fn.sig);
- match sig.output {
- ty::FnDiverging => {}
- ty::FnConverging(output) => {
- if !output.is_nil() {
- let r = self.check_type_for_ffi(cache, output);
- match r {
- FfiSafe => {}
- _ => { return r; }
- }
- }
+ if !sig.output.is_nil() {
+ let r = self.check_type_for_ffi(cache, sig.output);
+ match r {
+ FfiSafe => {}
+ _ => { return r; }
}
}
for arg in sig.inputs {
}
if let hir::Return(ref ret_hir) = decl.output {
- let ret_ty = sig.output.unwrap();
+ let ret_ty = sig.output;
if !ret_ty.is_nil() {
self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty);
}
let t = cx.tcx.expr_ty(&expr);
let warned = match t.sty {
ty::TyTuple(ref tys) if tys.is_empty() => return,
+ ty::TyNever => return,
ty::TyBool => return,
ty::TyStruct(def, _) |
ty::TyEnum(def, _) => {
|this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref)))
})
}
+
+ adjustment::AdjustNeverToAny(ref ty) => {
+ this.emit_enum_variant("AdjustNeverToAny", 5, 1, |this| {
+ this.emit_enum_variant_arg(0, |this| Ok(this.emit_ty(ecx, ty)))
+ })
+ }
}
});
}
-> adjustment::AutoAdjustment<'tcx> {
self.read_enum("AutoAdjustment", |this| {
let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer",
- "AdjustMutToConstPointer", "AdjustDerefRef"];
+ "AdjustMutToConstPointer", "AdjustDerefRef",
+ "AdjustNeverToAny"];
this.read_enum_variant(&variants, |this, i| {
Ok(match i {
1 => adjustment::AdjustReifyFnPointer,
adjustment::AdjustDerefRef(auto_deref_ref)
}
+ 5 => {
+ let ty: Ty<'tcx> = this.read_enum_variant_arg(0, |this| {
+ Ok(this.read_ty(dcx))
+ }).unwrap();
+
+ adjustment::AdjustNeverToAny(ty)
+ }
_ => bug!("bad enum variant for adjustment::AutoAdjustment")
})
})
let tcx = self.tcx;
match self.next() {
'b' => return tcx.types.bool,
+ '!' => return tcx.types.never,
'i' => { /* eat the s of is */ self.next(); return tcx.types.isize },
'u' => { /* eat the s of us */ self.next(); return tcx.types.usize },
'M' => {
'N' => false,
r => bug!("bad variadic: {}", r),
};
- let output = match self.peek() {
- 'z' => {
- self.pos += 1;
- ty::FnDiverging
- }
- _ => ty::FnConverging(self.parse_ty())
- };
+ let output = self.parse_ty();
ty::Binder(ty::FnSig {inputs: inputs,
output: output,
variadic: variadic})
match t.sty {
ty::TyBool => { write!(w, "b"); }
ty::TyChar => { write!(w, "c"); }
+ ty::TyNever => { write!(w, "!"); }
ty::TyInt(t) => {
match t {
ast::IntTy::Is => write!(w, "is"),
} else {
write!(w, "N");
}
- match fsig.0.output {
- ty::FnConverging(result_type) => {
- enc_ty(w, cx, result_type);
- }
- ty::FnDiverging => {
- write!(w, "z");
- }
- }
+ enc_ty(w, cx, fsig.0.output);
}
pub fn enc_builtin_bounds(w: &mut Cursor<Vec<u8>>, _cx: &ctxt, bs: &ty::BuiltinBounds) {
ExprKind::LogicalOp { .. } |
ExprKind::Box { .. } |
ExprKind::Cast { .. } |
+ ExprKind::NeverToAny { .. } |
ExprKind::ReifyFnPointer { .. } |
ExprKind::UnsafeFnPointer { .. } |
ExprKind::Unsize { .. } |
ExprKind::Block { .. } |
ExprKind::Match { .. } |
ExprKind::If { .. } |
+ ExprKind::NeverToAny { .. } |
ExprKind::Loop { .. } |
ExprKind::LogicalOp { .. } |
ExprKind::Call { .. } |
ExprKind::LogicalOp { .. } |
ExprKind::If { .. } |
ExprKind::Match { .. } |
+ ExprKind::NeverToAny { .. } |
ExprKind::Call { .. } =>
Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Match { discriminant, arms } => {
this.match_expr(destination, expr_span, block, discriminant, arms)
}
+ ExprKind::NeverToAny { source } => {
+ let source = this.hir.mirror(source);
+ let is_call = match source.kind {
+ ExprKind::Call { .. } => true,
+ _ => false,
+ };
+
+ unpack!(block = this.as_rvalue(block, source));
+
+ // This is an optimization. If the expression was a call then we already have an
+ // unreachable block. Don't bother to terminate it and create a new one.
+ if is_call {
+ block.unit()
+ } else {
+ this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
+ let end_block = this.cfg.start_new_block();
+ end_block.unit()
+ }
+ }
ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
let operand = unpack!(block = this.as_operand(block, cond_expr));
ExprKind::Call { ty, fun, args } => {
let diverges = match ty.sty {
ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
- f.sig.0.output.diverges()
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ f.sig.0.output.is_never()
}
_ => false
};
pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
fn_id: ast::NodeId,
arguments: A,
- return_ty: ty::FnOutput<'gcx>,
+ return_ty: Ty<'gcx>,
ast_block: &'gcx hir::Block)
-> (Mir<'tcx>, ScopeAuxiliaryVec)
where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
});
let ty = tcx.expr_ty_adjusted(ast_expr);
- builder.finish(vec![], IndexVec::new(), ty::FnConverging(ty))
+ builder.finish(vec![], IndexVec::new(), ty)
}
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn finish(self,
upvar_decls: Vec<UpvarDecl>,
arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
- return_ty: ty::FnOutput<'tcx>)
+ return_ty: Ty<'tcx>)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
fn args_and_body<A>(&mut self,
mut block: BasicBlock,
- return_ty: ty::FnOutput<'tcx>,
+ return_ty: Ty<'tcx>,
arguments: A,
argument_extent: CodeExtent,
ast_block: &'gcx hir::Block)
}
// FIXME(#32959): temporary hack for the issue at hand
- let return_is_unit = if let ty::FnConverging(t) = return_ty {
- t.is_nil()
- } else {
- false
- };
+ let return_is_unit = return_ty.is_nil();
// start the first basic block and translate the body
unpack!(block = self.ast_block(&Lvalue::ReturnPointer, return_is_unit, block, ast_block));
use rustc::hir::def_id::DefId;
use rustc::mir::repr::*;
use rustc::mir::mir_map::MirMap;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::TyCtxt;
use std::fmt::Debug;
use std::io::{self, Write};
use syntax::ast::NodeId;
write!(w, "{:?}: {}", Lvalue::Arg(Arg::new(i)), escape(&arg.ty))?;
}
- write!(w, ") -> ")?;
-
- // fn return type.
- match mir.return_ty {
- ty::FnOutput::FnConverging(ty) => write!(w, "{}", escape(ty))?,
- ty::FnOutput::FnDiverging => write!(w, "!")?,
- }
-
+ write!(w, ") -> {}", escape(mir.return_ty))?;
write!(w, r#"<br align="left"/>"#)?;
// User variable types (including the user's name in a comment).
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
};
}
+ Some(&ty::adjustment::AdjustNeverToAny(adjusted_ty)) => {
+ expr = Expr {
+ temp_lifetime: temp_lifetime,
+ ty: adjusted_ty,
+ span: self.span,
+ kind: ExprKind::NeverToAny { source: expr.to_ref() },
+ };
+ }
Some(&ty::adjustment::AdjustMutToConstPointer) => {
let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
expr = Expr {
let ref_ty = cx.tcx.no_late_bound_regions(&meth_ty.fn_ret());
let (region, mutbl) = match ref_ty {
- Some(ty::FnConverging(&ty::TyS {
+ Some(&ty::TyS {
sty: ty::TyRef(region, mt), ..
- })) => (region, mt.mutbl),
+ }) => (region, mt.mutbl),
_ => span_bug!(expr.span, "autoderef returned bad type")
};
let tables = cx.tcx.tables.borrow();
let callee = &tables.method_map[&method_call];
let ref_ty = callee.ty.fn_ret();
- let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap().unwrap();
- // 1~~~~~ 2~~~~~
- // (1) callees always have all late-bound regions fully instantiated,
- // (2) overloaded methods don't return `!`
+ let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap();
+ // callees always have all late-bound regions fully instantiated,
// construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type
Cast {
source: ExprRef<'tcx>,
},
+ NeverToAny {
+ source: ExprRef<'tcx>,
+ },
ReifyFnPointer {
source: ExprRef<'tcx>,
},
use rustc::mir::repr::*;
use rustc::mir::mir_map::MirMap;
use rustc::mir::transform::MirSource;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::TyCtxt;
use rustc_data_structures::fnv::FnvHashMap;
use rustc_data_structures::indexed_vec::{Idx};
use std::fmt::Display;
write!(w, "{:?}: {}", Lvalue::Arg(i), arg.ty)?;
}
- write!(w, ") -> ")?;
-
- // fn return type.
- match mir.return_ty {
- ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty),
- ty::FnOutput::FnDiverging => write!(w, "!"),
- }
+ write!(w, ") -> {}", mir.return_ty)
} else {
assert!(mir.arg_decls.is_empty());
- write!(w, ": {} =", mir.return_ty.unwrap())
+ write!(w, ": {} =", mir.return_ty)
}
}
use rustc::mir::repr::*;
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::TyCtxt;
use syntax_pos::Span;
use build::Location;
let span = self.promoted.span;
let new_operand = Operand::Constant(Constant {
span: span,
- ty: self.promoted.return_ty.unwrap(),
+ ty: self.promoted.return_ty,
literal: Literal::Promoted {
index: Promoted::new(self.source.promoted.len())
}
parent_scope: None
}).into_iter().collect(),
IndexVec::new(),
- ty::FnConverging(ty),
+ ty,
IndexVec::new(),
IndexVec::new(),
IndexVec::new(),
}
}
- let return_ty = mir.return_ty.unwrap();
+ let return_ty = mir.return_ty;
self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
match self.mode {
// Statics must be Sync.
if mode == Mode::Static {
- let ty = mir.return_ty.unwrap();
+ let ty = mir.return_ty;
tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
let mut fulfillment_cx = traits::FulfillmentContext::new();
}
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
- if let ty::FnConverging(t) = mir.return_ty {
- self.sanitize_type(&"return type", t);
- }
+ self.sanitize_type(&"return type", mir.return_ty);
for var_decl in &mir.var_decls {
self.sanitize_type(var_decl, var_decl.ty);
}
Lvalue::Static(def_id) =>
LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
Lvalue::ReturnPointer => {
- if let ty::FnConverging(return_ty) = self.mir.return_ty {
- LvalueTy::Ty { ty: return_ty }
- } else {
- LvalueTy::Ty {
- ty: span_mirbug_and_err!(
- self, lvalue, "return in diverging function")
- }
- }
+ LvalueTy::Ty { ty: self.mir.return_ty }
}
Lvalue::Projection(ref proj) => {
let base_ty = self.sanitize_lvalue(&proj.base);
sig: &ty::FnSig<'tcx>,
destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
let tcx = self.tcx();
- match (destination, sig.output) {
- (&Some(..), ty::FnDiverging) => {
- span_mirbug!(self, term, "call to diverging function {:?} with dest", sig);
- }
- (&Some((ref dest, _)), ty::FnConverging(ty)) => {
+ match *destination {
+ Some((ref dest, _)) => {
let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
- if let Err(terr) = self.sub_types(self.last_span, ty, dest_ty) {
+ if let Err(terr) = self.sub_types(self.last_span, sig.output, dest_ty) {
span_mirbug!(self, term,
"call dest mismatch ({:?} <- {:?}): {:?}",
- dest_ty, ty, terr);
+ dest_ty, sig.output, terr);
}
- }
- (&None, ty::FnDiverging) => {}
- (&None, ty::FnConverging(..)) => {
- span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
- }
+ },
+ None => {
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ if !sig.output.is_never() {
+ span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
+ }
+ },
}
}
fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
match v.tcx.tables.borrow().adjustments.get(&e.id) {
None |
+ Some(&ty::adjustment::AdjustNeverToAny(..)) |
Some(&ty::adjustment::AdjustReifyFnPointer) |
Some(&ty::adjustment::AdjustUnsafeFnPointer) |
Some(&ty::adjustment::AdjustMutToConstPointer) => {}
let expr_ty = self.tcx.expr_ty(expr);
let def = match expr_ty.sty {
ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
- output: ty::FnConverging(ty), ..
+ output: ty, ..
}), ..}) => ty,
_ => expr_ty
}.ty_adt_def().unwrap();
sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", "));
sig.push(')');
match decl.output {
- ast::FunctionRetTy::None(_) => sig.push_str(" -> !"),
ast::FunctionRetTy::Default(_) => {}
ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
}
}
let t = node_id_type(bcx, discr_expr.id);
- let chk = if t.is_empty(tcx) {
+ let chk = if t.is_uninhabited(tcx) {
Unreachable
} else {
Infallible
}
};
- let ret_ty = match sig.output {
- ty::FnConverging(ret_ty) => ret_ty,
- ty::FnDiverging => ccx.tcx().mk_nil()
- };
+ let ret_ty = sig.output;
let mut ret = arg_of(ret_ty, true);
if !type_is_fat_ptr(ccx.tcx(), ret_ty) {
};
// Fat pointers are returned by-value.
if !self.ret.is_ignore() {
- if !type_is_fat_ptr(ccx.tcx(), sig.output.unwrap()) {
+ if !type_is_fat_ptr(ccx.tcx(), sig.output) {
fixup(&mut self.ret);
}
}
let sig = ccx.tcx().erase_late_bound_regions(&ctor_ty.fn_sig());
let sig = ccx.tcx().normalize_associated_type(&sig);
- let result_ty = sig.output.unwrap();
+ let result_ty = sig.output;
// Get location to store the result. If the user does not care about
// the result, just make a stack slot
if !fcx.fn_ty.ret.is_ignore() {
let dest = fcx.get_ret_slot(bcx, "eret_slot");
let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value
- let repr = adt::represent_type(ccx, sig.output.unwrap());
+ let repr = adt::represent_type(ccx, sig.output);
let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
let mut arg_idx = 0;
for (i, arg_ty) in sig.inputs.into_iter().enumerate() {
let opt_llretslot = dest.and_then(|dest| match dest {
expr::SaveIn(dst) => Some(dst),
expr::Ignore => {
- let needs_drop = || match output {
- ty::FnConverging(ret_ty) => bcx.fcx.type_needs_drop(ret_ty),
- ty::FnDiverging => false
- };
+ let needs_drop = || bcx.fcx.type_needs_drop(output);
if fn_ty.ret.is_indirect() || fn_ty.ret.cast.is_some() || needs_drop() {
// Push the out-pointer if we use an out-pointer for this
// return type, otherwise push "undef".
// If the caller doesn't care about the result of this fn call,
// drop the temporary slot we made.
- match (dest, opt_llretslot, output) {
- (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => {
+ match (dest, opt_llretslot) {
+ (Some(expr::Ignore), Some(llretslot)) => {
// drop the value if it is not being saved.
- bcx = glue::drop_ty(bcx, llretslot, ret_ty, debug_loc);
+ bcx = glue::drop_ty(bcx, llretslot, output, debug_loc);
call_lifetime_end(bcx, llretslot);
}
_ => {}
}
- if output == ty::FnDiverging {
+ // FIXME(canndrew): This is_never should really be an is_uninhabited
+ if output.is_never() {
Unreachable(bcx);
}
trans_closure_expr(Dest::Ignore(ccx),
&hir::FnDecl {
inputs: P::new(),
- output: hir::NoReturn(DUMMY_SP),
+ output: hir::Return(P(hir::Ty {
+ id: DUMMY_NODE_ID,
+ span: DUMMY_SP,
+ node: hir::Ty_::TyNever,
+ })),
variadic: false
},
&hir::Block {
ty::TyRef(..) |
ty::TyFnDef(..) |
ty::TyFnPtr(_) |
+ ty::TyNever |
ty::TyTrait(_) => {
/* nothing to do */
}
abi: Abi::C,
sig: ty::Binder(ty::FnSig {
inputs: vec![tcx.mk_mut_ptr(tcx.types.u8)],
- output: ty::FnDiverging,
+ output: tcx.types.never,
variadic: false
}),
}));
inlined_vid);
let adt_def = match ctor_ty.sty {
ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
- output: ty::FnConverging(ty), ..
- }), ..}) => ty,
+ output, ..
+ }), ..}) => output,
_ => ctor_ty
}.ty_adt_def().unwrap();
let variant_def_id = if ccx.tcx().map.is_inlined_node_id(inlined_vid) {
use value::Value;
use Disr;
use rustc::ty::subst::Substs;
-use rustc::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
+use rustc::ty::adjustment::{AdjustNeverToAny, AdjustDerefRef, AdjustReifyFnPointer};
use rustc::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::cast::{CastTy,IntTy};
&cx.tcx().expr_ty_adjusted(e));
let opt_adj = cx.tcx().tables.borrow().adjustments.get(&e.id).cloned();
match opt_adj {
+ Some(AdjustNeverToAny(..)) => span_bug!(e.span, "const expression of type ! encountered"),
Some(AdjustReifyFnPointer) => {
match ety.sty {
ty::TyFnDef(def_id, substs, _) => {
unique_type_id.push('{');
match type_.sty {
+ ty::TyNever |
ty::TyBool |
ty::TyChar |
ty::TyStr |
}
unique_type_id.push_str(")->");
- match sig.output {
- ty::FnConverging(ret_ty) => {
- let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty);
- let return_type_id = self.get_unique_type_id_as_string(return_type_id);
- unique_type_id.push_str(&return_type_id[..]);
- }
- ty::FnDiverging => {
- unique_type_id.push_str("!");
- }
- }
+ let return_type_id = self.get_unique_type_id_of_type(cx, sig.output);
+ let return_type_id = self.get_unique_type_id_as_string(return_type_id);
+ unique_type_id.push_str(&return_type_id[..]);
},
ty::TyClosure(_, substs) if substs.upvar_tys.is_empty() => {
push_debuginfo_type_name(cx, type_, false, &mut unique_type_id);
let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.inputs.len() + 1);
// return type
- signature_metadata.push(match signature.output {
- ty::FnConverging(ret_ty) => match ret_ty.sty {
- ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(),
- _ => type_metadata(cx, ret_ty, span)
- },
- ty::FnDiverging => diverging_type_metadata(cx)
+ signature_metadata.push(match signature.output.sty {
+ ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(),
+ _ => type_metadata(cx, signature.output, span)
});
// regular arguments
let sty = &t.sty;
let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty {
+ ty::TyNever |
ty::TyBool |
ty::TyChar |
ty::TyInt(_) |
}
}
-pub fn diverging_type_metadata(cx: &CrateContext) -> DIType {
- unsafe {
- llvm::LLVMRustDIBuilderCreateBasicType(
- DIB(cx),
- "!\0".as_ptr() as *const _,
- bytes_to_bits(0),
- bytes_to_bits(0),
- DW_ATE_unsigned)
- }
-}
-
fn basic_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>) -> DIType {
debug!("basic_type_metadata: {:?}", t);
let (name, encoding) = match t.sty {
+ ty::TyNever => ("!", DW_ATE_unsigned),
ty::TyTuple(ref elements) if elements.is_empty() =>
("()", DW_ATE_unsigned),
ty::TyBool => ("bool", DW_ATE_boolean),
use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit};
use self::namespace::mangled_name_of_item;
use self::type_names::compute_debuginfo_type_name;
-use self::metadata::{type_metadata, diverging_type_metadata};
-use self::metadata::{file_metadata, TypeMap};
+use self::metadata::{type_metadata, file_metadata, TypeMap};
use self::source_loc::InternalDebugLocation::{self, UnknownLocation};
use llvm;
let mut signature = Vec::with_capacity(sig.inputs.len() + 1);
// Return type -- llvm::DIBuilder wants this at index 0
- signature.push(match sig.output {
- ty::FnConverging(ret_ty) => match ret_ty.sty {
- ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(),
- _ => type_metadata(cx, ret_ty, syntax_pos::DUMMY_SP)
- },
- ty::FnDiverging => diverging_type_metadata(cx)
+ signature.push(match sig.output.sty {
+ ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(),
+ _ => type_metadata(cx, sig.output, syntax_pos::DUMMY_SP)
});
let inputs = if abi == Abi::RustCall {
ty::TyBool => output.push_str("bool"),
ty::TyChar => output.push_str("char"),
ty::TyStr => output.push_str("str"),
+ ty::TyNever => output.push_str("!"),
ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()),
ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()),
ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()),
output.push(')');
- match sig.output {
- ty::FnConverging(result_type) if result_type.is_nil() => {}
- ty::FnConverging(result_type) => {
- output.push_str(" -> ");
- push_debuginfo_type_name(cx, result_type, true, output);
- }
- ty::FnDiverging => {
- output.push_str(" -> !");
- }
+ if !sig.output.is_nil() {
+ output.push_str(" -> ");
+ push_debuginfo_type_name(cx, sig.output, true, output);
}
},
ty::TyClosure(..) => {
let fty = FnType::new(ccx, abi, &sig, &[]);
let llfn = declare_raw_fn(ccx, name, fty.cconv, fty.llvm_type(ccx));
- if sig.output == ty::FnDiverging {
+ // FIXME(canndrew): This is_never should really be an is_uninhabited
+ if sig.output.is_never() {
llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
}
use type_of;
use value::Value;
use Disr;
-use rustc::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
+use rustc::ty::adjustment::{AdjustNeverToAny, AdjustDerefRef, AdjustReifyFnPointer};
use rustc::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
use rustc::ty::adjustment::CustomCoerceUnsized;
use rustc::ty::{self, Ty, TyCtxt};
}
match adjustment {
+ AdjustNeverToAny(..) => true,
AdjustReifyFnPointer => true,
AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
// purely a type-level thing
debug!("unadjusted datum for expr {:?}: {:?} adjustment={:?}",
expr, datum, adjustment);
match adjustment {
+ AdjustNeverToAny(ref target) => {
+ let mono_target = bcx.monomorphize(target);
+ let llty = type_of::type_of(bcx.ccx(), mono_target);
+ let dummy = C_undef(llty.ptr_to());
+ datum = Datum::new(dummy, mono_target, Lvalue::new("never")).to_expr_datum();
+ }
AdjustReifyFnPointer => {
match datum.ty.sty {
ty::TyFnDef(def_id, substs, _) => {
let ix_datum = unpack_datum!(bcx, trans(bcx, idx));
let ref_ty = // invoked methods have LB regions instantiated:
- bcx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap();
+ bcx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap();
let elt_ty = match ref_ty.builtin_deref(true, ty::NoPreference) {
None => {
span_bug!(index_expr.span,
};
let ref_ty = // invoked methods have their LB regions instantiated
- ccx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap();
+ ccx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap();
let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref");
bcx = Callee::method(bcx, method)
// For `transmute` we can just trans the input expr directly into dest
if name == "transmute" {
- let llret_ty = type_of::type_of(ccx, ret_ty.unwrap());
+ let llret_ty = type_of::type_of(ccx, ret_ty);
match args {
callee::ArgExprs(arg_exprs) => {
assert_eq!(arg_exprs.len(), 1);
return Result::new(bcx, C_nil(ccx));
}
- let ret_ty = match ret_ty {
- ty::FnConverging(ret_ty) => ret_ty,
- ty::FnDiverging => bug!()
- };
-
let llret_ty = type_of::type_of(ccx, ret_ty);
// Get location to store the result. If the user does
fn gen_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
name: &str,
inputs: Vec<Ty<'tcx>>,
- output: ty::FnOutput<'tcx>,
+ output: Ty<'tcx>,
trans: &mut for<'b> FnMut(Block<'b, 'tcx>))
-> ValueRef {
let ccx = fcx.ccx;
abi: Abi::Rust,
sig: ty::Binder(ty::FnSig {
inputs: vec![i8p],
- output: ty::FnOutput::FnConverging(tcx.mk_nil()),
+ output: tcx.mk_nil(),
variadic: false,
}),
}));
- let output = ty::FnOutput::FnConverging(tcx.types.i32);
+ let output = tcx.types.i32;
let rust_try = gen_fn(fcx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans);
ccx.rust_try_fn().set(Some(rust_try));
return rust_try
use rustc::mir::traversal;
use common::{self, Block, BlockAndBuilder};
use glue;
+use std::iter;
use super::rvalue;
pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
let local_types = mir.arg_decls.iter().map(|a| a.ty)
.chain(mir.var_decls.iter().map(|v| v.ty))
.chain(mir.temp_decls.iter().map(|t| t.ty))
- .chain(mir.return_ty.maybe_converging());
+ .chain(iter::once(mir.return_ty));
for (index, ty) in local_types.enumerate() {
let ty = bcx.monomorphize(&ty);
debug!("local {} has type {:?}", index, ty);
// Make a fake operand for store_return
let op = OperandRef {
val: Ref(dst),
- ty: sig.output.unwrap()
+ ty: sig.output,
};
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
}
debug_loc.apply_to_bcx(ret_bcx);
let op = OperandRef {
val: Immediate(invokeret),
- ty: sig.output.unwrap()
+ ty: sig.output,
};
self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
});
if let Some((_, target)) = *destination {
let op = OperandRef {
val: Immediate(llret),
- ty: sig.output.unwrap()
+ ty: sig.output,
};
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
funclet_br(self, bcx, target);
use std::ops::Deref;
use std::rc::Rc;
+use std::iter;
use basic_block::BasicBlock;
let locals = mir.temp_decls.iter().enumerate().map(|(i, decl)| {
(mir::Lvalue::Temp(mir::Temp::new(i)), decl.ty)
- }).chain(mir.return_ty.maybe_converging().map(|ty| (mir::Lvalue::ReturnPointer, ty)));
+ }).chain(iter::once((mir::Lvalue::ReturnPointer, mir.return_ty)));
args.into_iter().chain(vars).chain(locals.map(|(lvalue, ty)| {
let ty = bcx.monomorphize(&ty);
let sig = ty::FnSig {
inputs: vec![tcx.mk_mut_ptr(tcx.types.i8)],
- output: ty::FnOutput::FnConverging(tcx.mk_nil()),
+ output: tcx.mk_nil(),
variadic: false,
};
ty::TyBool => output.push_str("bool"),
ty::TyChar => output.push_str("char"),
ty::TyStr => output.push_str("str"),
+ ty::TyNever => output.push_str("!"),
ty::TyInt(ast::IntTy::Is) => output.push_str("isize"),
ty::TyInt(ast::IntTy::I8) => output.push_str("i8"),
ty::TyInt(ast::IntTy::I16) => output.push_str("i16"),
output.push(')');
- match sig.output {
- ty::FnConverging(result_type) if result_type.is_nil() => {}
- ty::FnConverging(result_type) => {
- output.push_str(" -> ");
- push_unique_type_name(tcx, result_type, output);
- }
- ty::FnDiverging => {
- output.push_str(" -> !");
- }
+ if !sig.output.is_nil() {
+ output.push_str(" -> ");
+ push_unique_type_name(tcx, sig.output, output);
}
},
ty::TyClosure(def_id, ref closure_substs) => {
ty::TyInt(t) => Type::int_from_ty(cx, t),
ty::TyUint(t) => Type::uint_from_ty(cx, t),
ty::TyFloat(t) => Type::float_from_ty(cx, t),
+ ty::TyNever => Type::nil(cx),
ty::TyBox(ty) |
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyInt(t) => Type::int_from_ty(cx, t),
ty::TyUint(t) => Type::uint_from_ty(cx, t),
ty::TyFloat(t) => Type::float_from_ty(cx, t),
+ ty::TyNever => Type::nil(cx),
ty::TyEnum(def, ref substs) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache. This
let t = self.ast_ty_to_ty(rscope1, &mt.ty);
tcx.mk_ref(tcx.mk_region(r), ty::TypeAndMut {ty: t, mutbl: mt.mutbl})
}
+ hir::TyNever => {
+ tcx.types.never
+ },
hir::TyTup(ref fields) => {
let flds = fields.iter()
.map(|t| self.ast_ty_to_ty(rscope, &t))
let output_ty = match decl.output {
hir::Return(ref output) =>
- ty::FnConverging(self.convert_ty_with_lifetime_elision(implied_output_region,
- &output,
- ret_anon_scope)),
- hir::DefaultReturn(..) => ty::FnConverging(self.tcx().mk_nil()),
- hir::NoReturn(..) => ty::FnDiverging
+ self.convert_ty_with_lifetime_elision(implied_output_region,
+ &output,
+ ret_anon_scope),
+ hir::DefaultReturn(..) => self.tcx().mk_nil(),
};
(self.tcx().mk_bare_fn(ty::BareFnTy {
_ if is_infer && expected_ret_ty.is_some() =>
expected_ret_ty.unwrap(),
_ if is_infer =>
- ty::FnConverging(self.ty_infer(None, None, None, decl.output.span())),
+ self.ty_infer(None, None, None, decl.output.span()),
hir::Return(ref output) =>
- ty::FnConverging(self.ast_ty_to_ty(&rb, &output)),
+ self.ast_ty_to_ty(&rb, &output),
hir::DefaultReturn(..) => bug!(),
- hir::NoReturn(..) => ty::FnDiverging
};
debug!("ty_of_closure: input_tys={:?}", input_tys);
let scheme = tcx.lookup_item_type(def.def_id());
let scheme = if scheme.ty.is_fn() {
// Replace constructor type with constructed type for tuple struct patterns.
- let fn_ret = tcx.no_late_bound_regions(&scheme.ty.fn_ret()).unwrap().unwrap();
+ let fn_ret = tcx.no_late_bound_regions(&scheme.ty.fn_ret()).unwrap();
ty::TypeScheme { ty: fn_ret, generics: scheme.generics }
} else {
// Leave the type as is for unit structs (backward compatibility).
// set up all the node type bindings.
error_fn_sig = ty::Binder(ty::FnSig {
inputs: self.err_args(arg_exprs.len()),
- output: ty::FnConverging(self.tcx.types.err),
+ output: self.tcx.types.err,
variadic: false
});
fcx.demand_eqtype(self.call_expr.span, self_arg_ty, method_arg_ty);
}
- let nilty = fcx.tcx.mk_nil();
fcx.demand_eqtype(self.call_expr.span,
- method_sig.output.unwrap_or(nilty),
- self.fn_sig.output.unwrap_or(nilty));
+ method_sig.output,
+ self.fn_sig.output);
fcx.write_overloaded_call_method_map(self.call_expr, method_callee);
}
let fn_sig = ty::FnSig {
inputs: input_tys,
- output: ty::FnConverging(ret_param_ty),
+ output: ret_param_ty,
variadic: false
};
debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
use rustc::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
use rustc::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer};
use rustc::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
+use rustc::ty::adjustment::AdjustNeverToAny;
use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::error::TypeError;
return self.identity(b);
}
+ if a.is_never() {
+ return Ok((b, AdjustNeverToAny(b)));
+ }
+
// Consider coercing the subtype to a DST
let unsize = self.coerce_unsized(a, b);
if unsize.is_ok() {
apply(&mut coerce, &|| Some(expr), source, target)?;
if !adjustment.is_identity() {
debug!("Success, coerced with {:?}", adjustment);
- assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
+ match self.tables.borrow().adjustments.get(&expr.id) {
+ None | Some(&AdjustNeverToAny(..)) => (),
+ _ => bug!("expr already has an adjustment on it!"),
+ };
self.write_adjustment(expr.id, adjustment);
}
Ok(ty)
}
_ => false
},
+ Some(&AdjustNeverToAny(_)) => true,
Some(_) => false,
None => true
};
Ok((ty, adjustment)) => {
if !adjustment.is_identity() {
for expr in exprs() {
- self.write_adjustment(expr.id, adjustment);
+ let previous = self.tables.borrow().adjustments.get(&expr.id).cloned();
+ if let Some(AdjustNeverToAny(_)) = previous {
+ self.write_adjustment(expr.id, AdjustNeverToAny(ty));
+ } else {
+ self.write_adjustment(expr.id, adjustment);
+ }
}
}
Ok(ty)
// We still need to ensure all referenced data is safe.
match ty.sty {
ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
- ty::TyFloat(_) | ty::TyStr => {
+ ty::TyFloat(_) | ty::TyStr | ty::TyNever => {
// primitive - definitely safe
Ok(())
}
it: &hir::ForeignItem,
n_tps: usize,
abi: Abi,
- inputs: Vec<ty::Ty<'tcx>>,
- output: ty::FnOutput<'tcx>) {
+ inputs: Vec<Ty<'tcx>>,
+ output: Ty<'tcx>) {
let tcx = ccx.tcx;
let def_id = tcx.map.local_def_id(it.id);
let i_ty = tcx.lookup_item_type(def_id);
return;
}
};
- (n_tps, inputs, ty::FnConverging(output))
+ (n_tps, inputs, output)
} else if &name[..] == "abort" || &name[..] == "unreachable" {
- (0, Vec::new(), ty::FnDiverging)
+ (0, Vec::new(), tcx.types.never)
} else {
let (n_tps, inputs, output) = match &name[..] {
"breakpoint" => (0, Vec::new(), tcx.mk_nil()),
abi: Abi::Rust,
sig: ty::Binder(FnSig {
inputs: vec![mut_u8],
- output: ty::FnOutput::FnConverging(tcx.mk_nil()),
+ output: tcx.mk_nil(),
variadic: false,
}),
});
return;
}
};
- (n_tps, inputs, ty::FnConverging(output))
+ (n_tps, inputs, output)
};
equate_intrinsic_type(ccx, it, n_tps, Abi::RustIntrinsic, inputs, output)
}
}
match_intrinsic_type_to_type(ccx, "return value", it.span,
&mut structural_to_nomimal,
- &intr.output, sig.output.unwrap());
+ &intr.output, sig.output);
return
}
None => {
};
equate_intrinsic_type(ccx, it, n_tps, Abi::PlatformIntrinsic,
- inputs, ty::FnConverging(output))
+ inputs, output)
}
// walk the expected type and the actual type in lock step, checking they're
// expects the types within the function to be consistent.
err_count_on_creation: usize,
- ret_ty: ty::FnOutput<'tcx>,
+ ret_ty: Ty<'tcx>,
ps: RefCell<UnsafetyState>,
let mut fcx = FnCtxt::new(inherited, fn_sig.output, body.id);
*fcx.ps.borrow_mut() = UnsafetyState::function(unsafety, unsafety_id);
- fn_sig.output = match fcx.ret_ty {
- ty::FnConverging(orig_ret_ty) => {
- fcx.require_type_is_sized(orig_ret_ty, decl.output.span(), traits::ReturnType);
- ty::FnConverging(fcx.instantiate_anon_types(&orig_ret_ty))
- }
- ty::FnDiverging => ty::FnDiverging
- };
- fcx.ret_ty = fn_sig.output;
+ fcx.require_type_is_sized(fcx.ret_ty, decl.output.span(), traits::ReturnType);
+ fcx.ret_ty = fcx.instantiate_anon_types(&fcx.ret_ty);
+ fn_sig.output = fcx.ret_ty;
{
let mut visit = GatherLocalsVisitor { fcx: &fcx, };
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
- fcx.check_block_with_expected(body, match fcx.ret_ty {
- ty::FnConverging(result_type) => ExpectHasType(result_type),
- ty::FnDiverging => NoExpectation
- });
+ fcx.check_block_with_expected(body, ExpectHasType(fcx.ret_ty));
fcx
}
expected_type: Ty<'tcx>,
id: ast::NodeId) {
ccx.inherited(id).enter(|inh| {
- let fcx = FnCtxt::new(&inh, ty::FnConverging(expected_type), expr.id);
+ let fcx = FnCtxt::new(&inh, expected_type, expr.id);
fcx.require_type_is_sized(expected_type, expr.span, traits::ConstSized);
// Gather locals in statics (because of block expressions).
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
- rty: ty::FnOutput<'tcx>,
+ rty: Ty<'tcx>,
body_id: ast::NodeId)
-> FnCtxt<'a, 'gcx, 'tcx> {
FnCtxt {
debug!("write_ty({}, {:?}) in fcx {}",
node_id, ty, self.tag());
self.tables.borrow_mut().node_types.insert(node_id, ty);
+
+ // Add adjustments to !-expressions
+ if ty.is_never() {
+ if let Some(hir::map::NodeExpr(_)) = self.tcx.map.find(node_id) {
+ let adj = adjustment::AdjustNeverToAny(self.next_diverging_ty_var());
+ self.write_adjustment(node_id, adj);
+ }
+ }
}
pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) {
let mut type_scheme = self.tcx.lookup_item_type(did);
if type_scheme.ty.is_fn() {
// Tuple variants have fn type even in type namespace, extract true variant type from it
- let fn_ret = self.tcx.no_late_bound_regions(&type_scheme.ty.fn_ret()).unwrap().unwrap();
+ let fn_ret = self.tcx.no_late_bound_regions(&type_scheme.ty.fn_ret()).unwrap();
type_scheme = ty::TypeScheme { ty: fn_ret, generics: type_scheme.generics }
}
let type_predicates = self.tcx.lookup_predicates(did);
pub fn write_nil(&self, node_id: ast::NodeId) {
self.write_ty(node_id, self.tcx.mk_nil());
}
+
+ pub fn write_never(&self, node_id: ast::NodeId) {
+ self.write_ty(node_id, self.tcx.types.never);
+ }
+
pub fn write_error(&self, node_id: ast::NodeId) {
self.write_ty(node_id, self.tcx.types.err);
}
}
pub fn expr_ty(&self, ex: &hir::Expr) -> Ty<'tcx> {
+ if let Some(&adjustment::AdjustNeverToAny(ref t))
+ = self.tables.borrow().adjustments.get(&ex.id) {
+ return t;
+ }
match self.tables.borrow().node_types.get(&ex.id) {
Some(&t) => t,
None => {
for ty in &self.unsolved_variables() {
let resolved = self.resolve_type_vars_if_possible(ty);
if self.type_var_diverges(resolved) {
- debug!("default_type_parameters: defaulting `{:?}` to `()` because it diverges",
+ debug!("default_type_parameters: defaulting `{:?}` to `!` because it diverges",
resolved);
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(resolved) {
UnconstrainedInt => {
for ty in &unsolved_variables {
let resolved = self.resolve_type_vars_if_possible(ty);
if self.type_var_diverges(resolved) {
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(resolved) {
UnconstrainedInt | UnconstrainedFloat => {
let _ = self.commit_if_ok(|_: &infer::CombinedSnapshot| {
for ty in &unbound_tyvars {
if self.type_var_diverges(ty) {
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(ty) {
UnconstrainedInt => {
// reporting for more then one conflict.
for ty in &unbound_tyvars {
if self.type_var_diverges(ty) {
- self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
+ self.demand_eqtype(syntax_pos::DUMMY_SP, *ty,
+ self.tcx.mk_diverging_default());
} else {
match self.type_is_unconstrained_numeric(ty) {
UnconstrainedInt => {
// extract method return type, which will be &T;
// all LB regions should have been instantiated during method lookup
let ret_ty = method.ty.fn_ret();
- let ret_ty = self.tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap();
+ let ret_ty = self.tcx.no_late_bound_regions(&ret_ty).unwrap();
// method returns &T, but the type as visible to user is T, so deref
ret_ty.builtin_deref(true, NoPreference).unwrap()
args_no_rcvr: &'gcx [P<hir::Expr>],
tuple_arguments: TupleArgumentsFlag,
expected: Expectation<'tcx>)
- -> ty::FnOutput<'tcx> {
+ -> Ty<'tcx> {
if method_fn_ty.references_error() {
let err_inputs = self.err_args(args_no_rcvr.len());
self.check_argument_types(sp, &err_inputs[..], &[], args_no_rcvr,
false, tuple_arguments);
- ty::FnConverging(self.tcx.types.err)
+ self.tcx.types.err
} else {
match method_fn_ty.sty {
ty::TyFnDef(_, _, ref fty) => {
}
if let Some(&arg_ty) = self.tables.borrow().node_types.get(&arg.id) {
- any_diverges = any_diverges || self.type_var_diverges(arg_ty);
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ any_diverges = any_diverges ||
+ self.type_var_diverges(arg_ty) ||
+ arg_ty.is_never();
}
}
if any_diverges && !warned {
fn write_call(&self,
call_expr: &hir::Expr,
- output: ty::FnOutput<'tcx>) {
- self.write_ty(call_expr.id, match output {
- ty::FnConverging(output_ty) => output_ty,
- ty::FnDiverging => self.next_diverging_ty_var()
- });
+ output: Ty<'tcx>) {
+ self.write_ty(call_expr.id, output);
}
// AST fragment checking
fn expected_types_for_fn_args(&self,
call_span: Span,
expected_ret: Expectation<'tcx>,
- formal_ret: ty::FnOutput<'tcx>,
+ formal_ret: Ty<'tcx>,
formal_args: &[Ty<'tcx>])
-> Vec<Ty<'tcx>> {
let expected_args = expected_ret.only_has_type(self).and_then(|ret_ty| {
- if let ty::FnConverging(formal_ret_ty) = formal_ret {
- self.commit_regions_if_ok(|| {
- // Attempt to apply a subtyping relationship between the formal
- // return type (likely containing type variables if the function
- // is polymorphic) and the expected return type.
- // No argument expectations are produced if unification fails.
- let origin = TypeOrigin::Misc(call_span);
- let ures = self.sub_types(false, origin, formal_ret_ty, ret_ty);
- // FIXME(#15760) can't use try! here, FromError doesn't default
- // to identity so the resulting type is not constrained.
- match ures {
- // FIXME(#32730) propagate obligations
- Ok(InferOk { obligations, .. }) => assert!(obligations.is_empty()),
- Err(e) => return Err(e),
- }
+ self.commit_regions_if_ok(|| {
+ // Attempt to apply a subtyping relationship between the formal
+ // return type (likely containing type variables if the function
+ // is polymorphic) and the expected return type.
+ // No argument expectations are produced if unification fails.
+ let origin = TypeOrigin::Misc(call_span);
+ let ures = self.sub_types(false, origin, formal_ret, ret_ty);
+ // FIXME(#15760) can't use try! here, FromError doesn't default
+ // to identity so the resulting type is not constrained.
+ match ures {
+ // FIXME(#32730) propagate obligations
+ Ok(InferOk { obligations, .. }) => assert!(obligations.is_empty()),
+ Err(e) => return Err(e),
+ }
- // Record all the argument types, with the substitutions
- // produced from the above subtyping unification.
- Ok(formal_args.iter().map(|ty| {
- self.resolve_type_vars_if_possible(ty)
- }).collect())
- }).ok()
- } else {
- None
- }
+ // Record all the argument types, with the substitutions
+ // produced from the above subtyping unification.
+ Ok(formal_args.iter().map(|ty| {
+ self.resolve_type_vars_if_possible(ty)
+ }).collect())
+ }).ok()
}).unwrap_or(vec![]);
debug!("expected_types_for_fn_args(formal={:?} -> {:?}, expected={:?} -> {:?})",
formal_args, formal_ret,
}
self.write_nil(id);
}
- hir::ExprBreak(_) => { self.write_ty(id, self.next_diverging_ty_var()); }
- hir::ExprAgain(_) => { self.write_ty(id, self.next_diverging_ty_var()); }
+ hir::ExprBreak(_) => { self.write_never(id); }
+ hir::ExprAgain(_) => { self.write_never(id); }
hir::ExprRet(ref expr_opt) => {
- match self.ret_ty {
- ty::FnConverging(result_type) => {
- if let Some(ref e) = *expr_opt {
- self.check_expr_coercable_to_type(&e, result_type);
- } else {
- let eq_result = self.eq_types(false,
- TypeOrigin::Misc(expr.span),
- result_type,
- tcx.mk_nil())
- // FIXME(#32730) propagate obligations
- .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()));
- if eq_result.is_err() {
- struct_span_err!(tcx.sess, expr.span, E0069,
- "`return;` in a function whose return type is not `()`")
- .span_label(expr.span, &format!("return type is not ()"))
- .emit();
- }
- }
- }
- ty::FnDiverging => {
- if let Some(ref e) = *expr_opt {
- self.check_expr(&e);
- }
- struct_span_err!(tcx.sess, expr.span, E0166,
- "`return` in a function declared as diverging")
- .span_label(expr.span, &format!("diverging function cannot return"))
+ if let Some(ref e) = *expr_opt {
+ self.check_expr_coercable_to_type(&e, self.ret_ty);
+ } else {
+ let eq_result = self.eq_types(false,
+ TypeOrigin::Misc(expr.span),
+ self.ret_ty,
+ tcx.mk_nil())
+ // FIXME(#32730) propagate obligations
+ .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()));
+ if eq_result.is_err() {
+ struct_span_err!(tcx.sess, expr.span, E0069,
+ "`return;` in a function whose return type is not `()`")
+ .span_label(expr.span, &format!("return type is not ()"))
.emit();
}
}
- self.write_ty(id, self.next_diverging_ty_var());
+ self.write_never(id);
}
hir::ExprAssign(ref lhs, ref rhs) => {
self.check_expr_with_lvalue_pref(&lhs, PreferMutLvalue);
hir::ExprLoop(ref body, _) => {
self.check_block_no_value(&body);
if !may_break(tcx, expr.id, &body) {
- self.write_ty(id, self.next_diverging_ty_var());
+ self.write_never(id);
} else {
self.write_nil(id);
}
"unreachable statement".to_string());
warned = true;
}
- any_diverges = any_diverges || self.type_var_diverges(s_ty);
+ // FIXME(canndrew): This is_never should probably be an is_uninhabited
+ any_diverges = any_diverges ||
+ self.type_var_diverges(s_ty) ||
+ s_ty.is_never();
any_err = any_err || s_ty.references_error();
}
match blk.expr {
// extract return type for method; all late bound regions
// should have been instantiated by now
let ret_ty = method_ty.fn_ret();
- Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap())
+ Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap())
}
None => {
Err(())
let fn_sig_tys: Vec<_> =
fn_sig.inputs.iter()
.cloned()
- .chain(Some(fn_sig.output.unwrap_or(self.tcx.types.bool)))
+ .chain(Some(fn_sig.output))
.collect();
let old_body_id = self.set_body_id(body.id);
None::<hir::Expr>.iter(), true);
// late-bound regions in overloaded method calls are instantiated
let fn_ret = self.tcx.no_late_bound_regions(&method.ty.fn_ret());
- fn_ret.unwrap().unwrap()
+ fn_ret.unwrap()
}
None => self.resolve_node_type(base.id)
};
// Specialized version of constrain_call.
self.type_must_outlive(infer::CallRcvr(deref_expr.span),
self_ty, r_deref_expr);
- match fn_sig.output {
- ty::FnConverging(return_type) => {
- self.type_must_outlive(infer::CallReturn(deref_expr.span),
- return_type, r_deref_expr);
- return_type
- }
- ty::FnDiverging => bug!()
- }
+ self.type_must_outlive(infer::CallReturn(deref_expr.span),
+ fn_sig.output, r_deref_expr);
+ fn_sig.output
}
None => derefd_ty
};
let id = self.id;
let span = self.span;
self.inherited.enter(|inh| {
- let fcx = FnCtxt::new(&inh, ty::FnDiverging, id);
+ let fcx = FnCtxt::new(&inh, inh.ccx.tcx.types.never, id);
let wf_tys = f(&fcx, &mut CheckTypeWellFormedVisitor {
ccx: fcx.ccx,
code: code
}
implied_bounds.extend(sig.inputs);
- match sig.output {
- ty::FnConverging(output) => {
- fcx.register_wf_obligation(output, span, self.code.clone());
+ fcx.register_wf_obligation(sig.output, span, self.code.clone());
- // FIXME(#25759) return types should not be implied bounds
- implied_bounds.push(output);
- }
- ty::FnDiverging => { }
- }
+ // FIXME(#25759) return types should not be implied bounds
+ implied_bounds.push(sig.output);
self.check_where_clauses(fcx, span, predicates);
}
Some(adjustment) => {
let resolved_adjustment = match adjustment {
+ adjustment::AdjustNeverToAny(ty) => {
+ adjustment::AdjustNeverToAny(self.resolve(&ty, reason))
+ }
+
adjustment::AdjustReifyFnPointer => {
adjustment::AdjustReifyFnPointer
}
use rustc::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment};
use rustc::ty::{Ty, TyBool, TyChar, TyEnum, TyError};
use rustc::ty::{TyParam, TyRawPtr};
-use rustc::ty::{TyRef, TyStruct, TyTrait, TyTuple};
+use rustc::ty::{TyRef, TyStruct, TyTrait, TyNever, TyTuple};
use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt};
use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr};
use rustc::ty::{TyProjection, TyAnon};
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
TyStr | TyArray(..) | TySlice(..) | TyFnDef(..) | TyFnPtr(_) |
- TyTuple(..) | TyParam(..) | TyError |
+ TyTuple(..) | TyParam(..) | TyError | TyNever |
TyRawPtr(_) | TyRef(_, _) | TyProjection(..) => {
None
}
abi: abi::Abi::Rust,
sig: ty::Binder(ty::FnSig {
inputs: inputs,
- output: ty::FnConverging(scheme.ty),
+ output: scheme.ty,
variadic: false
})
}))
let output = match decl.output {
hir::Return(ref ty) =>
- ty::FnConverging(AstConv::ast_ty_to_ty(&ccx.icx(ast_generics), &rb, &ty)),
+ AstConv::ast_ty_to_ty(&ccx.icx(ast_generics), &rb, &ty),
hir::DefaultReturn(..) =>
- ty::FnConverging(ccx.tcx.mk_nil()),
- hir::NoReturn(..) =>
- ty::FnDiverging
+ ccx.tcx.mk_nil(),
};
// feature gate SIMD types in FFI, since I (huonw) am not sure the
check(&input.ty, ty)
}
if let hir::Return(ref ty) = decl.output {
- check(&ty, output.unwrap())
+ check(&ty, output)
}
}
```
"##,
-E0166: r##"
-This error means that the compiler found a return expression in a function
-marked as diverging. A function diverges if it has `!` in the place of the
-return type in its signature. For example:
-
-```compile_fail,E0166
-fn foo() -> ! { return; } // error
-```
-
-For a function that diverges, every control path in the function must never
-return, for example with a `loop` that never breaks or a call to another
-diverging function (such as `panic!()`).
-"##,
-
E0172: r##"
This error means that an attempt was made to specify the type of a variable with
a combination of a concrete type and a trait. Consider the following example:
abi: Abi::Rust,
sig: ty::Binder(ty::FnSig {
inputs: Vec::new(),
- output: ty::FnConverging(tcx.mk_nil()),
+ output: tcx.mk_nil(),
variadic: false
})
}));
tcx.types.isize,
tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))
),
- output: ty::FnConverging(tcx.types.isize),
+ output: tcx.types.isize,
variadic: false,
}),
}));
match ty.sty {
ty::TyBool |
ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
- ty::TyFloat(_) | ty::TyStr => {
+ ty::TyFloat(_) | ty::TyStr | ty::TyNever => {
/* leaf type -- noop */
}
for &input in &sig.0.inputs {
self.add_constraints_from_ty(generics, input, contra);
}
- if let ty::FnConverging(result_type) = sig.0.output {
- self.add_constraints_from_ty(generics, result_type, variance);
- }
+ self.add_constraints_from_ty(generics, sig.0.output, variance);
}
/// Adds constraints appropriate for a region appearing in a
}
}
-impl<'tcx> Clean<Type> for ty::FnOutput<'tcx> {
- fn clean(&self, cx: &DocContext) -> Type {
- match *self {
- ty::FnConverging(ty) => ty.clean(cx),
- ty::FnDiverging => Bottom
- }
- }
-}
-
impl<'a, 'tcx> Clean<FnDecl> for (DefId, &'a ty::PolyFnSig<'tcx>) {
fn clean(&self, cx: &DocContext) -> FnDecl {
let (did, sig) = *self;
pub enum FunctionRetTy {
Return(Type),
DefaultReturn,
- NoReturn
}
impl Clean<FunctionRetTy> for hir::FunctionRetTy {
match *self {
hir::Return(ref typ) => Return(typ.clean(cx)),
hir::DefaultReturn(..) => DefaultReturn,
- hir::NoReturn(..) => NoReturn
}
}
}
Tuple(Vec<Type>),
Vector(Box<Type>),
FixedVector(Box<Type>, String),
- /// aka TyBot
- Bottom,
+ Never,
Unique(Box<Type>),
RawPointer(Mutability, Box<Type>),
BorrowedRef {
fn clean(&self, cx: &DocContext) -> Type {
use rustc::hir::*;
match self.node {
+ TyNever => Never,
TyPtr(ref m) => RawPointer(m.mutbl.clean(cx), box m.ty.clean(cx)),
TyRptr(ref l, ref m) =>
BorrowedRef {lifetime: l.clean(cx), mutability: m.mutbl.clean(cx),
impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
fn clean(&self, cx: &DocContext) -> Type {
match self.sty {
+ ty::TyNever => Never,
ty::TyBool => Primitive(Bool),
ty::TyChar => Primitive(Char),
ty::TyInt(ast::IntTy::Is) => Primitive(Isize),
primitive_link(f, clean::PrimitiveType::Array,
&format!("; {}]", Escape(s)))
}
- clean::Bottom => f.write_str("!"),
+ clean::Never => f.write_str("!"),
clean::RawPointer(m, ref t) => {
match **t {
clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
clean::Return(ref ty) => write!(f, " -> {}", ty),
clean::DefaultReturn => Ok(()),
- clean::NoReturn => write!(f, " -> !")
}
}
}
/// The different kinds of types recognized by the compiler
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum TyKind {
+ /// A variable-length array (`[T]`)
Vec(P<Ty>),
/// A fixed length array (`[T; n]`)
FixedLengthVec(P<Ty>, P<Expr>),
Rptr(Option<Lifetime>, MutTy),
/// A bare function (e.g. `fn(usize) -> bool`)
BareFn(P<BareFnTy>),
+ /// The never type (`!`)
+ Never,
/// A tuple (`(A, B, C, D,...)`)
Tup(Vec<P<Ty>> ),
/// A path (`module::module::...::Type`), optionally
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum FunctionRetTy {
- /// Functions with return type `!`that always
- /// raise an error or exit (i.e. never return to the caller)
- None(Span),
/// Return type is not specified.
///
/// Functions default to `()` and
impl FunctionRetTy {
pub fn span(&self) -> Span {
match *self {
- FunctionRetTy::None(span) => span,
FunctionRetTy::Default(span) => span,
FunctionRetTy::Ty(ref ty) => ty.span,
}
// Allows tuple structs and variants in more contexts,
// Permits numeric fields in struct expressions and patterns.
- (active, relaxed_adts, "1.12.0", Some(35626))
+ (active, relaxed_adts, "1.12.0", Some(35626)),
+
+ // The `!` type
+ (active, never_type, "1.13.0", Some(35121))
);
declare_features! (
gate_feature_post!(&self, conservative_impl_trait, ty.span,
"`impl Trait` is experimental");
}
+ ast::TyKind::Never => {
+ gate_feature_post!(&self, never_type, ty.span,
+ "The `!` type is experimental");
+ },
_ => {}
}
visit::walk_ty(self, ty)
}
+ fn visit_fn_ret_ty(&mut self, ret_ty: &ast::FunctionRetTy) {
+ if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
+ match output_ty.node {
+ ast::TyKind::Never => return,
+ _ => (),
+ };
+ self.visit_ty(output_ty)
+ }
+ }
+
fn visit_expr(&mut self, e: &ast::Expr) {
match e.node {
ast::ExprKind::Box(_) => {
decl: fld.fold_fn_decl(decl)
}))
}
+ TyKind::Never => node,
TyKind::Tup(tys) => TyKind::Tup(tys.move_map(|ty| fld.fold_ty(ty))),
TyKind::Paren(ty) => TyKind::Paren(fld.fold_ty(ty)),
TyKind::Path(qself, path) => {
output: match output {
FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(fld.fold_ty(ty)),
FunctionRetTy::Default(span) => FunctionRetTy::Default(span),
- FunctionRetTy::None(span) => FunctionRetTy::None(span),
},
variadic: variadic
})
/// Parse optional return type [ -> TY ] in function decl
pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> {
if self.eat(&token::RArrow) {
- if self.eat(&token::Not) {
- Ok(FunctionRetTy::None(self.last_span))
- } else {
- Ok(FunctionRetTy::Ty(self.parse_ty()?))
- }
+ Ok(FunctionRetTy::Ty(self.parse_ty()?))
} else {
let pos = self.span.lo;
Ok(FunctionRetTy::Default(mk_sp(pos, pos)))
} else {
TyKind::Tup(ts)
}
+ } else if self.eat(&token::Not) {
+ TyKind::Never
} else if self.check(&token::BinOp(token::Star)) {
// STAR POINTER (bare pointer?)
self.bump();
try!(self.print_opt_lifetime(lifetime));
try!(self.print_mt(mt));
}
+ ast::TyKind::Never => {
+ try!(word(&mut self.s, "!"));
+ },
ast::TyKind::Tup(ref elts) => {
try!(self.popen());
try!(self.commasep(Inconsistent, &elts[..],
self.maybe_print_comment(ty.span.lo)
}
ast::FunctionRetTy::Default(..) => unreachable!(),
- ast::FunctionRetTy::None(span) => {
- try!(self.word_nbsp("!"));
- self.maybe_print_comment(span.lo)
- }
}
}
try!(self.ibox(INDENT_UNIT));
try!(self.word_space("->"));
match decl.output {
- ast::FunctionRetTy::None(_) =>
- try!(self.word_nbsp("!")),
ast::FunctionRetTy::Default(..) => unreachable!(),
ast::FunctionRetTy::Ty(ref ty) =>
try!(self.print_type(&ty))
fn visit_vis(&mut self, vis: &Visibility) {
walk_vis(self, vis)
}
+ fn visit_fn_ret_ty(&mut self, ret_ty: &FunctionRetTy) {
+ walk_fn_ret_ty(self, ret_ty)
+ }
}
#[macro_export]
walk_list!(visitor, visit_lifetime, opt_lifetime);
visitor.visit_ty(&mutable_type.ty)
}
+ TyKind::Never => {},
TyKind::Tup(ref tuple_element_types) => {
walk_list!(visitor, visit_ty, tuple_element_types);
}
visitor.visit_pat(&argument.pat);
visitor.visit_ty(&argument.ty)
}
- walk_fn_ret_ty(visitor, &function_declaration.output)
+ visitor.visit_fn_ret_ty(&function_declaration.output)
}
pub fn walk_fn_kind<V: Visitor>(visitor: &mut V, function_kind: FnKind) {
+++ /dev/null
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn foo() -> ! { return; }
- //~^ ERROR E0166
- //~| NOTE diverging function cannot return
-
-fn main() {
-}
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Tests that a function with a ! annotation always actually fails
-
-fn bad_bang(i: usize) -> ! {
- return 7;
- //~^ ERROR `return` in a function declared as diverging [E0166]
- //~| NOTE diverging function cannot return
-}
-
-fn main() { bad_bang(5); }
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Tests that a function with a ! annotation always actually fails
-
-fn bad_bang(i: usize) -> ! { //~ ERROR computation may converge in a function marked as diverging
- if i < 0 { } else { panic!(); }
-}
-
-fn main() { bad_bang(5); }
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn f() -> ! { //~ ERROR computation may converge in a function marked as diverging
- 3
-}
-fn main() { }
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can't pass other types for !
+
+#![feature(never_type)]
+
+fn foo(x: !) -> ! {
+ x
+}
+
+fn main() {
+ foo("wow"); //~ ERROR mismatched types
+}
+
+++ /dev/null
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn f<F, R>(_: F) where F: Fn() -> R {
-}
-
-fn main() {
- f(|| -> ! { () });
-//~^ ERROR: computation may converge in a function marked as diverging [E0270]
-}
+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![deny(unreachable_code)]
-
-fn g() -> ! { panic!(); }
-fn f() -> ! {
- return g(); //~ ERROR `return` in a function declared as diverging
- g();
-}
-fn h() -> ! {
- loop {}
- g();
-}
-
-fn main() { f() }
+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![deny(unreachable_code)]
-
-fn f() -> ! {
- return panic!(); //~ ERROR `return` in a function declared as diverging
- panic!(); // the unreachable statement error is in <std macro>, at this line, there
- // only is a note
-}
-
-fn main() { f() }
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Tests that a function with a ! annotation always actually fails
-
-fn bad_bang(i: usize) -> ! { //~ ERROR computation may converge in a function marked as diverging
- println!("{}", 3);
-}
-
-fn main() { bad_bang(5); }
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-/* Make sure a loop{} with a break in it can't be
- the tailexpr in the body of a diverging function */
-fn forever() -> ! {
- loop {
- break;
- }
- return 42; //~ ERROR `return` in a function declared as diverging
-}
-
-fn main() {
-}
+++ /dev/null
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn forever2() -> ! { //~ ERROR computation may converge in a function marked as diverging
- loop { break }
-}
-
-fn main() {}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that an assignment of type ! makes the rest of the block dead code.
+
+#![feature(never_type)]
+#![deny(unused, unreachable_code)]
+
+fn main() {
+ let x: ! = panic!("aah"); //~ ERROR unused
+ drop(x); //~ ERROR unreachable
+}
+
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can't use another type in place of !
+
+#![feature(never_type)]
+
+fn main() {
+ let x: ! = "hello"; //~ ERROR mismatched types
+}
+
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that ! errors when used in illegal positions with feature(never_type) disabled
+
+trait Foo {
+ type Wub;
+}
+
+type Ma = (u32, !, i32); //~ ERROR type is experimental
+type Meeshka = Vec<!>; //~ ERROR type is experimental
+type Mow = &fn(!) -> !; //~ ERROR type is experimental
+type Skwoz = &mut !; //~ ERROR type is experimental
+
+impl Foo for Meeshka {
+ type Wub = !; //~ ERROR type is experimental
+}
+
+fn main() {
+}
+
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that diverging types default to ! when feature(never_type) is enabled. This test is the
+// same as run-pass/unit-fallback.rs except that ! is enabled.
+
+#![feature(never_type)]
+
+trait Balls: Sized {
+ fn smeg() -> Result<Self, ()>;
+}
+
+impl Balls for () {
+ fn smeg() -> Result<(), ()> { Ok(()) }
+}
+
+struct Flah;
+
+impl Flah {
+ fn flah<T: Balls>(&self) -> Result<T, ()> {
+ T::smeg()
+ }
+}
+
+fn doit() -> Result<(), ()> {
+ // The type of _ is unconstrained here and should default to !
+ let _ = try!(Flah.flah()); //~ ERROR the trait bound
+ Ok(())
+}
+
+fn main() {
+ let _ = doit();
+}
+
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that return another type in place of ! raises a type mismatch.
+
+fn fail() -> ! {
+ return "wow"; //~ ERROR mismatched types
+}
+
+fn main() {
+}
+
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we get the usual error that we'd get for any other return type and not something about
+// diverging functions not being able to return.
+
+fn fail() -> ! {
+ return; //~ ERROR in a function whose return type is not
+}
+
+fn main() {
+}
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that a variable of type ! can coerce to another type.
+
+#![feature(never_type)]
+
+// error-pattern:explicit
+fn main() {
+ let x: ! = panic!();
+ let y: u32 = x;
+}
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can use a ! for an argument of type !
+
+// error-pattern:wowzers!
+
+#![feature(never_type)]
+#![allow(unreachable_code)]
+
+fn foo(x: !) -> ! {
+ x
+}
+
+fn main() {
+ foo(panic!("wowzers!"))
+}
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can explicitly cast ! to another type
+
+#![feature(never_type)]
+
+// error-pattern:explicit
+fn main() {
+ let x: ! = panic!();
+ let y: u32 = x as u32;
+}
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can use ! as an associated type.
+
+#![feature(never_type)]
+
+// error-pattern:kapow!
+
+trait Foo {
+ type Wow;
+
+ fn smeg(&self) -> Self::Wow;
+}
+
+struct Blah;
+impl Foo for Blah {
+ type Wow = !;
+ fn smeg(&self) -> ! {
+ panic!("kapow!");
+ }
+}
+
+fn main() {
+ Blah.smeg();
+}
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can use ! as an argument to a trait impl.
+
+// error-pattern:oh no!
+
+#![feature(never_type)]
+
+struct Wub;
+
+impl PartialEq<!> for Wub {
+ fn eq(&self, other: &!) -> bool {
+ *other
+ }
+}
+
+fn main() {
+ let _ = Wub == panic!("oh no!");
+}
+
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that ! coerces to other types.
+
+// error-pattern:aah!
+
+fn call_another_fn<T, F: FnOnce() -> T>(f: F) -> T {
+ f()
+}
+
+fn wub() -> ! {
+ panic!("aah!");
+}
+
+fn main() {
+ let x: i32 = call_another_fn(wub);
+ let y: u32 = wub();
+}
+
+
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can call static methods on ! both directly and when it appears in a generic
+
+#![feature(never_type)]
+
+trait StringifyType {
+ fn stringify_type() -> &'static str;
+}
+
+impl StringifyType for ! {
+ fn stringify_type() -> &'static str {
+ "!"
+ }
+}
+
+fn maybe_stringify<T: StringifyType>(opt: Option<T>) -> &'static str {
+ match opt {
+ Some(_) => T::stringify_type(),
+ None => "none",
+ }
+}
+
+fn main() {
+ println!("! is {}", <!>::stringify_type());
+ println!("None is {}", maybe_stringify(None::<!>));
+}
+
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// pretty-expanded FIXME #23616
-
-enum v {}
-pub fn main() {
- let y: v = unsafe { ::std::mem::uninitialized() };
-}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we can extract a ! through pattern matching then use it as several different types.
+
+#![feature(never_type)]
+
+fn main() {
+ let x: Result<u32, !> = Ok(123);
+ match x {
+ Ok(z) => (),
+ Err(y) => {
+ let q: u32 = y;
+ let w: i32 = y;
+ let e: String = y;
+ y
+ },
+ }
+}
+
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that having something of type ! doesn't screw up type-checking and that it coerces to the
+// LUB type of the other match arms.
+
+fn main() {
+ let v: Vec<u32> = Vec::new();
+ match 0u32 {
+ 0 => &v,
+ 1 => return,
+ _ => &v[..],
+ };
+}
+
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that diverging types default to () (with feature(never_type) disabled).
+
+trait Balls: Sized {
+ fn smeg() -> Result<Self, ()>;
+}
+
+impl Balls for () {
+ fn smeg() -> Result<(), ()> { Ok(()) }
+}
+
+struct Flah;
+
+impl Flah {
+ fn flah<T: Balls>(&self) -> Result<T, ()> {
+ T::smeg()
+ }
+}
+
+fn doit() -> Result<(), ()> {
+ // The type of _ is unconstrained here and should default to ()
+ let _ = try!(Flah.flah());
+ Ok(())
+}
+
+fn main() {
+ let _ = doit();
+}
+