Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement the ! type #35162

Merged
merged 39 commits into from
Aug 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ba7330c
Start implementation of RFC 1216 (make ! a type)
canndrew May 9, 2016
b0a9acd
Parse `!` as TyEmpty (except in fn return type)
canndrew Jun 22, 2016
0d86361
Add EmptyToAny adjustment
canndrew Jun 25, 2016
c88c54e
Invoke coercions on !
canndrew Jun 25, 2016
798f719
Add run-fail/adjust_empty.rs test
canndrew Jun 25, 2016
533a389
Small optimization
canndrew Jun 27, 2016
9f9f856
Fix rustdoc after rebase
canndrew Jun 29, 2016
104963c
Switch on TyEmpty
canndrew Jul 22, 2016
f31d975
Fix super_relate_tys so that ! == !
canndrew Jul 29, 2016
0829152
Make unused lint ignore unused `!`
canndrew Jul 29, 2016
ee78f37
impl Debug + Display for !
canndrew Jul 30, 2016
ed02344
Remove obsolete divergence related stuff
canndrew Jul 31, 2016
b22beed
Fix build after rebase to lastest master
canndrew Aug 1, 2016
51c6ae2
implement std::cmp::* traits for `!`
canndrew Aug 1, 2016
ba65d2e
Remove invalid compile-fail tests related to `!`
canndrew Aug 1, 2016
0e1c2aa
Make AdjustEmptyToAny actually perform the adjustment
canndrew Aug 1, 2016
5096a8c
Control usage of `!` through a feature gate.
canndrew Aug 1, 2016
f0a8b6d
Minor fixups based on @eddyb's feedback
canndrew Aug 2, 2016
fadabe0
Rename empty/bang to never
canndrew Aug 2, 2016
8010314
Un-improve Ty::is_uninabited
canndrew Aug 2, 2016
54c72d8
Minor fix
canndrew Aug 2, 2016
3639341
Default diverging types based on feature gate.
canndrew Aug 2, 2016
a6d6fff
Lookup node type in map rather than using write_ty_expr
canndrew Aug 2, 2016
69e2761
Minor fixup.
canndrew Aug 2, 2016
f019a92
Correctly handle AdjustNeverToAny in try_find_coercion_lub
canndrew Aug 3, 2016
a05560b
Add run-pass/never_coercions.rs test
canndrew Aug 3, 2016
a4e6009
Minor fixups based on feedback
canndrew Aug 3, 2016
c6890e1
Revert Ty::is_uninhabited to its original state
canndrew Aug 3, 2016
2eff282
Add some tests for ! type
canndrew Aug 10, 2016
00a71ea
Add tests for ! type
canndrew Aug 11, 2016
06747c6
Add another test for !
canndrew Aug 11, 2016
bcff5a7
Permit `! as T` with test
canndrew Aug 11, 2016
29f3636
Add explanations to tests
canndrew Aug 11, 2016
5bd54a2
Fix `make tidy`
canndrew Aug 11, 2016
6b8dace
Improve comments on ! tests
canndrew Aug 11, 2016
ef1b507
Fix build after rebase
canndrew Aug 13, 2016
c3131f2
Fix bug in PostExpansionVisitor
canndrew Aug 13, 2016
0add394
Remove diagnostic E0166
canndrew Aug 13, 2016
f59f1f0
Fix bug for ! in old trans
canndrew Aug 15, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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!();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikomatsakis What do you think about these impls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure I added these out of necessity to make a test pass. Also ! is trivially totally ordered so it should impl Ord.


// & 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