Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1349,3 +1349,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicConstStableIndirectPar
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect;
}

pub(crate) struct RustcExhaustiveParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcExhaustiveParser {
const PATH: &'static [Symbol] = &[sym::rustc_must_match_exhaustively];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Enum)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcMustMatchExhaustively;
}
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcEffectiveVisibilityParser>>,
Single<WithoutArgs<RustcEiiForeignItemParser>>,
Single<WithoutArgs<RustcEvaluateWhereClausesParser>>,
Single<WithoutArgs<RustcExhaustiveParser>>,
Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
Single<WithoutArgs<RustcHiddenTypeOfOpaquesParser>>,
Single<WithoutArgs<RustcInheritOverflowChecksParser>>,
Expand Down
11 changes: 4 additions & 7 deletions compiler/rustc_const_eval/src/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,10 @@ impl Qualif for HasMutInterior {
// FIXME(#132279): Once we've got a typing mode which reveals opaque types using the HIR
// typeck results without causing query cycles, we should use this here instead of defining
// opaque types.
let typing_env = ty::TypingEnv {
typing_mode: ty::TypingMode::analysis_in_body(
cx.tcx,
cx.body.source.def_id().expect_local(),
),
param_env: cx.typing_env.param_env,
};
let typing_env = ty::TypingEnv::new(
cx.typing_env.param_env,
ty::TypingMode::analysis_in_body(cx.tcx, cx.body.source.def_id().expect_local()),
);
Comment on lines +106 to +109
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

oh, this should use ty::TyingEnv::Borrowck or even PostBorrowckAnalysis with the new solver

let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(typing_env);
let ocx = ObligationCtxt::new(&infcx);
let obligation = Obligation::new(
Expand Down
16 changes: 13 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
// This shouldn't be used for statics, since statics are conceptually places,
// not values -- so what we do here could break pointer identity.
assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id()));
// Const eval always happens in PostAnalysis mode . See the comment in
// `InterpCx::new` for more details.
debug_assert_eq!(key.typing_env.typing_mode, ty::TypingMode::PostAnalysis);

if cfg!(debug_assertions) {
match key.typing_env.typing_mode() {
ty::TypingMode::PostAnalysis => {}
ty::TypingMode::Coherence
| ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. } => {
bug!(
"Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details."
)
}
}

// Make sure we format the instance even if we do not print it.
// This serves as a regression test against an ICE on printing.
// The next two lines concatenated contain some discussion:
Expand Down
16 changes: 13 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,19 @@ pub(crate) fn eval_to_valtree<'tcx>(
typing_env: ty::TypingEnv<'tcx>,
cid: GlobalId<'tcx>,
) -> EvalToValTreeResult<'tcx> {
// Const eval always happens in PostAnalysis mode . See the comment in
// `InterpCx::new` for more details.
debug_assert_eq!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
if cfg!(debug_assertions) {
match typing_env.typing_mode() {
ty::TypingMode::PostAnalysis => {}
ty::TypingMode::Coherence
| ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. } => {
bug!(
"Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details."
)
}
}
}
let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?;

// FIXME Need to provide a span to `eval_to_valtree`
Expand Down
20 changes: 15 additions & 5 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::debug_assert_matches;

use either::{Left, Right};
use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout};
use rustc_hir::def_id::DefId;
Expand All @@ -11,9 +9,10 @@ use rustc_middle::ty::layout::{
LayoutOfHelpers, TyAndLayout,
};
use rustc_middle::ty::{
self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, Variance,
self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, TypingMode,
Variance,
};
use rustc_middle::{mir, span_bug};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::Span;
use rustc_target::callconv::FnAbi;
use tracing::{debug, trace};
Expand Down Expand Up @@ -243,7 +242,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// opaque types. This is needed for trivial things like `size_of`, but also for using associated
// types that are not specified in the opaque type. We also use MIR bodies whose opaque types have
// already been revealed, so we'd be able to at least partially observe the hidden types anyways.
debug_assert_matches!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
if cfg!(debug_assertions) {
match typing_env.typing_mode() {
TypingMode::PostAnalysis => {}
TypingMode::Coherence
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => {
bug!("Const eval should always happens in PostAnalysis mode.");
}
}
}

InterpCx {
machine,
tcx: tcx.at(root_span),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_scalable_vector, Normal, template!(List: &["count"]), WarnFollowing, EncodeCrossCrate::Yes,
"`#[rustc_scalable_vector]` defines a scalable vector type"
),
rustc_attr!(
rustc_must_match_exhaustively, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes,
"enums with `#[rustc_must_match_exhaustively]` must be matched on with a match block that mentions all variants explicitly"
),

// ==========================================================================
// Internal attributes, Testing:
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,9 @@ pub enum AttributeKind {
fn_names: ThinVec<Ident>,
},

/// Represents `#[rustc_must_match_exhaustively]`
RustcMustMatchExhaustively(Span),

/// Represents `#[rustc_never_returns_null_ptr]`
RustcNeverReturnsNullPtr,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ impl AttributeKind {
RustcMain => No,
RustcMir(..) => Yes,
RustcMustImplementOneOf { .. } => No,
RustcMustMatchExhaustively(..) => Yes,
RustcNeverReturnsNullPtr => Yes,
RustcNeverTypeOptions { .. } => No,
RustcNoImplicitAutorefs => Yes,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_middle::ty::{
self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitableExt,
};
use rustc_type_ir::TypingModeEqWrapper;
use smallvec::SmallVec;
use tracing::debug;

Expand Down Expand Up @@ -72,7 +73,7 @@ impl<'tcx> InferCtxt<'tcx> {
query_state,
)
.unchecked_map(|(param_env, value)| param_env.and(value));
CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }
CanonicalQueryInput { canonical, typing_mode: TypingModeEqWrapper(self.typing_mode()) }
}

/// Canonicalizes a query *response* `V`. When we canonicalize a
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,16 +564,16 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let infcx = self.build(input.typing_mode);
let infcx = self.build(input.typing_mode.0);
let (value, args) = infcx.instantiate_canonical(span, &input.canonical);
(infcx, value, args)
}

pub fn build_with_typing_env(
mut self,
TypingEnv { typing_mode, param_env }: TypingEnv<'tcx>,
typing_env: TypingEnv<'tcx>,
) -> (InferCtxt<'tcx>, ty::ParamEnv<'tcx>) {
(self.build(typing_mode), param_env)
(self.build(typing_env.typing_mode()), typing_env.param_env)
}

pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> {
Expand Down Expand Up @@ -1376,7 +1376,7 @@ impl<'tcx> InferCtxt<'tcx> {
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis) => mode,
};
ty::TypingEnv { typing_mode, param_env }
ty::TypingEnv::new(param_env, typing_mode)
}

/// Similar to [`Self::canonicalize_query`], except that it returns
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl<'tcx> InferCtxt<'tcx> {
if def_id.is_local() =>
{
let def_id = def_id.expect_local();
if let ty::TypingMode::Coherence = self.typing_mode() {
if self.typing_mode().is_coherence() {
// See comment on `insert_hidden_type` for why this is sufficient in coherence
return Some(self.register_hidden_type(
OpaqueTypeKey { def_id, args },
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_infer/src/infer/relate/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_middle::bug;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{
self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, TypingMode,
TypeVisitableExt, TypeVisitor,
};
use rustc_span::Span;
use tracing::{debug, instrument, warn};
Expand Down Expand Up @@ -603,7 +603,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
//
// cc trait-system-refactor-initiative#108
if self.infcx.next_trait_solver()
&& !matches!(self.infcx.typing_mode(), TypingMode::Coherence)
&& !self.infcx.typing_mode().is_coherence()
&& self.in_alias
{
inner.type_variables().equate(vid, new_var_id);
Expand Down Expand Up @@ -735,7 +735,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
// See the comment for type inference variables
// for more details.
if self.infcx.next_trait_solver()
&& !matches!(self.infcx.typing_mode(), TypingMode::Coherence)
&& !self.infcx.typing_mode().is_coherence()
&& self.in_alias
{
variable_table.union(vid, new_var_id);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ impl<'tcx> LateContext<'tcx> {
}

pub fn typing_env(&self) -> TypingEnv<'tcx> {
TypingEnv { typing_mode: self.typing_mode(), param_env: self.param_env }
TypingEnv::new(self.param_env, self.typing_mode())
}

pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
Expand Down
91 changes: 89 additions & 2 deletions compiler/rustc_lint/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use rustc_span::{Span, sym};
use crate::lints::{
AttributeKindInFindAttr, BadOptAccessDiag, DefaultHashTypesDiag,
ImplicitSysrootCrateImportDiag, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability,
QueryUntracked, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, TyQualified, TykindDiag,
TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage,
QueryUntracked, RustcMustMatchExhaustivelyNotExhaustive, SpanUseEqCtxtDiag,
SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrDirectUse,
TypeIrInherentUsage, TypeIrTraitUsage,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};

Expand Down Expand Up @@ -713,3 +714,89 @@ impl EarlyLintPass for BadUseOfFindAttr {
}
}
}

declare_tool_lint! {
pub rustc::RUSTC_MUST_MATCH_EXHAUSTIVELY,
Allow,
"Forbids matches with wildcards, or if-let matching on enums marked with `#[rustc_must_match_exhaustively]`",
report_in_external_macro: true
}
declare_lint_pass!(RustcMustMatchExhaustively => [RUSTC_MUST_MATCH_EXHAUSTIVELY]);

fn is_rustc_must_match_exhaustively(cx: &LateContext<'_>, id: HirId) -> Option<Span> {
let res = cx.typeck_results();

let ty = res.node_type(id);

let ty = if let ty::Ref(_, ty, _) = ty.kind() { *ty } else { ty };

if let Some(adt_def) = ty.ty_adt_def()
&& adt_def.is_enum()
{
find_attr!(cx.tcx, adt_def.did(), RustcMustMatchExhaustively(span) => *span)
} else {
None
}
}

fn pat_is_not_exhaustive_heuristic(pat: &hir::Pat<'_>) -> Option<(Span, &'static str)> {
match pat.kind {
hir::PatKind::Missing => None,
hir::PatKind::Wild => Some((pat.span, "because of this wildcard pattern")),
hir::PatKind::Binding(_, _, _, Some(pat)) => pat_is_not_exhaustive_heuristic(pat),
hir::PatKind::Binding(..) => Some((pat.span, "because of this variable binding")),
hir::PatKind::Struct(..) => None,
hir::PatKind::TupleStruct(..) => None,
hir::PatKind::Or(..) => None,
hir::PatKind::Never => None,
hir::PatKind::Tuple(..) => None,
hir::PatKind::Box(pat) => pat_is_not_exhaustive_heuristic(&*pat),
hir::PatKind::Deref(pat) => pat_is_not_exhaustive_heuristic(&*pat),
hir::PatKind::Ref(pat, _, _) => pat_is_not_exhaustive_heuristic(&*pat),
hir::PatKind::Expr(..) => None,
hir::PatKind::Guard(..) => None,
hir::PatKind::Range(..) => None,
hir::PatKind::Slice(..) => None,
hir::PatKind::Err(..) => None,
}
}

impl<'tcx> LateLintPass<'tcx> for RustcMustMatchExhaustively {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
match expr.kind {
// This is not perfect exhaustiveness checking, that's why this is just a rustc internal
// attribute. But it catches most reasonable cases
hir::ExprKind::Match(expr, arms, _) => {
if let Some(attr_span) = is_rustc_must_match_exhaustively(cx, expr.hir_id) {
for arm in arms {
if let Some((span, message)) = pat_is_not_exhaustive_heuristic(arm.pat) {
cx.emit_span_lint(
RUSTC_MUST_MATCH_EXHAUSTIVELY,
expr.span,
RustcMustMatchExhaustivelyNotExhaustive {
attr_span,
pat_span: span,
message,
},
);
}
}
}
}
hir::ExprKind::If(expr, ..) if let ExprKind::Let(expr) = expr.kind => {
if let Some(attr_span) = is_rustc_must_match_exhaustively(cx, expr.init.hir_id) {
cx.emit_span_lint(
RUSTC_MUST_MATCH_EXHAUSTIVELY,
expr.span,
RustcMustMatchExhaustivelyNotExhaustive {
attr_span,
pat_span: expr.span,
message: "using if let only matches on one variant (try using `match`)",
},
);
}
}
_ => {}
}
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,8 @@ fn register_internals(store: &mut LintStore) {
store.register_early_pass(|| Box::new(ImplicitSysrootCrateImport));
store.register_lints(&BadUseOfFindAttr::lint_vec());
store.register_early_pass(|| Box::new(BadUseOfFindAttr));
store.register_lints(&RustcMustMatchExhaustively::lint_vec());
store.register_late_pass(|_| Box::new(RustcMustMatchExhaustively));
store.register_group(
false,
"rustc::internal",
Expand All @@ -688,6 +690,7 @@ fn register_internals(store: &mut LintStore) {
LintId::of(DIRECT_USE_OF_RUSTC_TYPE_IR),
LintId::of(IMPLICIT_SYSROOT_CRATE_IMPORT),
LintId::of(BAD_USE_OF_FIND_ATTR),
LintId::of(RUSTC_MUST_MATCH_EXHAUSTIVELY),
],
);
}
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,18 @@ pub(crate) struct ImplicitSysrootCrateImportDiag<'a> {
#[help("remove `AttributeKind`")]
pub(crate) struct AttributeKindInFindAttr;

#[derive(Diagnostic)]
#[diag("match is not exhaustive")]
#[help("explicitly list all variants of the enum in a `match`")]
pub(crate) struct RustcMustMatchExhaustivelyNotExhaustive {
#[label("required because of this attribute")]
pub attr_span: Span,

#[note("{$message}")]
pub pat_span: Span,
pub message: &'static str,
}

// let_underscore.rs
#[derive(Diagnostic)]
pub(crate) enum NonBindingLet {
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,10 @@ impl<'tcx> Body<'tcx> {
pub fn typing_env(&self, tcx: TyCtxt<'tcx>) -> TypingEnv<'tcx> {
match self.phase {
// FIXME(#132279): we should reveal the opaques defined in the body during analysis.
MirPhase::Built | MirPhase::Analysis(_) => TypingEnv {
typing_mode: ty::TypingMode::non_body_analysis(),
param_env: tcx.param_env(self.source.def_id()),
},
MirPhase::Built | MirPhase::Analysis(_) => TypingEnv::new(
tcx.param_env(self.source.def_id()),
ty::TypingMode::non_body_analysis(),
),
MirPhase::Runtime(_) => TypingEnv::post_analysis(tcx, self.source.def_id()),
}
}
Expand Down
Loading
Loading