Skip to content

Commit

Permalink
Auto merge of #35162 - canndrew:bang_type_coerced, r=nikomatsakis
Browse files Browse the repository at this point in the history
Implement the `!` type

This implements the never type (`!`) and hides it behind the feature gate `#[feature(never_type)]`. With the feature gate off, things should build as normal (although some error messages may be different). With the gate on, `!` is usable as a type and diverging type variables (ie. types that are unconstrained by anything in the code) will default to `!` instead of `()`.
  • Loading branch information
bors committed Aug 16, 2016
2 parents 197be89 + f59f1f0 commit e25542c
Show file tree
Hide file tree
Showing 128 changed files with 899 additions and 705 deletions.
33 changes: 33 additions & 0 deletions src/libcore/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,39 @@ mod impls {

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")]
Expand Down
23 changes: 23 additions & 0 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,29 @@ macro_rules! fmt_refs {

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 {
Expand Down
3 changes: 3 additions & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
#![feature(unboxed_closures)]
#![feature(question_mark)]

// NOTE: remove the cfg_attr next snapshot
#![cfg_attr(not(stage0), feature(never_type))]

#[macro_use]
mod macros;

Expand Down
3 changes: 2 additions & 1 deletion src/librustc/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {

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
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/hir/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
}
}))
}
TyNever => node,
TyTup(tys) => TyTup(tys.move_map(|ty| fld.fold_ty(ty))),
TyPath(qself, path) => {
let qself = qself.map(|QSelf { ty, position }| {
Expand Down Expand Up @@ -515,7 +516,6 @@ pub fn noop_fold_fn_decl<T: Folder>(decl: P<FnDecl>, fld: &mut T) -> P<FnDecl> {
output: match output {
Return(ty) => Return(fld.fold_ty(ty)),
DefaultReturn(span) => DefaultReturn(span),
NoReturn(span) => NoReturn(span),
},
variadic: variadic,
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ impl<'a> LoweringContext<'a> {
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);
Expand Down Expand Up @@ -402,7 +403,6 @@ impl<'a> LoweringContext<'a> {
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,
})
Expand Down
7 changes: 3 additions & 4 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,7 @@ pub struct BareFnTy {
#[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>),
Expand All @@ -1121,6 +1122,8 @@ pub enum Ty_ {
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
Expand Down Expand Up @@ -1283,9 +1286,6 @@ impl fmt::Debug for ImplPolarity {

#[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
Expand All @@ -1299,7 +1299,6 @@ pub enum FunctionRetTy {
impl FunctionRetTy {
pub fn span(&self) -> Span {
match *self {
NoReturn(span) => span,
DefaultReturn(span) => span,
Return(ref ty) => ty.span,
}
Expand Down
8 changes: 3 additions & 5 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,9 @@ impl<'a> State<'a> {
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))?;
Expand Down Expand Up @@ -1959,10 +1962,6 @@ impl<'a> State<'a> {
self.maybe_print_comment(ty.span.lo)
}
hir::DefaultReturn(..) => unreachable!(),
hir::NoReturn(span) => {
self.word_nbsp("!")?;
self.maybe_print_comment(span.lo)
}
}
}

Expand Down Expand Up @@ -2195,7 +2194,6 @@ impl<'a> State<'a> {
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)?,
}
Expand Down
1 change: 0 additions & 1 deletion src/librustc/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,6 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
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)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc/infer/freshen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
ty::TyTrait(..) |
ty::TyStruct(..) |
ty::TyClosure(..) |
ty::TyNever |
ty::TyTuple(..) |
ty::TyProjection(..) |
ty::TyParam(..) |
Expand Down
1 change: 0 additions & 1 deletion src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,6 @@ impl_trans_normalize!('gcx,
Ty<'gcx>,
&'gcx Substs<'gcx>,
ty::FnSig<'gcx>,
ty::FnOutput<'gcx>,
&'gcx ty::BareFnTy<'gcx>,
ty::ClosureSubsts<'gcx>,
ty::PolyTraitRef<'gcx>
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
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 => {
Expand Down
7 changes: 3 additions & 4 deletions src/librustc/middle/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,9 @@ impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for ExprVisitor<'a, 'gcx, 'tcx> {
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?!");
Expand Down
89 changes: 40 additions & 49 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ use self::VarKind::*;
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;
Expand Down Expand Up @@ -1111,8 +1111,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}

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 {
Expand All @@ -1125,7 +1126,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
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
Expand Down Expand Up @@ -1454,7 +1456,7 @@ fn check_fn(_v: &Liveness,
}

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) =>
Expand All @@ -1477,55 +1479,44 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
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, &param_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, &param_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");
}

_ => {}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
self.cat_expr_autoderefd(expr, autoderefs)
}

adjustment::AdjustNeverToAny(..) |
adjustment::AdjustReifyFnPointer |
adjustment::AdjustUnsafeFnPointer |
adjustment::AdjustMutToConstPointer |
Expand Down Expand Up @@ -922,7 +923,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
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
Expand Down Expand Up @@ -1244,7 +1245,6 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// to skip past the binder.
self.tcx().no_late_bound_regions(&method_ty.fn_ret())
.unwrap()
.unwrap() // overloaded ops do not diverge, either
}
}

Expand Down
Loading

0 comments on commit e25542c

Please sign in to comment.