From a885411c6613cf47d012c77033e9f0400d6aeb5d Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 11 Sep 2018 12:41:56 +0100 Subject: [PATCH 1/6] Check for uninhabitedness instead of never --- src/librustc/cfg/construct.rs | 3 +- src/librustc/middle/liveness.rs | 6 +-- src/librustc/ty/sty.rs | 11 ++++++ src/librustc_codegen_llvm/debuginfo/mod.rs | 2 + src/librustc_codegen_llvm/mir/block.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 3 +- src/librustc_mir/build/expr/into.rs | 4 +- src/librustc_mir/hair/pattern/check_match.rs | 11 +----- src/test/debuginfo/nil-enum.rs | 39 ++++--------------- 9 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 6b9c0aac52e79..cbc0b7c5485c8 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,8 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - // FIXME(canndrew): This is_never should probably be an is_uninhabited. - if self.tables.expr_ty(call_expr).is_never() { + if self.tables.expr_ty(call_expr).conservative_is_uninhabited() { self.add_unreachable_node() } else { ret diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 0d70a64123bf3..96bd21b822ba3 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1209,8 +1209,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - // FIXME(canndrew): This is_never should really be an is_uninhabited - let succ = if self.tables.expr_ty(expr).is_never() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { self.s.exit_ln } else { succ @@ -1220,8 +1219,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - // FIXME(canndrew): This is_never should really be an is_uninhabited - let succ = if self.tables.expr_ty(expr).is_never() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { self.s.exit_ln } else { succ diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 8fb5ed66a82ef..ac6ef7ea812a0 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1489,6 +1489,17 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn conservative_is_uninhabited(&self) -> bool { + // Checks whether a type is definitely uninhabited. This is + // conservative: for some types that are uninhabited we return `false`, + // but we only return `true` for types that are definitely uninhabited. + match self.sty { + ty::Never => true, + ty::Adt(def, _) => def.variants.is_empty(), + _ => false + } + } + pub fn is_primitive(&self) -> bool { match self.sty { Bool | Char | Int(_) | Uint(_) | Float(_) => true, diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 7b0c413e85761..dd2ed03646415 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -279,6 +279,8 @@ pub fn create_function_debug_context( } None => {} }; + + // Tell LLVM that functions that return uninhabited types will not return. if cx.layout_of(sig.output()).abi.is_uninhabited() { flags = flags | DIFlags::FlagNoReturn; } diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 709fceb492509..568d3faad8aef 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -457,7 +457,7 @@ impl FunctionCx<'a, 'll, 'tcx> { // we can do what we like. Here, we declare that transmuting // into an uninhabited type is impossible, so anything following // it must be unreachable. - assert_eq!(bx.cx.layout_of(sig.output()).abi, layout::Abi::Uninhabited); + assert!(sig.output().conservative_is_uninhabited()); bx.unreachable(); } return; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index a20d7cc561979..764d84bb2252a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1259,8 +1259,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } None => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - if !sig.output().is_never() { + if !sig.output().conservative_is_uninhabited() { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 5708ac4e6b50f..f471f8ad5e55a 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -265,8 +265,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { exit_block.unit() } ExprKind::Call { ty, fun, args } => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - let diverges = expr.ty.is_never(); let intrinsic = match ty.sty { ty::FnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); @@ -321,7 +319,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { func: fun, args, cleanup: Some(cleanup), - destination: if diverges { + destination: if expr.ty.conservative_is_uninhabited() { None } else { Some((destination.clone(), success)) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 23667d1b331a3..a8af38fef597d 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -229,7 +229,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - self.conservative_is_uninhabited(pat_ty) + pat_ty.conservative_is_uninhabited() }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong @@ -257,15 +257,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { }) } - fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool { - // "rustc-1.0-style" uncontentious uninhabitableness check - match scrutinee_ty.sty { - ty::Never => true, - ty::Adt(def, _) => def.variants.is_empty(), - _ => false - } - } - fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) { let module = self.tcx.hir.get_module_parent(pat.id); MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { diff --git a/src/test/debuginfo/nil-enum.rs b/src/test/debuginfo/nil-enum.rs index ab9c7e2dd2758..3f74f90fec7c4 100644 --- a/src/test/debuginfo/nil-enum.rs +++ b/src/test/debuginfo/nil-enum.rs @@ -1,50 +1,25 @@ -// Copyright 2013-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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - // NOTE Instantiating an empty enum is UB. This test may break in the future. - -// LLDB can't handle zero-sized values +// +// LLDB can't handle zero-sized values. // ignore-lldb - // compile-flags:-g // gdb-command:run -// gdb-command:print first +// gdb-command:print *first // gdbg-check:$1 = {} // gdbr-check:$1 = -// gdb-command:print second -// gdbg-check:$2 = {} -// gdbr-check:$2 = - #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] -#![feature(maybe_uninit)] #![omit_gdb_pretty_printer_section] -use std::mem::MaybeUninit; - -enum ANilEnum {} -enum AnotherNilEnum {} +enum Void {} -// This test relies on gdbg printing the string "{}" for empty -// structs (which may change some time) -// The error from gdbr is expected since nil enums are not supposed to exist. fn main() { - unsafe { - let first: ANilEnum = MaybeUninit::uninitialized().into_inner(); - let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner(); + let first: *const Void = 1 as *const _; - zzz(); // #break - } + zzz(); // #break } -fn zzz() {()} +fn zzz() {} From a45cbac3b5cebc55e8b89841625da6dc1b3636a5 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 11 Sep 2018 13:04:21 +0100 Subject: [PATCH 2/6] Make uninhabitedness checking more intelligent --- src/librustc/cfg/construct.rs | 2 +- src/librustc/middle/liveness.rs | 4 +-- src/librustc/ty/sty.rs | 32 +++++++++++++++++-- src/librustc_codegen_llvm/mir/block.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 2 +- src/librustc_mir/build/expr/into.rs | 2 +- src/librustc_mir/hair/pattern/check_match.rs | 2 +- 7 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index cbc0b7c5485c8..2c52697d56d27 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,7 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - if self.tables.expr_ty(call_expr).conservative_is_uninhabited() { + if self.tables.expr_ty(call_expr).conservative_is_uninhabited(self.tcx) { self.add_unreachable_node() } else { ret diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 96bd21b822ba3..963f721204678 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1209,7 +1209,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) { self.s.exit_ln } else { succ @@ -1219,7 +1219,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited(self.ir.tcx) { self.s.exit_ln } else { succ diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index ac6ef7ea812a0..13bc4992aff7b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1489,14 +1489,40 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - pub fn conservative_is_uninhabited(&self) -> bool { + pub fn conservative_is_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { // Checks whether a type is definitely uninhabited. This is // conservative: for some types that are uninhabited we return `false`, // but we only return `true` for types that are definitely uninhabited. match self.sty { ty::Never => true, - ty::Adt(def, _) => def.variants.is_empty(), - _ => false + ty::Adt(def, _) => { + // Any ADT is uninhabited if: + // (a) It has no variants (i.e. an empty `enum`); + // (b) Each of its variants (a single one in the case of a `struct`) has at least + // one uninhabited field. + def.variants.iter().all(|var| { + var.fields.iter().any(|field| { + tcx.type_of(field.did).conservative_is_uninhabited(tcx) + }) + }) + } + ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited(tcx)), + ty::Array(ty, len) => { + match len.val.try_to_scalar() { + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + Some(n) if !n.is_null() => ty.conservative_is_uninhabited(tcx), + _ => false + } + } + ty::Ref(..) => { + // Though references to uninhabited types are trivially uninhabited + // theoretically, null references are permitted in unsafe code (as + // long as the value is not dereferenced), so we treat all references + // as inhabited. + false + } + _ => false, } } diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 568d3faad8aef..d3daa25ee59e5 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -457,7 +457,7 @@ impl FunctionCx<'a, 'll, 'tcx> { // we can do what we like. Here, we declare that transmuting // into an uninhabited type is impossible, so anything following // it must be unreachable. - assert!(sig.output().conservative_is_uninhabited()); + assert!(sig.output().conservative_is_uninhabited(bx.tcx())); bx.unreachable(); } return; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 764d84bb2252a..2d0b910802bb8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1259,7 +1259,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } None => { - if !sig.output().conservative_is_uninhabited() { + if !sig.output().conservative_is_uninhabited(self.tcx()) { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index f471f8ad5e55a..3104fc96bf100 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -319,7 +319,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { func: fun, args, cleanup: Some(cleanup), - destination: if expr.ty.conservative_is_uninhabited() { + destination: if expr.ty.conservative_is_uninhabited(this.hir.tcx()) { None } else { Some((destination.clone(), success)) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index a8af38fef597d..bdcb8d573674b 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -229,7 +229,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - pat_ty.conservative_is_uninhabited() + pat_ty.conservative_is_uninhabited(self.tcx) }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong From 681057cd794ca93f421c5610da729e87bbb1e9f3 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 11 Sep 2018 14:55:12 +0100 Subject: [PATCH 3/6] Update uninhabited matches tests --- .../uninhabited-matches-feature-gated.rs | 4 +-- .../uninhabited-matches-feature-gated.stderr | 26 +------------------ 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs index 1d3f8ff12d865..a3b99deac233b 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs @@ -20,10 +20,10 @@ fn main() { let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; // okay let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; // okay let x: &[Void] = unsafe { std::mem::uninitialized() }; let _ = match x { //~ ERROR non-exhaustive diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index d86ebda027efb..abaad8958dbe6 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -16,30 +16,6 @@ help: Please ensure that all possible cases are being handled; possibly adding w LL | let _ = match x {}; //~ ERROR non-exhaustive | ^ -error[E0004]: non-exhaustive patterns: type (Void,) is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:23:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - | -help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms. - --> $DIR/uninhabited-matches-feature-gated.rs:23:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - -error[E0004]: non-exhaustive patterns: type [Void; 1] is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:26:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - | -help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms. - --> $DIR/uninhabited-matches-feature-gated.rs:26:19 - | -LL | let _ = match x {}; //~ ERROR non-exhaustive - | ^ - error[E0004]: non-exhaustive patterns: `&[_]` not covered --> $DIR/uninhabited-matches-feature-gated.rs:29:19 | @@ -58,7 +34,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered LL | let Ok(x) = x; | ^^^^^ pattern `Err(_)` not covered -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors Some errors occurred: E0004, E0005. For more information about an error, try `rustc --explain E0004`. From 044484325cfdbfc9487609f98c0ce98d68bc100d Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 14 Sep 2018 22:46:13 +0100 Subject: [PATCH 4/6] Address comments --- src/librustc/ty/sty.rs | 6 +++++- src/librustc_codegen_llvm/mir/block.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 13bc4992aff7b..7d13bb57e0740 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1495,8 +1495,12 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { // but we only return `true` for types that are definitely uninhabited. match self.sty { ty::Never => true, + ty::Adt(def, _) if def.is_union() => { + // For now, `union`s are never considered uninhabited. + false + } ty::Adt(def, _) => { - // Any ADT is uninhabited if: + // Any ADT is uninhabited if either: // (a) It has no variants (i.e. an empty `enum`); // (b) Each of its variants (a single one in the case of a `struct`) has at least // one uninhabited field. diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index d3daa25ee59e5..709fceb492509 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -457,7 +457,7 @@ impl FunctionCx<'a, 'll, 'tcx> { // we can do what we like. Here, we declare that transmuting // into an uninhabited type is impossible, so anything following // it must be unreachable. - assert!(sig.output().conservative_is_uninhabited(bx.tcx())); + assert_eq!(bx.cx.layout_of(sig.output()).abi, layout::Abi::Uninhabited); bx.unreachable(); } return; From 541bd2cabcf73f55bf4d3c90921aa2d5894d46df Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 20 Sep 2018 20:22:02 +0100 Subject: [PATCH 5/6] Fix Ref inhabitedness comment --- src/librustc/ty/sty.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 7d13bb57e0740..fb818d9cbdcdf 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1520,10 +1520,9 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } ty::Ref(..) => { - // Though references to uninhabited types are trivially uninhabited - // theoretically, null references are permitted in unsafe code (as - // long as the value is not dereferenced), so we treat all references - // as inhabited. + // References to uninitialised memory is valid for any type, including + // uninhabited types, in unsafe code, so we treat all references as + // inhabited. false } _ => false, From 1abd0d82d5839bf294b8e818136ec6154796ca9f Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 26 Sep 2018 13:49:45 +0100 Subject: [PATCH 6/6] #[allow(unused_)] on some platforms --- src/libstd/net/tcp.rs | 1 + src/libstd/sys_common/process.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 75c7a3d928094..8a07a0ca7adb0 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -729,6 +729,7 @@ impl TcpListener { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + #[allow(unused_variables)] // `TcpStream` is uninhabited on some platforms. self.0.accept().map(|(a, b)| (TcpStream(a), b)) } diff --git a/src/libstd/sys_common/process.rs b/src/libstd/sys_common/process.rs index ddf0ebe603e08..465af74371cbc 100644 --- a/src/libstd/sys_common/process.rs +++ b/src/libstd/sys_common/process.rs @@ -66,6 +66,7 @@ impl CommandEnv { pub fn capture(&self) -> BTreeMap { let mut result = BTreeMap::::new(); if !self.clear { + #[allow(unused_mut)] // `Env` is uninhabited on some platforms. for (k, v) in env::vars_os() { result.insert(k.into(), v); } @@ -83,6 +84,7 @@ impl CommandEnv { // Apply these changes directly to the current environment pub fn apply(&self) { if self.clear { + #[allow(unused_mut)] // `Env` is uninhabited on some platforms. for (k, _) in env::vars_os() { env::remove_var(k); }