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

Rollup of 8 pull requests #94787

Merged
merged 18 commits into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
104 changes: 65 additions & 39 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ struct AstValidator<'a> {
/// certain positions.
is_assoc_ty_bound_banned: bool,

/// Used to allow `let` expressions in certain syntactic locations.
is_let_allowed: bool,
/// See [ForbiddenLetReason]
forbidden_let_reason: Option<ForbiddenLetReason>,

lint_buffer: &'a mut LintBuffer,
}
Expand Down Expand Up @@ -103,20 +103,28 @@ impl<'a> AstValidator<'a> {
self.is_tilde_const_allowed = old;
}

fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) {
let old = mem::replace(&mut self.is_let_allowed, allowed);
fn with_let_management(
&mut self,
forbidden_let_reason: Option<ForbiddenLetReason>,
f: impl FnOnce(&mut Self, Option<ForbiddenLetReason>),
) {
let old = mem::replace(&mut self.forbidden_let_reason, forbidden_let_reason);
f(self, old);
self.is_let_allowed = old;
self.forbidden_let_reason = old;
}

/// Emits an error banning the `let` expression provided in the given location.
fn ban_let_expr(&self, expr: &'a Expr) {
fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) {
let sess = &self.session;
if sess.opts.unstable_features.is_nightly_build() {
sess.struct_span_err(expr.span, "`let` expressions are not supported here")
.note("only supported directly in conditions of `if`- and `while`-expressions")
.note("as well as when nested within `&&` and parentheses in those conditions")
.emit();
let err = "`let` expressions are not supported here";
let mut diag = sess.struct_span_err(expr.span, err);
diag.note("only supported directly in conditions of `if` and `while` expressions");
diag.note("as well as when nested within `&&` and parentheses in those conditions");
if let ForbiddenLetReason::ForbiddenWithOr(span) = forbidden_let_reason {
diag.span_note(span, "`||` operators are not allowed in let chain expressions");
}
diag.emit();
} else {
sess.struct_span_err(expr.span, "expected expression, found statement (`let`)")
.note("variable declaration using `let` is a statement")
Expand Down Expand Up @@ -988,39 +996,48 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}

fn visit_expr(&mut self, expr: &'a Expr) {
self.with_let_allowed(false, |this, let_allowed| match &expr.kind {
ExprKind::If(cond, then, opt_else) => {
this.visit_block(then);
walk_list!(this, visit_expr, opt_else);
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
return;
}
ExprKind::Let(..) if !let_allowed => this.ban_let_expr(expr),
ExprKind::Match(expr, arms) => {
this.visit_expr(expr);
for arm in arms {
this.visit_expr(&arm.body);
this.visit_pat(&arm.pat);
walk_list!(this, visit_attribute, &arm.attrs);
if let Some(ref guard) = arm.guard {
if let ExprKind::Let(_, ref expr, _) = guard.kind {
this.with_let_allowed(true, |this, _| this.visit_expr(expr));
self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| {
match &expr.kind {
ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => {
let forbidden_let_reason = Some(ForbiddenLetReason::ForbiddenWithOr(*span));
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(lhs));
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(rhs));
}
ExprKind::If(cond, then, opt_else) => {
this.visit_block(then);
walk_list!(this, visit_expr, opt_else);
this.with_let_management(None, |this, _| this.visit_expr(cond));
return;
}
ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => {
this.ban_let_expr(expr, elem);
},
ExprKind::Match(scrutinee, arms) => {
this.visit_expr(scrutinee);
for arm in arms {
this.visit_expr(&arm.body);
this.visit_pat(&arm.pat);
walk_list!(this, visit_attribute, &arm.attrs);
if let Some(guard) = &arm.guard && let ExprKind::Let(_, guard_expr, _) = &guard.kind {
this.with_let_management(None, |this, _| {
this.visit_expr(guard_expr)
});
return;
}
}
}
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr));
return;
}
ExprKind::While(cond, then, opt_label) => {
walk_list!(this, visit_label, opt_label);
this.visit_block(then);
this.with_let_management(None, |this, _| this.visit_expr(cond));
return;
}
_ => visit::walk_expr(this, expr),
}
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr));
return;
}
ExprKind::While(cond, then, opt_label) => {
walk_list!(this, visit_label, opt_label);
this.visit_block(then);
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
return;
}
_ => visit::walk_expr(this, expr),
});
}

Expand Down Expand Up @@ -1772,10 +1789,19 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
is_tilde_const_allowed: false,
is_impl_trait_banned: false,
is_assoc_ty_bound_banned: false,
is_let_allowed: false,
forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden),
lint_buffer: lints,
};
visit::walk_crate(&mut validator, krate);

validator.has_proc_macro_decls
}

/// Used to forbid `let` expressions in certain syntactic locations.
#[derive(Clone, Copy)]
enum ForbiddenLetReason {
/// A let chain with the `||` operator
ForbiddenWithOr(Span),
/// `let` is not valid and the source environment is not important
GenericForbidden,
}
6 changes: 4 additions & 2 deletions compiler/rustc_ast_passes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
//!
//! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`.

#![feature(iter_is_partitioned)]
#![allow(rustc::potential_query_instability)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_is_partitioned)]
#![feature(let_chains)]
#![feature(let_else)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]

pub mod ast_validation;
pub mod feature_gate;
Expand Down
86 changes: 79 additions & 7 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use super::{PatCtxt, PatternError};
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_errors::{
error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
ErrorGuaranteed,
};
use rustc_hir as hir;
use rustc_hir::def::*;
Expand All @@ -20,7 +21,7 @@ use rustc_session::lint::builtin::{
};
use rustc_session::Session;
use rustc_span::source_map::Spanned;
use rustc_span::{DesugaringKind, ExpnKind, MultiSpan, Span};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span};

crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
let body_id = match def_id.as_local() {
Expand Down Expand Up @@ -241,6 +242,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
}

let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);

let mut bindings = vec![];

let mut err = struct_span_err!(
self.tcx.sess,
pat.span,
Expand All @@ -257,6 +261,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
false
}
_ => {
pat.walk(&mut |pat: &hir::Pat<'_>| {
match pat.kind {
hir::PatKind::Binding(_, _, ident, _) => {
bindings.push(ident);
}
_ => {}
}
true
});

err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns));
true
}
Expand All @@ -267,13 +281,71 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
"`let` bindings require an \"irrefutable pattern\", like a `struct` or \
an `enum` with only one variant",
);
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
err.span_suggestion(
span,
"you might want to use `if let` to ignore the variant that isn't matched",
format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
if self.tcx.sess.source_map().span_to_snippet(span).is_ok() {
let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1));
let start_span = span.shrink_to_lo();
let end_span = semi_span.shrink_to_lo();
err.multipart_suggestion(
&format!(
"you might want to use `if let` to ignore the variant{} that {} matched",
pluralize!(witnesses.len()),
match witnesses.len() {
1 => "isn't",
_ => "aren't",
},
),
vec![
match &bindings[..] {
[] => (start_span, "if ".to_string()),
[binding] => (start_span, format!("let {} = if ", binding)),
bindings => (
start_span,
format!(
"let ({}) = if ",
bindings
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ")
),
),
},
match &bindings[..] {
[] => (semi_span, " { todo!() }".to_string()),
[binding] => {
(end_span, format!(" {{ {} }} else {{ todo!() }}", binding))
}
bindings => (
end_span,
format!(
" {{ ({}) }} else {{ todo!() }}",
bindings
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ")
),
),
},
],
Applicability::HasPlaceholders,
);
if !bindings.is_empty() && cx.tcx.sess.is_nightly_build() {
err.span_suggestion_verbose(
semi_span.shrink_to_lo(),
&format!(
"alternatively, on nightly, you might want to use \
`#![feature(let_else)]` to handle the variant{} that {} matched",
pluralize!(witnesses.len()),
match witnesses.len() {
1 => "isn't",
_ => "aren't",
},
),
" else { todo!() }".to_string(),
Applicability::HasPlaceholders,
);
}
}
err.note(
"for more information, visit \
Expand Down
15 changes: 10 additions & 5 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,7 @@ impl CrateCheckConfig {
// NOTE: This should be kept in sync with `default_configuration` and
// `fill_well_known_values`
const WELL_KNOWN_NAMES: &[Symbol] = &[
// rustc
sym::unix,
sym::windows,
sym::target_os,
Expand All @@ -1091,9 +1092,12 @@ impl CrateCheckConfig {
sym::debug_assertions,
sym::proc_macro,
sym::test,
sym::feature,
// rustdoc
sym::doc,
sym::doctest,
sym::feature,
// miri
sym::miri,
];

// We only insert well-known names if `names()` was activated
Expand Down Expand Up @@ -1128,13 +1132,14 @@ impl CrateCheckConfig {

// No-values
for name in [
sym::doc,
sym::miri,
sym::unix,
sym::windows,
sym::debug_assertions,
sym::proc_macro,
sym::test,
sym::doc,
sym::doctest,
sym::windows,
sym::proc_macro,
sym::debug_assertions,
sym::target_thread_local,
] {
self.values_valid.entry(name).or_default();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,7 @@ symbols! {
minnumf32,
minnumf64,
mips_target_feature,
miri,
misc,
mmx_reg,
modifiers,
Expand Down
21 changes: 15 additions & 6 deletions library/core/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ pub trait Clone: Sized {
/// allocations.
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn clone_from(&mut self, source: &Self) {
#[default_method_body_is_const]
fn clone_from(&mut self, source: &Self)
where
Self: ~const Drop,
{
*self = source.clone()
}
}
Expand Down Expand Up @@ -178,7 +182,8 @@ mod impls {
($($t:ty)*) => {
$(
#[stable(feature = "rust1", since = "1.0.0")]
impl Clone for $t {
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl const Clone for $t {
#[inline]
fn clone(&self) -> Self {
*self
Expand All @@ -196,23 +201,26 @@ mod impls {
}

#[unstable(feature = "never_type", issue = "35121")]
impl Clone for ! {
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl const Clone for ! {
#[inline]
fn clone(&self) -> Self {
*self
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Clone for *const T {
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for *const T {
#[inline]
fn clone(&self) -> Self {
*self
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Clone for *mut T {
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for *mut T {
#[inline]
fn clone(&self) -> Self {
*self
Expand All @@ -221,7 +229,8 @@ mod impls {

/// Shared references can be cloned, but mutable references *cannot*!
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Clone for &T {
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for &T {
#[inline]
#[rustc_diagnostic_item = "noop_method_clone"]
fn clone(&self) -> Self {
Expand Down
Loading