Merge from rustc

This commit is contained in:
Ralf Jung 2023-12-10 09:03:54 +01:00
commit 035d86594d
482 changed files with 12465 additions and 6271 deletions

View file

@ -288,6 +288,9 @@ jobs:
- name: x86_64-gnu-aux
os: ubuntu-20.04-4core-16gb
env: {}
- name: x86_64-gnu-integration
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-debug
os: ubuntu-20.04-8core-32gb
env: {}

View file

@ -795,10 +795,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "coverage_test_macros"
version = "0.0.0"
[[package]]
name = "cpufeatures"
version = "0.2.8"
@ -3760,6 +3756,7 @@ dependencies = [
"rustc_monomorphize",
"rustc_parse",
"rustc_passes",
"rustc_pattern_analysis",
"rustc_privacy",
"rustc_query_system",
"rustc_resolve",
@ -4233,6 +4230,7 @@ dependencies = [
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_pattern_analysis",
"rustc_session",
"rustc_span",
"rustc_target",
@ -4266,7 +4264,6 @@ dependencies = [
name = "rustc_mir_transform"
version = "0.0.0"
dependencies = [
"coverage_test_macros",
"either",
"itertools",
"rustc_arena",
@ -4308,6 +4305,13 @@ dependencies = [
"tracing",
]
[[package]]
name = "rustc_next_trait_solver"
version = "0.0.0"
dependencies = [
"rustc_type_ir",
]
[[package]]
name = "rustc_parse"
version = "0.0.0"
@ -4362,6 +4366,26 @@ dependencies = [
"tracing",
]
[[package]]
name = "rustc_pattern_analysis"
version = "0.0.0"
dependencies = [
"rustc_apfloat",
"rustc_arena",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
"rustc_session",
"rustc_span",
"rustc_target",
"smallvec",
"tracing",
]
[[package]]
name = "rustc_privacy"
version = "0.0.0"
@ -4492,6 +4516,7 @@ dependencies = [
name = "rustc_smir"
version = "0.0.0"
dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_hir",
"rustc_middle",
@ -4576,6 +4601,7 @@ dependencies = [
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_next_trait_solver",
"rustc_parse_format",
"rustc_query_system",
"rustc_session",

View file

@ -658,6 +658,24 @@ impl Pat {
pub fn is_rest(&self) -> bool {
matches!(self.kind, PatKind::Rest)
}
/// Whether this could be a never pattern, taking into account that a macro invocation can
/// return a never pattern. Used to inform errors during parsing.
pub fn could_be_never_pattern(&self) -> bool {
let mut could_be_never_pattern = false;
self.walk(&mut |pat| match &pat.kind {
PatKind::Never | PatKind::MacCall(_) => {
could_be_never_pattern = true;
false
}
PatKind::Or(s) => {
could_be_never_pattern = s.iter().all(|p| p.could_be_never_pattern());
false
}
_ => true,
});
could_be_never_pattern
}
}
/// A single field in a struct pattern.
@ -1080,8 +1098,8 @@ pub struct Arm {
pub pat: P<Pat>,
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
pub guard: Option<P<Expr>>,
/// Match arm body.
pub body: P<Expr>,
/// Match arm body. Omitted if the pattern is a never pattern.
pub body: Option<P<Expr>>,
pub span: Span,
pub id: NodeId,
pub is_placeholder: bool,
@ -1311,7 +1329,7 @@ pub struct Closure {
pub binder: ClosureBinder,
pub capture_clause: CaptureBy,
pub constness: Const,
pub coro_kind: Option<CoroutineKind>,
pub coroutine_kind: Option<CoroutineKind>,
pub movability: Movability,
pub fn_decl: P<FnDecl>,
pub body: P<Expr>,
@ -1516,6 +1534,7 @@ pub enum ExprKind {
pub enum GenBlockKind {
Async,
Gen,
AsyncGen,
}
impl fmt::Display for GenBlockKind {
@ -1529,6 +1548,7 @@ impl GenBlockKind {
match self {
GenBlockKind::Async => "async",
GenBlockKind::Gen => "gen",
GenBlockKind::AsyncGen => "async gen",
}
}
}
@ -2413,10 +2433,12 @@ pub enum Unsafe {
/// Iterator`.
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
pub enum CoroutineKind {
/// `async`, which evaluates to `impl Future`
/// `async`, which returns an `impl Future`
Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
/// `gen`, which evaluates to `impl Iterator`
/// `gen`, which returns an `impl Iterator`
Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
/// `async gen`, which returns an `impl AsyncIterator`
AsyncGen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
}
impl CoroutineKind {
@ -2428,12 +2450,23 @@ impl CoroutineKind {
matches!(self, CoroutineKind::Gen { .. })
}
pub fn closure_id(self) -> NodeId {
match self {
CoroutineKind::Async { closure_id, .. }
| CoroutineKind::Gen { closure_id, .. }
| CoroutineKind::AsyncGen { closure_id, .. } => closure_id,
}
}
/// In this case this is an `async` or `gen` return, the `NodeId` for the generated `impl Trait`
/// item.
pub fn return_id(self) -> (NodeId, Span) {
match self {
CoroutineKind::Async { return_impl_trait_id, span, .. }
| CoroutineKind::Gen { return_impl_trait_id, span, .. } => (return_impl_trait_id, span),
| CoroutineKind::Gen { return_impl_trait_id, span, .. }
| CoroutineKind::AsyncGen { return_impl_trait_id, span, .. } => {
(return_impl_trait_id, span)
}
}
}
}
@ -2838,7 +2871,7 @@ pub struct FnHeader {
/// The `unsafe` keyword, if any
pub unsafety: Unsafe,
/// Whether this is `async`, `gen`, or nothing.
pub coro_kind: Option<CoroutineKind>,
pub coroutine_kind: Option<CoroutineKind>,
/// The `const` keyword, if any
pub constness: Const,
/// The `extern` keyword and corresponding ABI string, if any
@ -2848,9 +2881,9 @@ pub struct FnHeader {
impl FnHeader {
/// Does this function header have any qualifiers or is it empty?
pub fn has_qualifiers(&self) -> bool {
let Self { unsafety, coro_kind, constness, ext } = self;
let Self { unsafety, coroutine_kind, constness, ext } = self;
matches!(unsafety, Unsafe::Yes(_))
|| coro_kind.is_some()
|| coroutine_kind.is_some()
|| matches!(constness, Const::Yes(_))
|| !matches!(ext, Extern::None)
}
@ -2858,7 +2891,12 @@ impl FnHeader {
impl Default for FnHeader {
fn default() -> FnHeader {
FnHeader { unsafety: Unsafe::No, coro_kind: None, constness: Const::No, ext: Extern::None }
FnHeader {
unsafety: Unsafe::No,
coroutine_kind: None,
constness: Const::No,
ext: Extern::None,
}
}
}

View file

@ -121,8 +121,8 @@ pub trait MutVisitor: Sized {
noop_visit_fn_decl(d, self);
}
fn visit_coro_kind(&mut self, a: &mut CoroutineKind) {
noop_visit_coro_kind(a, self);
fn visit_coroutine_kind(&mut self, a: &mut CoroutineKind) {
noop_visit_coroutine_kind(a, self);
}
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
@ -453,7 +453,7 @@ pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[
vis.visit_id(id);
vis.visit_pat(pat);
visit_opt(guard, |guard| vis.visit_expr(guard));
vis.visit_expr(body);
visit_opt(body, |body| vis.visit_expr(body));
vis.visit_span(span);
smallvec![arm]
}
@ -871,10 +871,11 @@ pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis:
}
}
pub fn noop_visit_coro_kind<T: MutVisitor>(coro_kind: &mut CoroutineKind, vis: &mut T) {
match coro_kind {
pub fn noop_visit_coroutine_kind<T: MutVisitor>(coroutine_kind: &mut CoroutineKind, vis: &mut T) {
match coroutine_kind {
CoroutineKind::Async { span, closure_id, return_impl_trait_id }
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id } => {
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
| CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id } => {
vis.visit_span(span);
vis.visit_id(closure_id);
vis.visit_id(return_impl_trait_id);
@ -1171,9 +1172,9 @@ fn visit_const_item<T: MutVisitor>(
}
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
let FnHeader { unsafety, coro_kind, constness, ext: _ } = header;
let FnHeader { unsafety, coroutine_kind, constness, ext: _ } = header;
visit_constness(constness, vis);
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
visit_unsafety(unsafety, vis);
}
@ -1407,7 +1408,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
binder,
capture_clause,
constness,
coro_kind,
coroutine_kind,
movability: _,
fn_decl,
body,
@ -1416,7 +1417,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
}) => {
vis.visit_closure_binder(binder);
visit_constness(constness, vis);
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
vis.visit_capture_by(capture_clause);
vis.visit_fn_decl(fn_decl);
vis.visit_expr(body);

View file

@ -861,7 +861,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Closure(box Closure {
binder,
capture_clause,
coro_kind: _,
coroutine_kind: _,
constness: _,
movability: _,
fn_decl,
@ -951,7 +951,7 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
visitor.visit_pat(&arm.pat);
walk_list!(visitor, visit_expr, &arm.guard);
visitor.visit_expr(&arm.body);
walk_list!(visitor, visit_expr, &arm.body);
walk_list!(visitor, visit_attribute, &arm.attrs);
}

View file

@ -91,6 +91,10 @@ ast_lowering_invalid_register =
ast_lowering_invalid_register_class =
invalid register class `{$reg_class}`: {$error}
ast_lowering_match_arm_with_no_body =
`match` arm with no body
.suggestion = add a body after the pattern
ast_lowering_misplaced_assoc_ty_binding =
associated type bounds are only allowed in where clauses and function signatures, not in {$position}
@ -104,6 +108,15 @@ ast_lowering_misplaced_impl_trait =
ast_lowering_misplaced_relax_trait_bound =
`?Trait` bounds are only permitted at the point where a type parameter is declared
ast_lowering_never_pattern_with_body =
a never pattern is always unreachable
.label = this will never be executed
.suggestion = remove this expression
ast_lowering_never_pattern_with_guard =
a guard on a never pattern will never be run
.suggestion = remove this guard
ast_lowering_not_supported_for_lifetime_binder_async_closure =
`for<...>` binders on `async` closures are not currently supported

View file

@ -340,6 +340,32 @@ pub struct NotSupportedForLifetimeBinderAsyncClosure {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_match_arm_with_no_body)]
pub struct MatchArmWithNoBody {
#[primary_span]
pub span: Span,
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_never_pattern_with_body)]
pub struct NeverPatternWithBody {
#[primary_span]
#[label]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_never_pattern_with_guard)]
pub struct NeverPatternWithGuard {
#[primary_span]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic, Clone, Copy)]
#[diag(ast_lowering_arbitrary_expression_in_pattern)]
pub struct ArbitraryExpressionInPattern {

View file

@ -1,8 +1,9 @@
use super::errors::{
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd,
NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
UnderscoreExprLhsAssign,
};
use super::ResolverAstLoweringExt;
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
@ -13,6 +14,7 @@ use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_middle::span_bug;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@ -195,22 +197,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
binder,
capture_clause,
constness,
coro_kind,
coroutine_kind,
movability,
fn_decl,
body,
fn_decl_span,
fn_arg_span,
}) => match coro_kind {
Some(
CoroutineKind::Async { closure_id, .. }
| CoroutineKind::Gen { closure_id, .. },
) => self.lower_expr_async_closure(
}) => match coroutine_kind {
Some(coroutine_kind) => self.lower_expr_coroutine_closure(
binder,
*capture_clause,
e.id,
hir_id,
*closure_id,
*coroutine_kind,
fn_decl,
body,
*fn_decl_span,
@ -324,6 +323,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::CoroutineSource::Block,
|this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)),
),
ExprKind::Gen(capture_clause, block, GenBlockKind::AsyncGen) => self
.make_async_gen_expr(
*capture_clause,
e.id,
None,
e.span,
hir::CoroutineSource::Block,
|this| this.with_new_scopes(e.span, |this| this.lower_block_expr(block)),
),
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
ExprKind::Err => hir::ExprKind::Err(
self.tcx.sess.span_delayed_bug(e.span, "lowered ExprKind::Err"),
@ -549,7 +557,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
let pat = self.lower_pat(&arm.pat);
let guard = arm.guard.as_ref().map(|cond| {
let mut guard = arm.guard.as_ref().map(|cond| {
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
hir::Guard::IfLet(self.arena.alloc(hir::Let {
hir_id: self.next_id(),
@ -564,14 +572,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
});
let hir_id = self.next_id();
let span = self.lower_span(arm.span);
self.lower_attrs(hir_id, &arm.attrs);
hir::Arm {
hir_id,
pat,
guard,
body: self.lower_expr(&arm.body),
span: self.lower_span(arm.span),
}
let is_never_pattern = pat.is_never_pattern();
let body = if let Some(body) = &arm.body
&& !is_never_pattern
{
self.lower_expr(body)
} else {
// Either `body.is_none()` or `is_never_pattern` here.
if !is_never_pattern {
let suggestion = span.shrink_to_hi();
self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
} else if let Some(body) = &arm.body {
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
guard = None;
} else if let Some(g) = &arm.guard {
self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span });
guard = None;
}
// We add a fake `loop {}` arm body so that it typecks to `!`.
// FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`.
let block = self.arena.alloc(hir::Block {
stmts: &[],
expr: None,
hir_id: self.next_id(),
rules: hir::BlockCheckMode::DefaultBlock,
span,
targeted_by_break: false,
});
self.arena.alloc(hir::Expr {
hir_id: self.next_id(),
kind: hir::ExprKind::Loop(block, None, hir::LoopSource::Loop, span),
span,
})
};
hir::Arm { hir_id, pat, guard, body, span }
}
/// Lower an `async` construct to a coroutine that implements `Future`.
@ -597,7 +634,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Resume argument type: `ResumeTy`
let unstable_span = self.mark_span_with_reason(
DesugaringKind::Async,
span,
self.lower_span(span),
Some(self.allow_gen_future.clone()),
);
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span);
@ -706,6 +743,87 @@ impl<'hir> LoweringContext<'_, 'hir> {
}))
}
/// Lower a `async gen` construct to a generator that implements `AsyncIterator`.
///
/// This results in:
///
/// ```text
/// static move? |_task_context| -> () {
/// <body>
/// }
/// ```
pub(super) fn make_async_gen_expr(
&mut self,
capture_clause: CaptureBy,
closure_node_id: NodeId,
_yield_ty: Option<hir::FnRetTy<'hir>>,
span: Span,
async_coroutine_source: hir::CoroutineSource,
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
) -> hir::ExprKind<'hir> {
let output = hir::FnRetTy::DefaultReturn(self.lower_span(span));
// Resume argument type: `ResumeTy`
let unstable_span = self.mark_span_with_reason(
DesugaringKind::Async,
self.lower_span(span),
Some(self.allow_gen_future.clone()),
);
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span);
let input_ty = hir::Ty {
hir_id: self.next_id(),
kind: hir::TyKind::Path(resume_ty),
span: unstable_span,
};
// The closure/coroutine `FnDecl` takes a single (resume) argument of type `input_ty`.
let fn_decl = self.arena.alloc(hir::FnDecl {
inputs: arena_vec![self; input_ty],
output,
c_variadic: false,
implicit_self: hir::ImplicitSelfKind::None,
lifetime_elision_allowed: false,
});
// Lower the argument pattern/ident. The ident is used again in the `.await` lowering.
let (pat, task_context_hid) = self.pat_ident_binding_mode(
span,
Ident::with_dummy_span(sym::_task_context),
hir::BindingAnnotation::MUT,
);
let param = hir::Param {
hir_id: self.next_id(),
pat,
ty_span: self.lower_span(span),
span: self.lower_span(span),
};
let params = arena_vec![self; param];
let body = self.lower_body(move |this| {
this.coroutine_kind = Some(hir::CoroutineKind::AsyncGen(async_coroutine_source));
let old_ctx = this.task_context;
this.task_context = Some(task_context_hid);
let res = body(this);
this.task_context = old_ctx;
(params, res)
});
// `static |_task_context| -> <ret_ty> { body }`:
hir::ExprKind::Closure(self.arena.alloc(hir::Closure {
def_id: self.local_def_id(closure_node_id),
binder: hir::ClosureBinder::Default,
capture_clause,
bound_generic_params: &[],
fn_decl,
body,
fn_decl_span: self.lower_span(span),
fn_arg_span: None,
movability: Some(hir::Movability::Static),
constness: hir::Constness::NotConst,
}))
}
/// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to
/// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled.
pub(super) fn maybe_forward_track_caller(
@ -755,15 +873,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// ```
fn lower_expr_await(&mut self, await_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
let full_span = expr.span.to(await_kw_span);
match self.coroutine_kind {
Some(hir::CoroutineKind::Async(_)) => {}
let is_async_gen = match self.coroutine_kind {
Some(hir::CoroutineKind::Async(_)) => false,
Some(hir::CoroutineKind::AsyncGen(_)) => true,
Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => {
return hir::ExprKind::Err(self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
await_kw_span,
item_span: self.current_item,
}));
}
}
};
let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, None);
let gen_future_span = self.mark_span_with_reason(
DesugaringKind::Await,
@ -852,12 +973,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.stmt_expr(span, match_expr)
};
// task_context = yield ();
// Depending on `async` of `async gen`:
// async - task_context = yield ();
// async gen - task_context = yield ASYNC_GEN_PENDING;
let yield_stmt = {
let unit = self.expr_unit(span);
let yielded = if is_async_gen {
self.arena.alloc(self.expr_lang_item_path(span, hir::LangItem::AsyncGenPending))
} else {
self.expr_unit(span)
};
let yield_expr = self.expr(
span,
hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
hir::ExprKind::Yield(yielded, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
);
let yield_expr = self.arena.alloc(yield_expr);
@ -967,7 +1095,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
Some(movability)
}
Some(hir::CoroutineKind::Gen(_)) | Some(hir::CoroutineKind::Async(_)) => {
Some(
hir::CoroutineKind::Gen(_)
| hir::CoroutineKind::Async(_)
| hir::CoroutineKind::AsyncGen(_),
) => {
panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
}
None => {
@ -994,18 +1126,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
(binder, params)
}
fn lower_expr_async_closure(
fn lower_expr_coroutine_closure(
&mut self,
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
closure_hir_id: hir::HirId,
inner_closure_id: NodeId,
coroutine_kind: CoroutineKind,
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
fn_arg_span: Span,
) -> hir::ExprKind<'hir> {
let CoroutineKind::Async { closure_id: inner_closure_id, .. } = coroutine_kind else {
span_bug!(fn_decl_span, "`async gen` and `gen` closures are not supported, yet");
};
if let &ClosureBinder::For { span, .. } = binder {
self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
}
@ -1474,8 +1610,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
match self.coroutine_kind {
Some(hir::CoroutineKind::Gen(_)) => {}
let is_async_gen = match self.coroutine_kind {
Some(hir::CoroutineKind::Gen(_)) => false,
Some(hir::CoroutineKind::AsyncGen(_)) => true,
Some(hir::CoroutineKind::Async(_)) => {
return hir::ExprKind::Err(
self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span }),
@ -1491,14 +1628,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
.emit();
}
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine)
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine);
false
}
}
};
let expr =
let mut yielded =
opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
hir::ExprKind::Yield(expr, hir::YieldSource::Yield)
if is_async_gen {
// yield async_gen_ready($expr);
yielded = self.expr_call_lang_item_fn(
span,
hir::LangItem::AsyncGenReady,
std::slice::from_ref(yielded),
);
}
hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
}
/// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:

View file

@ -206,19 +206,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `impl Future<Output = T>` here because lower_body
// only cares about the input argument patterns in the function
// declaration (decl), not the return types.
let coro_kind = header.coro_kind;
let coroutine_kind = header.coroutine_kind;
let body_id = this.lower_maybe_coroutine_body(
span,
hir_id,
decl,
coro_kind,
coroutine_kind,
body.as_deref(),
);
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
this.lower_generics(generics, header.constness, id, &itctx, |this| {
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coro_kind)
this.lower_fn_decl(
decl,
id,
*fn_sig_span,
FnDeclKind::Fn,
coroutine_kind,
)
});
let sig = hir::FnSig {
decl,
@ -734,7 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig,
i.id,
FnDeclKind::Trait,
sig.header.coro_kind,
sig.header.coroutine_kind,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
}
@ -743,7 +749,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.span,
hir_id,
&sig.decl,
sig.header.coro_kind,
sig.header.coroutine_kind,
Some(body),
);
let (generics, sig) = self.lower_method_sig(
@ -751,7 +757,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig,
i.id,
FnDeclKind::Trait,
sig.header.coro_kind,
sig.header.coroutine_kind,
);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
}
@ -844,7 +850,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.span,
hir_id,
&sig.decl,
sig.header.coro_kind,
sig.header.coroutine_kind,
body.as_deref(),
);
let (generics, sig) = self.lower_method_sig(
@ -852,7 +858,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig,
i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
sig.header.coro_kind,
sig.header.coroutine_kind,
);
(generics, hir::ImplItemKind::Fn(sig, body_id))
@ -1023,17 +1029,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: Span,
fn_id: hir::HirId,
decl: &FnDecl,
coro_kind: Option<CoroutineKind>,
coroutine_kind: Option<CoroutineKind>,
body: Option<&Block>,
) -> hir::BodyId {
let (Some(coro_kind), Some(body)) = (coro_kind, body) else {
let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else {
return self.lower_fn_body_block(span, decl, body);
};
let closure_id = match coro_kind {
CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } => {
closure_id
}
};
let closure_id = coroutine_kind.closure_id();
self.lower_body(|this| {
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
@ -1200,7 +1202,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
this.expr_block(body)
};
let coroutine_expr = match coro_kind {
// FIXME(gen_blocks): Consider unifying the `make_*_expr` functions.
let coroutine_expr = match coroutine_kind {
CoroutineKind::Async { .. } => this.make_async_expr(
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
closure_id,
@ -1217,6 +1220,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::CoroutineSource::Fn,
mkbody,
),
CoroutineKind::AsyncGen { .. } => this.make_async_gen_expr(
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
closure_id,
None,
body.span,
hir::CoroutineSource::Fn,
mkbody,
),
};
let hir_id = this.lower_node_id(closure_id);
@ -1233,19 +1244,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig: &FnSig,
id: NodeId,
kind: FnDeclKind,
coro_kind: Option<CoroutineKind>,
coroutine_kind: Option<CoroutineKind>,
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header);
let itctx = ImplTraitContext::Universal;
let (generics, decl) =
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coro_kind)
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
});
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coro_kind {
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
hir::IsAsync::Async(span)
} else {
hir::IsAsync::NotAsync
@ -1371,6 +1382,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let host_param_parts = if let Const::Yes(span) = constness
&& self.tcx.features().effects
{
let span = self.lower_span(span);
let param_node_id = self.next_node_id();
let hir_id = self.next_id();
let def_id = self.create_def(

View file

@ -132,6 +132,7 @@ struct LoweringContext<'a, 'hir> {
allow_try_trait: Lrc<[Symbol]>,
allow_gen_future: Lrc<[Symbol]>,
allow_async_iterator: Lrc<[Symbol]>,
/// Mapping from generics `def_id`s to TAIT generics `def_id`s.
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
@ -176,6 +177,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
[sym::gen_future].into()
},
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
generics_def_id_map: Default::default(),
host_param_id: None,
}
@ -1675,7 +1679,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
duplicated_lifetime_node_id,
lifetime.ident.name,
DefKind::LifetimeParam,
lifetime.ident.span,
self.lower_span(lifetime.ident.span),
);
captured_to_synthesized_mapping.insert(old_def_id, duplicated_lifetime_def_id);
// FIXME: Instead of doing this, we could move this whole loop
@ -1684,7 +1688,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
synthesized_lifetime_definitions.push((
duplicated_lifetime_node_id,
duplicated_lifetime_def_id,
lifetime.ident,
self.lower_ident(lifetime.ident),
));
// Now make an arg that we can use for the generic params of the opaque tykind.
@ -1900,13 +1904,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn_span: Span,
) -> hir::FnRetTy<'hir> {
let span = self.lower_span(fn_span);
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
let opaque_ty_node_id = match coro {
CoroutineKind::Async { return_impl_trait_id, .. }
| CoroutineKind::Gen { return_impl_trait_id, .. } => return_impl_trait_id,
let (opaque_ty_node_id, allowed_features) = match coro {
CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None),
CoroutineKind::AsyncGen { return_impl_trait_id, .. } => {
(return_impl_trait_id, Some(self.allow_async_iterator.clone()))
}
};
let opaque_ty_span =
self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
let captured_lifetimes: Vec<_> = self
.resolver
.take_extra_lifetime_params(opaque_ty_node_id)
@ -1925,7 +1934,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let bound = this.lower_coroutine_fn_output_type_to_bound(
output,
coro,
span,
opaque_ty_span,
ImplTraitContext::ReturnPositionOpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
fn_kind,
@ -1944,7 +1953,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
output: &FnRetTy,
coro: CoroutineKind,
span: Span,
opaque_ty_span: Span,
nested_impl_trait_context: ImplTraitContext,
) -> hir::GenericBound<'hir> {
// Compute the `T` in `Future<Output = T>` from the return type.
@ -1960,20 +1969,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// "<$assoc_ty_name = T>"
let (assoc_ty_name, trait_lang_item) = match coro {
CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future),
CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator),
CoroutineKind::Async { .. } => (sym::Output, hir::LangItem::Future),
CoroutineKind::Gen { .. } => (sym::Item, hir::LangItem::Iterator),
CoroutineKind::AsyncGen { .. } => (sym::Item, hir::LangItem::AsyncIterator),
};
let future_args = self.arena.alloc(hir::GenericArgs {
args: &[],
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, span, output_ty)],
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, opaque_ty_span, output_ty)],
parenthesized: hir::GenericArgsParentheses::No,
span_ext: DUMMY_SP,
});
hir::GenericBound::LangItemTrait(
trait_lang_item,
self.lower_span(span),
opaque_ty_span,
self.next_id(),
future_args,
)
@ -2243,7 +2253,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
match c.value.kind {
ExprKind::Underscore => {
if self.tcx.features().generic_arg_infer {
hir::ArrayLen::Infer(self.lower_node_id(c.id), c.value.span)
hir::ArrayLen::Infer(self.lower_node_id(c.id), self.lower_span(c.value.span))
} else {
feature_err(
&self.tcx.sess.parse_sess,

View file

@ -389,7 +389,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
};
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
let binding = self.assoc_ty_binding(hir::FN_OUTPUT_NAME, output_ty.span, output_ty);
let binding = self.assoc_ty_binding(sym::Output, output_ty.span, output_ty);
(
GenericArgsCtor {
args,

View file

@ -1271,14 +1271,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// Functions cannot both be `const async` or `const gen`
if let Some(&FnHeader {
constness: Const::Yes(cspan),
coro_kind:
Some(
CoroutineKind::Async { span: aspan, .. }
| CoroutineKind::Gen { span: aspan, .. },
),
coroutine_kind: Some(coroutine_kind),
..
}) = fk.header()
{
let aspan = match coroutine_kind {
CoroutineKind::Async { span: aspan, .. }
| CoroutineKind::Gen { span: aspan, .. }
| CoroutineKind::AsyncGen { span: aspan, .. } => aspan,
};
// FIXME(gen_blocks): Report a different error for `const gen`
self.err_handler().emit_err(errors::ConstAndAsync {
spans: vec![cspan, aspan],

View file

@ -1490,14 +1490,18 @@ impl<'a> State<'a> {
}
}
fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) {
match coro_kind {
fn print_coroutine_kind(&mut self, coroutine_kind: ast::CoroutineKind) {
match coroutine_kind {
ast::CoroutineKind::Gen { .. } => {
self.word_nbsp("gen");
}
ast::CoroutineKind::Async { .. } => {
self.word_nbsp("async");
}
ast::CoroutineKind::AsyncGen { .. } => {
self.word_nbsp("async");
self.word_nbsp("gen");
}
}
}
@ -1690,7 +1694,7 @@ impl<'a> State<'a> {
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
self.print_constness(header.constness);
header.coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
header.coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
self.print_unsafety(header.unsafety);
match header.ext {

View file

@ -413,7 +413,7 @@ impl<'a> State<'a> {
binder,
capture_clause,
constness,
coro_kind,
coroutine_kind,
movability,
fn_decl,
body,
@ -423,7 +423,7 @@ impl<'a> State<'a> {
self.print_closure_binder(binder);
self.print_constness(*constness);
self.print_movability(*movability);
coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
self.print_capture_clause(*capture_clause);
self.print_fn_params_and_ret(fn_decl, true);
@ -631,28 +631,33 @@ impl<'a> State<'a> {
self.print_expr(e);
self.space();
}
self.word_space("=>");
match &arm.body.kind {
ast::ExprKind::Block(blk, opt_label) => {
if let Some(label) = opt_label {
self.print_ident(label.ident);
self.word_space(":");
if let Some(body) = &arm.body {
self.word_space("=>");
match &body.kind {
ast::ExprKind::Block(blk, opt_label) => {
if let Some(label) = opt_label {
self.print_ident(label.ident);
self.word_space(":");
}
// The block will close the pattern's ibox.
self.print_block_unclosed_indent(blk);
// If it is a user-provided unsafe block, print a comma after it.
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
self.word(",");
}
}
// The block will close the pattern's ibox.
self.print_block_unclosed_indent(blk);
// If it is a user-provided unsafe block, print a comma after it.
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
_ => {
self.end(); // Close the ibox for the pattern.
self.print_expr(body);
self.word(",");
}
}
_ => {
self.end(); // Close the ibox for the pattern.
self.print_expr(&arm.body);
self.word(",");
}
} else {
self.word(",");
}
self.end(); // Close enclosing cbox.
}

View file

@ -11,101 +11,88 @@ use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::ResultsVisitable;
use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
use rustc_mir_dataflow::{Analysis, Direction, Results};
use rustc_mir_dataflow::{Analysis, AnalysisDomain, Results};
use std::fmt;
use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
/// A tuple with named fields that can hold either the results or the transient state of the
/// dataflow analyses used by the borrow checker.
#[derive(Debug)]
pub struct BorrowckAnalyses<B, U, E> {
pub borrows: B,
pub uninits: U,
pub ever_inits: E,
}
/// The results of the dataflow analyses used by the borrow checker.
pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses<
Results<'tcx, Borrows<'mir, 'tcx>>,
Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
>;
pub struct BorrowckResults<'mir, 'tcx> {
pub(crate) borrows: Results<'tcx, Borrows<'mir, 'tcx>>,
pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
}
/// The transient state of the dataflow analyses used by the borrow checker.
pub type BorrowckFlowState<'mir, 'tcx> =
<BorrowckResults<'mir, 'tcx> as ResultsVisitable<'tcx>>::FlowState;
macro_rules! impl_visitable {
( $(
$T:ident { $( $field:ident : $A:ident ),* $(,)? }
)* ) => { $(
impl<'tcx, $($A),*, D: Direction> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*>
where
$( $A: Analysis<'tcx, Direction = D>, )*
{
type Direction = D;
type FlowState = $T<$( $A::Domain ),*>;
fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
$T {
$( $field: self.$field.analysis.bottom_value(body) ),*
}
}
fn reset_to_block_entry(
&self,
state: &mut Self::FlowState,
block: BasicBlock,
) {
$( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )*
}
fn reconstruct_before_statement_effect(
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
$( self.$field.analysis
.apply_before_statement_effect(&mut state.$field, stmt, loc); )*
}
fn reconstruct_statement_effect(
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
$( self.$field.analysis
.apply_statement_effect(&mut state.$field, stmt, loc); )*
}
fn reconstruct_before_terminator_effect(
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
$( self.$field.analysis
.apply_before_terminator_effect(&mut state.$field, term, loc); )*
}
fn reconstruct_terminator_effect(
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
$( self.$field.analysis
.apply_terminator_effect(&mut state.$field, term, loc); )*
}
}
)* }
#[derive(Debug)]
pub struct BorrowckFlowState<'mir, 'tcx> {
pub(crate) borrows: <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
pub(crate) uninits: <MaybeUninitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
pub(crate) ever_inits: <EverInitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
}
impl_visitable! {
BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E }
impl<'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'mir, 'tcx> {
// All three analyses are forward, but we have to use just one here.
type Direction = <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction;
type FlowState = BorrowckFlowState<'mir, 'tcx>;
fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
BorrowckFlowState {
borrows: self.borrows.analysis.bottom_value(body),
uninits: self.uninits.analysis.bottom_value(body),
ever_inits: self.ever_inits.analysis.bottom_value(body),
}
}
fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
state.borrows.clone_from(&self.borrows.entry_set_for_block(block));
state.uninits.clone_from(&self.uninits.entry_set_for_block(block));
state.ever_inits.clone_from(&self.ever_inits.entry_set_for_block(block));
}
fn reconstruct_before_statement_effect(
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_before_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.analysis.apply_before_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.analysis.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
}
fn reconstruct_statement_effect(
&mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.analysis.apply_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.analysis.apply_statement_effect(&mut state.ever_inits, stmt, loc);
}
fn reconstruct_before_terminator_effect(
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_before_terminator_effect(&mut state.borrows, term, loc);
self.uninits.analysis.apply_before_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.analysis.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
}
fn reconstruct_terminator_effect(
&mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_terminator_effect(&mut state.borrows, term, loc);
self.uninits.analysis.apply_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.analysis.apply_terminator_effect(&mut state.ever_inits, term, loc);
}
}
rustc_index::newtype_index! {
@ -598,7 +585,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
fn before_terminator_effect(
&mut self,
trans: &mut impl GenKill<Self::Idx>,
trans: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
) {
@ -625,7 +612,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
fn call_return_effect(
&mut self,
_trans: &mut impl GenKill<Self::Idx>,
_trans: &mut Self::Domain,
_block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {

View file

@ -2517,12 +2517,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
CoroutineKind::Gen(kind) => match kind {
CoroutineSource::Block => "gen block",
CoroutineSource::Closure => "gen closure",
_ => bug!("gen block/closure expected, but gen function found."),
CoroutineSource::Fn => {
bug!("gen block/closure expected, but gen function found.")
}
},
CoroutineKind::AsyncGen(kind) => match kind {
CoroutineSource::Block => "async gen block",
CoroutineSource::Closure => "async gen closure",
CoroutineSource::Fn => {
bug!("gen block/closure expected, but gen function found.")
}
},
CoroutineKind::Async(async_kind) => match async_kind {
CoroutineSource::Block => "async block",
CoroutineSource::Closure => "async closure",
_ => bug!("async block/closure expected, but async function found."),
CoroutineSource::Fn => {
bug!("async block/closure expected, but async function found.")
}
},
CoroutineKind::Coroutine => "coroutine",
},

View file

@ -684,7 +684,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
};
let mir_description = match hir.body(body).coroutine_kind {
Some(hir::CoroutineKind::Async(gen)) => match gen {
Some(hir::CoroutineKind::Async(src)) => match src {
hir::CoroutineSource::Block => " of async block",
hir::CoroutineSource::Closure => " of async closure",
hir::CoroutineSource::Fn => {
@ -701,7 +701,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
" of async function"
}
},
Some(hir::CoroutineKind::Gen(gen)) => match gen {
Some(hir::CoroutineKind::Gen(src)) => match src {
hir::CoroutineSource::Block => " of gen block",
hir::CoroutineSource::Closure => " of gen closure",
hir::CoroutineSource::Fn => {
@ -715,6 +715,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
" of gen function"
}
},
Some(hir::CoroutineKind::AsyncGen(src)) => match src {
hir::CoroutineSource::Block => " of async gen block",
hir::CoroutineSource::Closure => " of async gen closure",
hir::CoroutineSource::Fn => {
let parent_item =
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
let output = &parent_item
.fn_decl()
.expect("coroutine lowered from async gen fn should be in fn")
.output;
span = output.span();
" of async gen function"
}
},
Some(hir::CoroutineKind::Coroutine) => " of coroutine",
None => " of closure",
};

View file

@ -136,7 +136,7 @@ fn cs_partial_cmp(
&& let Some(last) = arms.last_mut()
&& let PatKind::Wild = last.pat.kind
{
last.body = expr2;
last.body = Some(expr2);
expr1
} else {
let eq_arm = cx.arm(

View file

@ -541,12 +541,30 @@ fn check_test_signature(
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
}
if let Some(ast::CoroutineKind::Async { span, .. }) = f.sig.header.coro_kind {
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" }));
}
if let Some(ast::CoroutineKind::Gen { span, .. }) = f.sig.header.coro_kind {
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "gen" }));
if let Some(coroutine_kind) = f.sig.header.coroutine_kind {
match coroutine_kind {
ast::CoroutineKind::Async { span, .. } => {
return Err(sd.emit_err(errors::TestBadFn {
span: i.span,
cause: span,
kind: "async",
}));
}
ast::CoroutineKind::Gen { span, .. } => {
return Err(sd.emit_err(errors::TestBadFn {
span: i.span,
cause: span,
kind: "gen",
}));
}
ast::CoroutineKind::AsyncGen { span, .. } => {
return Err(sd.emit_err(errors::TestBadFn {
span: i.span,
cause: span,
kind: "async gen",
}));
}
}
}
// If the termination trait is active, the compiler will check that the output

View file

@ -1,5 +1,6 @@
//! Codegen `extern "platform-intrinsic"` intrinsics.
use cranelift_codegen::ir::immediates::Offset32;
use rustc_middle::ty::GenericArgsRef;
use rustc_span::Symbol;
use rustc_target::abi::Endian;
@ -1008,8 +1009,57 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
}
}
sym::simd_masked_load => {
intrinsic_args!(fx, args => (mask, ptr, val); intrinsic);
let (val_lane_count, val_lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx);
let (mask_lane_count, _mask_lane_ty) = mask.layout().ty.simd_size_and_type(fx.tcx);
let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
assert_eq!(val_lane_count, mask_lane_count);
assert_eq!(val_lane_count, ret_lane_count);
let lane_clif_ty = fx.clif_type(val_lane_ty).unwrap();
let ret_lane_layout = fx.layout_of(ret_lane_ty);
let ptr_val = ptr.load_scalar(fx);
for lane_idx in 0..ret_lane_count {
let val_lane = val.value_lane(fx, lane_idx).load_scalar(fx);
let mask_lane = mask.value_lane(fx, lane_idx).load_scalar(fx);
let if_enabled = fx.bcx.create_block();
let if_disabled = fx.bcx.create_block();
let next = fx.bcx.create_block();
let res_lane = fx.bcx.append_block_param(next, lane_clif_ty);
fx.bcx.ins().brif(mask_lane, if_enabled, &[], if_disabled, &[]);
fx.bcx.seal_block(if_enabled);
fx.bcx.seal_block(if_disabled);
fx.bcx.switch_to_block(if_enabled);
let offset = lane_idx as i32 * lane_clif_ty.bytes() as i32;
let res = fx.bcx.ins().load(
lane_clif_ty,
MemFlags::trusted(),
ptr_val,
Offset32::new(offset),
);
fx.bcx.ins().jump(next, &[res]);
fx.bcx.switch_to_block(if_disabled);
fx.bcx.ins().jump(next, &[val_lane]);
fx.bcx.seal_block(next);
fx.bcx.switch_to_block(next);
fx.bcx.ins().nop();
ret.place_lane(fx, lane_idx)
.write_cvalue(fx, CValue::by_val(res_lane, ret_lane_layout));
}
}
sym::simd_scatter => {
intrinsic_args!(fx, args => (val, ptr, mask); intrinsic);
intrinsic_args!(fx, args => (mask, ptr, val); intrinsic);
let (val_lane_count, _val_lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx);
let (ptr_lane_count, _ptr_lane_ty) = ptr.layout().ty.simd_size_and_type(fx.tcx);

View file

@ -569,5 +569,6 @@ fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic,
TlsModel::InitialExec => gccjit::TlsModel::InitialExec,
TlsModel::LocalExec => gccjit::TlsModel::LocalExec,
TlsModel::Emulated => gccjit::TlsModel::GlobalDynamic,
}
}

View file

@ -39,7 +39,7 @@ impl OwnedTargetMachine {
split_dwarf_file: &CStr,
output_obj_file: &CStr,
debug_info_compression: &CStr,
force_emulated_tls: bool,
use_emulated_tls: bool,
args_cstr_buff: &[u8],
) -> Result<Self, LlvmError<'static>> {
assert!(args_cstr_buff.len() > 0);
@ -71,7 +71,7 @@ impl OwnedTargetMachine {
split_dwarf_file.as_ptr(),
output_obj_file.as_ptr(),
debug_info_compression.as_ptr(),
force_emulated_tls,
use_emulated_tls,
args_cstr_buff.as_ptr() as *const c_char,
args_cstr_buff.len(),
)

View file

@ -33,7 +33,7 @@ use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, Switc
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo};
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
use crate::llvm::diagnostic::OptimizationDiagnosticKind;
use libc::{c_char, c_int, c_uint, c_void, size_t};
@ -223,7 +223,7 @@ pub fn target_machine_factory(
let path_mapping = sess.source_map().path_mapping().clone();
let force_emulated_tls = sess.target.force_emulated_tls;
let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);
// copy the exe path, followed by path all into one buffer
// null terminating them so we can use them as null terminated strings
@ -297,7 +297,7 @@ pub fn target_machine_factory(
&split_dwarf_file,
&output_obj_file,
&debuginfo_compression,
force_emulated_tls,
use_emulated_tls,
&args_cstr_buff,
)
})

View file

@ -120,6 +120,7 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic,
TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec,
TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec,
TlsModel::Emulated => llvm::ThreadLocalMode::GeneralDynamic,
}
}

View file

@ -100,6 +100,9 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
let Coverage { kind } = coverage;
match *kind {
// Span markers are only meaningful during MIR instrumentation,
// and have no effect during codegen.
CoverageKind::SpanMarker => {}
CoverageKind::CounterIncrement { id } => {
func_coverage.mark_counter_id_seen(id);
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,

View file

@ -1492,6 +1492,198 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
return Ok(v);
}
if name == sym::simd_masked_load {
// simd_masked_load(mask: <N x i{M}>, pointer: *_ T, values: <N x T>) -> <N x T>
// * N: number of elements in the input vectors
// * T: type of the element to load
// * M: any integer width is supported, will be truncated to i1
// Loads contiguous elements from memory behind `pointer`, but only for
// those lanes whose `mask` bit is enabled.
// The memory addresses corresponding to the “off” lanes are not accessed.
// The element type of the "mask" argument must be a signed integer type of any width
let mask_ty = in_ty;
let (mask_len, mask_elem) = (in_len, in_elem);
// The second argument must be a pointer matching the element type
let pointer_ty = arg_tys[1];
// The last argument is a passthrough vector providing values for disabled lanes
let values_ty = arg_tys[2];
let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
require_simd!(ret_ty, SimdReturn);
// Of the same length:
require!(
values_len == mask_len,
InvalidMonomorphization::ThirdArgumentLength {
span,
name,
in_len: mask_len,
in_ty: mask_ty,
arg_ty: values_ty,
out_len: values_len
}
);
// The return type must match the last argument type
require!(
ret_ty == values_ty,
InvalidMonomorphization::ExpectedReturnType { span, name, in_ty: values_ty, ret_ty }
);
require!(
matches!(
pointer_ty.kind(),
ty::RawPtr(p) if p.ty == values_elem && p.ty.kind() == values_elem.kind()
),
InvalidMonomorphization::ExpectedElementType {
span,
name,
expected_element: values_elem,
second_arg: pointer_ty,
in_elem: values_elem,
in_ty: values_ty,
mutability: ExpectedPointerMutability::Not,
}
);
require!(
matches!(mask_elem.kind(), ty::Int(_)),
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: values_elem,
third_arg: mask_ty,
}
);
// Alignment of T, must be a constant integer value:
let alignment_ty = bx.type_i32();
let alignment = bx.const_i32(bx.align_of(values_ty).bytes() as i32);
// Truncate the mask vector to a vector of i1s:
let (mask, mask_ty) = {
let i1 = bx.type_i1();
let i1xn = bx.type_vector(i1, mask_len);
(bx.trunc(args[0].immediate(), i1xn), i1xn)
};
let llvm_pointer = bx.type_ptr();
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
let llvm_intrinsic = format!("llvm.masked.load.{llvm_elem_vec_str}.p0");
let fn_ty = bx
.type_func(&[llvm_pointer, alignment_ty, mask_ty, llvm_elem_vec_ty], llvm_elem_vec_ty);
let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
let v = bx.call(
fn_ty,
None,
None,
f,
&[args[1].immediate(), alignment, mask, args[2].immediate()],
None,
);
return Ok(v);
}
if name == sym::simd_masked_store {
// simd_masked_store(mask: <N x i{M}>, pointer: *mut T, values: <N x T>) -> ()
// * N: number of elements in the input vectors
// * T: type of the element to load
// * M: any integer width is supported, will be truncated to i1
// Stores contiguous elements to memory behind `pointer`, but only for
// those lanes whose `mask` bit is enabled.
// The memory addresses corresponding to the “off” lanes are not accessed.
// The element type of the "mask" argument must be a signed integer type of any width
let mask_ty = in_ty;
let (mask_len, mask_elem) = (in_len, in_elem);
// The second argument must be a pointer matching the element type
let pointer_ty = arg_tys[1];
// The last argument specifies the values to store to memory
let values_ty = arg_tys[2];
let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
// Of the same length:
require!(
values_len == mask_len,
InvalidMonomorphization::ThirdArgumentLength {
span,
name,
in_len: mask_len,
in_ty: mask_ty,
arg_ty: values_ty,
out_len: values_len
}
);
// The second argument must be a mutable pointer type matching the element type
require!(
matches!(
pointer_ty.kind(),
ty::RawPtr(p) if p.ty == values_elem && p.ty.kind() == values_elem.kind() && p.mutbl.is_mut()
),
InvalidMonomorphization::ExpectedElementType {
span,
name,
expected_element: values_elem,
second_arg: pointer_ty,
in_elem: values_elem,
in_ty: values_ty,
mutability: ExpectedPointerMutability::Mut,
}
);
require!(
matches!(mask_elem.kind(), ty::Int(_)),
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: values_elem,
third_arg: mask_ty,
}
);
// Alignment of T, must be a constant integer value:
let alignment_ty = bx.type_i32();
let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
// Truncate the mask vector to a vector of i1s:
let (mask, mask_ty) = {
let i1 = bx.type_i1();
let i1xn = bx.type_vector(i1, in_len);
(bx.trunc(args[0].immediate(), i1xn), i1xn)
};
let ret_t = bx.type_void();
let llvm_pointer = bx.type_ptr();
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
let llvm_intrinsic = format!("llvm.masked.store.{llvm_elem_vec_str}.p0");
let fn_ty = bx.type_func(&[llvm_elem_vec_ty, llvm_pointer, alignment_ty, mask_ty], ret_t);
let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
let v = bx.call(
fn_ty,
None,
None,
f,
&[args[2].immediate(), args[1].immediate(), alignment, mask],
None,
);
return Ok(v);
}
if name == sym::simd_scatter {
// simd_scatter(values: <N x T>, pointers: <N x *mut T>,
// mask: <N x i{M}>) -> ()

View file

@ -306,7 +306,9 @@ impl CodegenBackend for LlvmCodegenBackend {
}
PrintKind::TlsModels => {
writeln!(out, "Available TLS models:");
for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] {
for name in
&["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"]
{
writeln!(out, " {name}");
}
writeln!(out);

View file

@ -2159,7 +2159,7 @@ extern "C" {
SplitDwarfFile: *const c_char,
OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char,
ForceEmulatedTls: bool,
UseEmulatedTls: bool,
ArgsCstrBuff: *const c_char,
ArgsCstrBuffLen: usize,
) -> *mut TargetMachine;

View file

@ -263,6 +263,10 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> {
"sve2-bitperm",
TargetFeatureFoldStrength::EnableOnly("neon"),
),
// The unaligned-scalar-mem feature was renamed to fast-unaligned-access.
("riscv32" | "riscv64", "fast-unaligned-access") if get_version().0 <= 17 => {
LLVMFeature::new("unaligned-scalar-mem")
}
(_, s) => LLVMFeature::new(s),
}
}

View file

@ -1748,7 +1748,9 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) {
symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
tcx, symbol, cnum,
));
}
});

View file

@ -16,7 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OomStrategy};
use rustc_target::spec::SanitizerSet;
use rustc_target::spec::{SanitizerSet, TlsModel};
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
crates_export_threshold(tcx.crate_types())
@ -564,6 +564,12 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
// thread local will not be a function call,
// so it is safe to return before windows symbol decoration check.
if let Some(name) = maybe_emutls_symbol_name(tcx, symbol, &undecorated) {
return name;
}
let target = &tcx.sess.target;
if !target.is_like_windows {
// Mach-O has a global "_" suffix and `object` crate will handle it.
@ -624,6 +630,32 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
}
pub fn exporting_symbol_name_for_instance_in_crate<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
cnum: CrateNum,
) -> String {
let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, cnum);
maybe_emutls_symbol_name(tcx, symbol, &undecorated).unwrap_or(undecorated)
}
fn maybe_emutls_symbol_name<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
undecorated: &str,
) -> Option<String> {
if matches!(tcx.sess.tls_model(), TlsModel::Emulated)
&& let ExportedSymbol::NonGeneric(def_id) = symbol
&& tcx.is_thread_local_static(def_id)
{
// When using emutls, LLVM will add the `__emutls_v.` prefix to thread local symbols,
// and exported symbol name need to match this.
Some(format!("__emutls_v.{undecorated}"))
} else {
None
}
}
fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
// Build up a map from DefId to a `NativeLib` structure, where
// `NativeLib` internally contains information about

View file

@ -566,6 +566,9 @@ fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block",
Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure",
Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn",
Some(CoroutineKind::AsyncGen(CoroutineSource::Block)) => "async_gen_block",
Some(CoroutineKind::AsyncGen(CoroutineSource::Closure)) => "async_gen_closure",
Some(CoroutineKind::AsyncGen(CoroutineSource::Fn)) => "async_gen_fn",
Some(CoroutineKind::Coroutine) => "coroutine",
None => "closure",
}

View file

@ -43,7 +43,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let local = mir::Local::from_usize(local);
let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
if expected_ty != op.layout.ty {
warn!("Unexpected initial operand type. See the issues/114858");
warn!(
"Unexpected initial operand type: expected {expected_ty:?}, found {:?}.\
See <https://github.com/rust-lang/rust/issues/114858>.",
op.layout.ty
);
}
}
}

View file

@ -291,9 +291,9 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[
("d", Unstable(sym::riscv_target_feature)),
("e", Unstable(sym::riscv_target_feature)),
("f", Unstable(sym::riscv_target_feature)),
("fast-unaligned-access", Unstable(sym::riscv_target_feature)),
("m", Stable),
("relax", Unstable(sym::riscv_target_feature)),
("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)),
("v", Unstable(sym::riscv_target_feature)),
("zba", Stable),
("zbb", Stable),

View file

@ -22,7 +22,7 @@ use std::mem;
use std::ops::{ControlFlow, Deref};
use super::ops::{self, NonConstOp, Status};
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
use super::resolver::FlowSensitiveAnalysis;
use super::{ConstCx, Qualif};
use crate::const_eval::is_unstable_const_fn;
@ -35,7 +35,7 @@ type QualifResults<'mir, 'tcx, Q> =
pub struct Qualifs<'mir, 'tcx> {
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
// needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
}
impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
@ -78,27 +78,25 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
local: Local,
location: Location,
) -> bool {
// FIXME(effects) replace with `NeedsNonconstDrop` after const traits work again
/*
let ty = ccx.body.local_decls[local].ty;
if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
// Peeking into opaque types causes cycles if the current function declares said opaque
// type. Thus we avoid short circuiting on the type and instead run the more expensive
// analysis that looks at the actual usage within this function
if !ty.has_opaque_types() && !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
return false;
}
let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| {
let ConstCx { tcx, body, .. } = *ccx;
FlowSensitiveAnalysis::new(NeedsDrop, ccx)
.into_engine(tcx, &body)
FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
.into_engine(tcx, body)
.iterate_to_fixpoint()
.into_results_cursor(&body)
.into_results_cursor(body)
});
needs_non_const_drop.seek_before_primary_effect(location);
needs_non_const_drop.get().contains(local)
*/
self.needs_drop(ccx, local, location)
}
/// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
@ -1013,9 +1011,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let mut err_span = self.span;
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
// FIXME(effects) replace with `NeedsNonConstDrop` once we fix const traits
let ty_needs_non_const_drop =
qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);

View file

@ -5,7 +5,7 @@ use rustc_span::{symbol::sym, Span};
use super::check::Qualifs;
use super::ops::{self, NonConstOp};
use super::qualifs::{NeedsDrop, Qualif};
use super::qualifs::{NeedsNonConstDrop, Qualif};
use super::ConstCx;
/// Returns `true` if we should use the more precise live drop checker that runs after drop
@ -83,8 +83,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
// FIXME(effects) use `NeedsNonConstDrop`
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
// Instead of throwing a bug, we just return here. This is because we have to
// run custom `const Drop` impls.
return;

View file

@ -23,8 +23,7 @@ pub fn in_any_value_of_ty<'tcx>(
ConstQualifs {
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
// FIXME(effects)
needs_non_const_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
tainted_by_errors,
}
@ -155,12 +154,27 @@ impl Qualif for NeedsNonConstDrop {
return false;
}
// FIXME(effects) constness
// FIXME(effects): If `destruct` is not a `const_trait`,
// or effects are disabled in this crate, then give up.
let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
if cx.tcx.generics_of(destruct_def_id).host_effect_index.is_none()
|| !cx.tcx.features().effects
{
return NeedsDrop::in_any_value_of_ty(cx, ty);
}
let obligation = Obligation::new(
cx.tcx,
ObligationCause::dummy_with_span(cx.body.span),
cx.param_env,
ty::TraitRef::from_lang_item(cx.tcx, LangItem::Destruct, cx.body.span, [ty]),
ty::TraitRef::new(
cx.tcx,
destruct_def_id,
[
ty::GenericArg::from(ty),
ty::GenericArg::from(cx.tcx.expected_const_effect_param_for_body(cx.def_id())),
],
),
);
let infcx = cx.tcx.infer_ctxt().build();

View file

@ -52,7 +52,7 @@ fn default_client() -> Client {
static GLOBAL_CLIENT_CHECKED: OnceLock<Client> = OnceLock::new();
pub fn check(report_warning: impl FnOnce(&'static str)) {
pub fn initialize_checked(report_warning: impl FnOnce(&'static str)) {
let client_checked = match &*GLOBAL_CLIENT {
Ok(client) => client.clone(),
Err(e) => {

View file

@ -38,6 +38,7 @@ rustc_mir_transform = { path = "../rustc_mir_transform" }
rustc_monomorphize = { path = "../rustc_monomorphize" }
rustc_parse = { path = "../rustc_parse" }
rustc_passes = { path = "../rustc_passes" }
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
rustc_privacy = { path = "../rustc_privacy" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_resolve = { path = "../rustc_resolve" }

View file

@ -128,6 +128,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
rustc_parse::DEFAULT_LOCALE_RESOURCE,
rustc_passes::DEFAULT_LOCALE_RESOURCE,
rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE,
rustc_privacy::DEFAULT_LOCALE_RESOURCE,
rustc_query_system::DEFAULT_LOCALE_RESOURCE,
rustc_resolve::DEFAULT_LOCALE_RESOURCE,

View file

@ -591,17 +591,18 @@ impl Diagnostic {
pub fn multipart_suggestion_with_style(
&mut self,
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
mut suggestion: Vec<(Span, String)>,
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
let mut parts = suggestion
suggestion.sort_unstable();
suggestion.dedup();
let parts = suggestion
.into_iter()
.map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect::<Vec<_>>();
parts.sort_unstable_by_key(|part| part.span);
assert!(!parts.is_empty());
debug_assert_eq!(
parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),

View file

@ -71,6 +71,8 @@ expand_macro_const_stability =
.label = invalid const stability attribute
.label2 = const stability attribute affects this macro
expand_macro_expands_to_match_arm = macros cannot expand to match arms
expand_malformed_feature_attribute =
malformed `feature` attribute input
.expected = expected just one word

View file

@ -505,7 +505,7 @@ impl<'a> ExtCtxt<'a> {
attrs: AttrVec::new(),
pat,
guard: None,
body: expr,
body: Some(expr),
span,
id: ast::DUMMY_NODE_ID,
is_placeholder: false,
@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> {
binder: ast::ClosureBinder::NotPresent,
capture_clause: ast::CaptureBy::Ref,
constness: ast::Const::No,
coro_kind: None,
coroutine_kind: None,
movability: ast::Movability::Movable,
fn_decl,
body,

View file

@ -304,6 +304,8 @@ pub(crate) struct IncompleteParse<'a> {
pub label_span: Span,
pub macro_path: &'a ast::Path,
pub kind_name: &'a str,
#[note(expand_macro_expands_to_match_arm)]
pub expands_to_match_arm: Option<()>,
#[suggestion(
expand_suggestion_add_semi,

View file

@ -955,12 +955,15 @@ pub fn ensure_complete_parse<'a>(
_ => None,
};
let expands_to_match_arm = kind_name == "pattern" && parser.token == token::FatArrow;
parser.sess.emit_err(IncompleteParse {
span: def_site_span,
token,
label_span: span,
macro_path,
kind_name,
expands_to_match_arm: expands_to_match_arm.then_some(()),
add_semicolon,
});
}

View file

@ -119,7 +119,7 @@ pub fn placeholder(
}]),
AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm {
attrs: Default::default(),
body: expr_placeholder(),
body: Some(expr_placeholder()),
guard: None,
id,
pat: pat(),

View file

@ -1056,6 +1056,23 @@ impl<'hir> Pat<'hir> {
true
})
}
/// Whether this a never pattern.
pub fn is_never_pattern(&self) -> bool {
let mut is_never_pattern = false;
self.walk(|pat| match &pat.kind {
PatKind::Never => {
is_never_pattern = true;
false
}
PatKind::Or(s) => {
is_never_pattern = s.iter().all(|p| p.is_never_pattern());
false
}
_ => true,
});
is_never_pattern
}
}
/// A single field in a struct pattern.
@ -1339,12 +1356,16 @@ impl<'hir> Body<'hir> {
/// The type of source expression that caused this coroutine to be created.
#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)]
pub enum CoroutineKind {
/// An explicit `async` block or the body of an async function.
/// An explicit `async` block or the body of an `async` function.
Async(CoroutineSource),
/// An explicit `gen` block or the body of a `gen` function.
Gen(CoroutineSource),
/// An explicit `async gen` block or the body of an `async gen` function,
/// which is able to both `yield` and `.await`.
AsyncGen(CoroutineSource),
/// A coroutine literal created via a `yield` inside a closure.
Coroutine,
}
@ -1369,6 +1390,14 @@ impl fmt::Display for CoroutineKind {
}
k.fmt(f)
}
CoroutineKind::AsyncGen(k) => {
if f.alternate() {
f.write_str("`async gen` ")?;
} else {
f.write_str("async gen ")?
}
k.fmt(f)
}
}
}
}
@ -2064,17 +2093,6 @@ impl fmt::Display for YieldSource {
}
}
impl From<CoroutineKind> for YieldSource {
fn from(kind: CoroutineKind) -> Self {
match kind {
// Guess based on the kind of the current coroutine.
CoroutineKind::Coroutine => Self::Yield,
CoroutineKind::Async(_) => Self::Await { expr: None },
CoroutineKind::Gen(_) => Self::Yield,
}
}
}
// N.B., if you change this, you'll probably want to change the corresponding
// type structure in middle/ty.rs as well.
#[derive(Debug, Clone, Copy, HashStable_Generic)]
@ -2254,11 +2272,6 @@ pub enum ImplItemKind<'hir> {
Type(&'hir Ty<'hir>),
}
/// The name of the associated type for `Fn` return types.
pub const FN_OUTPUT_NAME: Symbol = sym::Output;
/// The name of the associated type for `Iterator` item types.
pub const ITERATOR_ITEM_NAME: Symbol = sym::Item;
/// Bind a type to an associated type (i.e., `A = Foo`).
///
/// Bindings like `A: Debug` are represented as a special type `A =

View file

@ -212,6 +212,7 @@ language_item_table! {
Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0);
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0);
CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None;
Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1);
Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
@ -294,6 +295,10 @@ language_item_table! {
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
AsyncGenReady, sym::AsyncGenReady, async_gen_ready, Target::Method(MethodKind::Inherent), GenericRequirement::Exact(1);
AsyncGenPending, sym::AsyncGenPending, async_gen_pending, Target::AssocConst, GenericRequirement::Exact(1);
AsyncGenFinished, sym::AsyncGenFinished, async_gen_finished, Target::AssocConst, GenericRequirement::Exact(1);
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;

View file

@ -1,8 +1,28 @@
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}`
.label = ambiguous associated {$assoc_kind} `{$assoc_name}`
hir_analysis_ambiguous_lifetime_bound =
ambiguous lifetime bound, explicit lifetime bound required
hir_analysis_assoc_bound_on_const = expected associated type, found {$descr}
.note = trait bounds not allowed on {$descr}
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`
hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
[true] an
*[false] a similarly named
} associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind}
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name
hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
.label = unexpected {$got}
.expected_because_label = expected a {$expected} because of this associated {$expected}
.note = the associated {$assoc_kind} is defined here
.bound_on_assoc_const_label = bounds are not allowed on associated constants
hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here
hir_analysis_assoc_type_binding_not_allowed =
associated type bindings are not allowed here
@ -280,10 +300,6 @@ hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is no
hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`
hir_analysis_return_type_notation_conflicting_bound =
ambiguous associated function `{$assoc_name}` for `{$ty_name}`
.note = `{$assoc_name}` is declared in two supertraits: `{$first_bound}` and `{$second_bound}`
hir_analysis_return_type_notation_equality_bound =
return type notation is not allowed to use type equality
@ -294,9 +310,6 @@ hir_analysis_return_type_notation_illegal_param_type =
return type notation is not allowed for functions that have type parameters
.label = type parameter declared here
hir_analysis_return_type_notation_missing_method =
cannot find associated function `{$assoc_name}` for `{$ty_name}`
hir_analysis_return_type_notation_on_non_rpitit =
return type notation used on function that is not `async` and does not return `impl Trait`
.note = function returns `{$ty}`, which is not compatible with associated type return bounds

View file

@ -3,8 +3,7 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_lint_defs::Applicability;
use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt};
use rustc_middle::ty::{self as ty, Ty};
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
@ -256,64 +255,49 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let tcx = self.tcx();
let return_type_notation =
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
let candidate = if return_type_notation {
if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Fn,
binding.item_name,
) {
trait_ref
let assoc_kind =
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
ty::AssocKind::Fn
} else if let ConvertedBindingKind::Equality(term) = binding.kind
&& let ty::TermKind::Const(_) = term.node.unpack()
{
ty::AssocKind::Const
} else {
self.one_bound_for_assoc_method(
traits::supertraits(tcx, trait_ref),
trait_ref.print_only_trait_path(),
binding.item_name,
path_span,
)?
}
} else if self.trait_defines_associated_item_named(
ty::AssocKind::Type
};
let candidate = if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Type,
assoc_kind,
binding.item_name,
) {
// Simple case: X is defined in the current trait.
// Simple case: The assoc item is defined in the current trait.
trait_ref
} else {
// Otherwise, we have to walk through the supertraits to find
// those that do.
self.one_bound_for_assoc_type(
// one that does define it.
self.one_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(),
None,
assoc_kind,
binding.item_name,
path_span,
match binding.kind {
ConvertedBindingKind::Equality(term) => Some(term),
_ => None,
},
Some(&binding),
)?
};
let (assoc_ident, def_scope) =
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
let find_item_of_kind = |kind| {
tcx.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
};
let assoc_item = if return_type_notation {
find_item_of_kind(ty::AssocKind::Fn)
} else {
find_item_of_kind(ty::AssocKind::Type)
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
}
.expect("missing associated type");
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
// `assoc_ident` again and again.
let assoc_item = tcx
.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
.expect("missing associated item");
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
tcx.sess
@ -340,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
.or_insert(binding.span);
}
let projection_ty = if return_type_notation {
let projection_ty = if let ty::AssocKind::Fn = assoc_kind {
let mut emitted_bad_param_err = false;
// If we have an method return type bound, then we need to substitute
// the method's early bound params with suitable late-bound params.
@ -467,7 +451,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty.node));
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);
@ -492,77 +476,27 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
}
}
let assoc_item_def_id = projection_ty.skip_binder().def_id;
let def_kind = tcx.def_kind(assoc_item_def_id);
match binding.kind {
ConvertedBindingKind::Equality(..) if return_type_notation => {
ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
));
}
ConvertedBindingKind::Equality(mut term) => {
ConvertedBindingKind::Equality(term) => {
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
// the "projection predicate" for:
//
// `<T as Iterator>::Item = u32`
match (def_kind, term.unpack()) {
(DefKind::AssocTy, ty::TermKind::Ty(_))
| (DefKind::AssocConst, ty::TermKind::Const(_)) => (),
(_, _) => {
let got = if let Some(_) = term.ty() { "type" } else { "constant" };
let expected = tcx.def_descr(assoc_item_def_id);
let mut err = tcx.sess.struct_span_err(
binding.span,
format!("expected {expected} bound, found {got}"),
);
err.span_note(
tcx.def_span(assoc_item_def_id),
format!("{expected} defined here"),
);
if let DefKind::AssocConst = def_kind
&& let Some(t) = term.ty()
&& (t.is_enum() || t.references_error())
&& tcx.features().associated_const_equality
{
err.span_suggestion(
binding.span,
"if equating a const, try wrapping with braces",
format!("{} = {{ const }}", binding.item_name),
Applicability::HasPlaceholders,
);
}
let reported = err.emit();
term = match def_kind {
DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
DefKind::AssocConst => ty::Const::new_error(
tcx,
reported,
tcx.type_of(assoc_item_def_id)
.instantiate(tcx, projection_ty.skip_binder().args),
)
.into(),
_ => unreachable!(),
};
}
}
bounds.push_projection_bound(
tcx,
projection_ty
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
projection_ty,
term: term.node,
}),
binding.span,
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
match def_kind {
DefKind::AssocTy => {}
_ => {
return Err(tcx.sess.emit_err(errors::AssocBoundOnConst {
span: assoc_ident.span,
descr: tcx.def_descr(assoc_item_def_id),
}));
}
}
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
//
// `<T as Iterator>::Item: Debug`

View file

@ -1,15 +1,16 @@
use crate::astconv::AstConv;
use crate::astconv::{AstConv, ConvertedBindingKind};
use crate::errors::{
AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
ParenthesizedFnTraitExpansion,
};
use crate::fluent_generated as fluent;
use crate::traits::error_reporting::report_object_safety_error;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::traits::FulfillmentError;
use rustc_middle::ty::{self, suggest_constraining_type_param, AssocItem, AssocKind, Ty, TyCtxt};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
@ -97,83 +98,88 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
pub(crate) fn complain_about_assoc_type_not_found<I>(
pub(super) fn complain_about_assoc_item_not_found<I>(
&self,
all_candidates: impl Fn() -> I,
ty_param_name: &str,
ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
) -> ErrorGuaranteed
where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
{
let tcx = self.tcx();
// First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches.
if let Some(assoc_item) = all_candidates().find_map(|r| {
tcx.associated_items(r.def_id())
.filter_by_name_unhygienic(assoc_name.name)
.find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id()))
}) {
return self.complain_about_assoc_kind_mismatch(
assoc_item, assoc_kind, assoc_name, span, binding,
);
}
let assoc_kind_str = super::assoc_kind_str(assoc_kind);
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
// valid span, so we point at the whole path segment instead.
let is_dummy = assoc_name.span == DUMMY_SP;
let mut err = struct_span_err!(
self.tcx().sess,
if is_dummy { span } else { assoc_name.span },
E0220,
"associated type `{}` not found for `{}`",
let mut err = errors::AssocItemNotFound {
span: if is_dummy { span } else { assoc_name.span },
assoc_name,
ty_param_name
);
assoc_kind: assoc_kind_str,
ty_param_name,
label: None,
sugg: None,
};
if is_dummy {
err.span_label(span, format!("associated type `{assoc_name}` not found"));
return err.emit();
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
return tcx.sess.emit_err(err);
}
let all_candidate_names: Vec<_> = all_candidates()
.flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
.flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order())
.filter_map(|item| {
if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type {
Some(item.name)
} else {
None
}
(!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name)
})
.collect();
if let Some(suggested_name) =
find_best_match_for_name(&all_candidate_names, assoc_name.name, None)
{
err.span_suggestion(
assoc_name.span,
"there is an associated type with a similar name",
err.sugg = Some(errors::AssocItemNotFoundSugg::Similar {
span: assoc_name.span,
assoc_kind: assoc_kind_str,
suggested_name,
Applicability::MaybeIncorrect,
);
return err.emit();
});
return tcx.sess.emit_err(err);
}
// If we didn't find a good item in the supertraits (or couldn't get
// the supertraits), like in ItemCtxt, then look more generally from
// all visible traits. If there's one clear winner, just suggest that.
let visible_traits: Vec<_> = self
.tcx()
let visible_traits: Vec<_> = tcx
.all_traits()
.filter(|trait_def_id| {
let viz = self.tcx().visibility(*trait_def_id);
let viz = tcx.visibility(*trait_def_id);
let def_id = self.item_def_id();
viz.is_accessible_from(def_id, self.tcx())
viz.is_accessible_from(def_id, tcx)
})
.collect();
let wider_candidate_names: Vec<_> = visible_traits
.iter()
.flat_map(|trait_def_id| {
self.tcx().associated_items(*trait_def_id).in_definition_order()
})
.flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order())
.filter_map(|item| {
if !item.is_impl_trait_in_trait() && item.kind == ty::AssocKind::Type {
Some(item.name)
} else {
None
}
(!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name)
})
.collect();
@ -182,52 +188,51 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
{
if let [best_trait] = visible_traits
.iter()
.copied()
.filter(|trait_def_id| {
self.tcx()
.associated_items(*trait_def_id)
tcx.associated_items(trait_def_id)
.filter_by_name_unhygienic(suggested_name)
.any(|item| item.kind == ty::AssocKind::Type)
.any(|item| item.kind == assoc_kind)
})
.collect::<Vec<_>>()[..]
{
let trait_name = self.tcx().def_path_str(*best_trait);
let an = if suggested_name != assoc_name.name { "a similarly named" } else { "an" };
err.span_label(
assoc_name.span,
format!(
"there is {an} associated type `{suggested_name}` in the \
trait `{trait_name}`",
),
);
let hir = self.tcx().hir();
let trait_name = tcx.def_path_str(best_trait);
err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait {
span: assoc_name.span,
assoc_kind: assoc_kind_str,
trait_name: &trait_name,
suggested_name,
identically_named: suggested_name == assoc_name.name,
});
let hir = tcx.hir();
if let Some(def_id) = ty_param_def_id
&& let parent = hir.get_parent_item(self.tcx().local_def_id_to_hir_id(def_id))
&& let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id))
&& let Some(generics) = hir.get_generics(parent.def_id)
{
if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any(
|b| match b {
hir::GenericBound::Trait(t, ..) => {
t.trait_ref.trait_def_id().as_ref() == Some(best_trait)
t.trait_ref.trait_def_id() == Some(best_trait)
}
_ => false,
},
) {
// The type param already has a bound for `trait_name`, we just need to
// change the associated type.
err.span_suggestion_verbose(
assoc_name.span,
format!(
"change the associated type name to use `{suggested_name}` from \
`{trait_name}`",
),
suggested_name.to_string(),
Applicability::MaybeIncorrect,
);
} else if suggest_constraining_type_param(
self.tcx(),
// change the associated item.
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
span: assoc_name.span,
assoc_kind: assoc_kind_str,
suggested_name,
});
return tcx.sess.emit_err(err);
}
let mut err = tcx.sess.create_err(err);
if suggest_constraining_type_param(
tcx,
generics,
&mut err,
ty_param_name,
&ty_param_name,
&trait_name,
None,
None,
@ -237,39 +242,101 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// was also not an exact match, so we also suggest changing it.
err.span_suggestion_verbose(
assoc_name.span,
"and also change the associated type name",
fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
suggested_name.to_string(),
Applicability::MaybeIncorrect,
);
}
return err.emit();
}
return err.emit();
return tcx.sess.emit_err(err);
}
}
// If we still couldn't find any associated type, and only one associated type exists,
// suggests using it.
if all_candidate_names.len() == 1 {
if let [candidate_name] = all_candidate_names.as_slice() {
// this should still compile, except on `#![feature(associated_type_defaults)]`
// where it could suggests `type A = Self::A`, thus recursing infinitely
let applicability = if self.tcx().features().associated_type_defaults {
let applicability = if tcx.features().associated_type_defaults {
Applicability::Unspecified
} else {
Applicability::MaybeIncorrect
};
err.span_suggestion(
assoc_name.span,
format!("`{ty_param_name}` has the following associated type"),
all_candidate_names.first().unwrap().to_string(),
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_name.span,
applicability,
);
ty_param_name,
assoc_kind: assoc_kind_str,
suggested_name: *candidate_name,
});
} else {
err.span_label(assoc_name.span, format!("associated type `{assoc_name}` not found"));
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span });
}
err.emit()
tcx.sess.emit_err(err)
}
fn complain_about_assoc_kind_mismatch(
&self,
assoc_item: &ty::AssocItem,
assoc_kind: ty::AssocKind,
ident: Ident,
span: Span,
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
) -> ErrorGuaranteed {
let tcx = self.tcx();
let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind
&& let Some(binding) = binding
&& let ConvertedBindingKind::Constraint(_) = binding.kind
{
let lo = if binding.gen_args.span_ext.is_dummy() {
ident.span
} else {
binding.gen_args.span_ext
};
Some(lo.between(span.shrink_to_hi()))
} else {
None
};
// FIXME(associated_const_equality): This has quite a few false positives and negatives.
let wrap_in_braces_sugg = if let Some(binding) = binding
&& let ConvertedBindingKind::Equality(term) = binding.kind
&& let ty::TermKind::Ty(ty) = term.node.unpack()
&& (ty.is_enum() || ty.references_error())
&& tcx.features().associated_const_equality
{
Some(errors::AssocKindMismatchWrapInBracesSugg {
lo: term.span.shrink_to_lo(),
hi: term.span.shrink_to_hi(),
})
} else {
None
};
// For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since
// one can argue that that's more “untuitive” to the user.
let (span, expected_because_label, expected, got) = if let Some(binding) = binding
&& let ConvertedBindingKind::Equality(term) = binding.kind
{
(term.span, Some(ident.span), assoc_item.kind, assoc_kind)
} else {
(ident.span, None, assoc_kind, assoc_item.kind)
};
tcx.sess.emit_err(errors::AssocKindMismatch {
span,
expected: super::assoc_kind_str(expected),
got: super::assoc_kind_str(got),
expected_because_label,
assoc_kind: super::assoc_kind_str(assoc_item.kind),
def_span: tcx.def_span(assoc_item.def_id),
bound_on_assoc_const_label,
wrap_in_braces_sugg,
})
}
pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
@ -598,7 +665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind(
tcx,
ident,
AssocKind::Type,
ty::AssocKind::Type,
trait_def,
);
@ -606,7 +673,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}))
})
.flatten()
.collect::<FxHashMap<Symbol, &AssocItem>>();
.collect::<FxHashMap<Symbol, &ty::AssocItem>>();
let mut names = names
.into_iter()

View file

@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError,
MultiSpan,
error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
FatalError, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@ -36,6 +36,7 @@ use rustc_middle::ty::{
};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
use rustc_target::spec::abi;
@ -162,7 +163,7 @@ struct ConvertedBinding<'a, 'tcx> {
#[derive(Debug)]
enum ConvertedBindingKind<'a, 'tcx> {
Equality(ty::Term<'tcx>),
Equality(Spanned<ty::Term<'tcx>>),
Constraint(&'a [hir::GenericBound<'a>]),
}
@ -595,12 +596,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.map(|binding| {
let kind = match &binding.kind {
hir::TypeBindingKind::Equality { term } => match term {
hir::Term::Ty(ty) => {
ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
}
hir::Term::Ty(ty) => ConvertedBindingKind::Equality(respan(
ty.span,
self.ast_ty_to_ty(ty).into(),
)),
hir::Term::Const(c) => {
let span = self.tcx().def_span(c.def_id);
let c = Const::from_anon_const(self.tcx(), c.def_id);
ConvertedBindingKind::Equality(c.into())
ConvertedBindingKind::Equality(respan(span, c.into()))
}
},
hir::TypeBindingKind::Constraint { bounds } => {
@ -1056,7 +1059,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
self.one_bound_for_assoc_type(
self.one_bound_for_assoc_item(
|| {
traits::transitive_bounds_that_define_assoc_item(
tcx,
@ -1068,6 +1071,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
},
param_name,
Some(ty_param_def_id),
ty::AssocKind::Type,
assoc_name,
span,
None,
@ -1076,48 +1080,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// Checks that `bounds` contains exactly one element and reports appropriate
// errors otherwise.
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
fn one_bound_for_assoc_type<I>(
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)]
fn one_bound_for_assoc_item<I>(
&self,
all_candidates: impl Fn() -> I,
ty_param_name: impl Display,
ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
is_equality: Option<ty::Term<'tcx>>,
binding: Option<&ConvertedBinding<'_, 'tcx>>,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
{
let tcx = self.tcx();
let mut matching_candidates = all_candidates().filter(|r| {
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Type, assoc_name)
});
let mut const_candidates = all_candidates().filter(|r| {
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name)
self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name)
});
let (mut bound, mut next_cand) = match (matching_candidates.next(), const_candidates.next())
{
(Some(bound), _) => (bound, matching_candidates.next()),
(None, Some(bound)) => (bound, const_candidates.next()),
(None, None) => {
let reported = self.complain_about_assoc_type_not_found(
all_candidates,
&ty_param_name.to_string(),
ty_param_def_id,
assoc_name,
span,
);
return Err(reported);
}
let Some(mut bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found(
all_candidates,
&ty_param_name.to_string(),
ty_param_def_id,
assoc_kind,
assoc_name,
span,
binding,
);
return Err(reported);
};
debug!(?bound);
// look for a candidate that is not the same as our first bound, disregarding
// whether the bound is const.
let mut next_cand = matching_candidates.next();
while let Some(mut bound2) = next_cand {
debug!(?bound2);
let tcx = self.tcx();
if bound2.bound_vars() != bound.bound_vars() {
break;
}
@ -1138,7 +1139,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
if unconsted_args.eq(bound2.skip_binder().args.iter()) {
next_cand = matching_candidates.next().or_else(|| const_candidates.next());
next_cand = matching_candidates.next();
} else {
break;
}
@ -1147,48 +1148,53 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if let Some(bound2) = next_cand {
debug!(?bound2);
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
let mut err = if is_equality.is_some() {
// More specific Error Index entry.
struct_span_err!(
self.tcx().sess,
span,
E0222,
"ambiguous associated type `{}` in bounds of `{}`",
assoc_name,
ty_param_name
)
} else {
struct_span_err!(
self.tcx().sess,
span,
E0221,
"ambiguous associated type `{}` in bounds of `{}`",
assoc_name,
ty_param_name
)
};
err.span_label(span, format!("ambiguous associated type `{assoc_name}`"));
let assoc_kind_str = assoc_kind_str(assoc_kind);
let ty_param_name = &ty_param_name.to_string();
let mut err = tcx.sess.create_err(crate::errors::AmbiguousAssocItem {
span,
assoc_kind: assoc_kind_str,
assoc_name,
ty_param_name,
});
// Provide a more specific error code index entry for equality bindings.
err.code(
if let Some(binding) = binding
&& let ConvertedBindingKind::Equality(_) = binding.kind
{
error_code!(E0222)
} else {
error_code!(E0221)
},
);
// FIXME(#97583): Resugar equality bounds to type/const bindings.
// FIXME: Turn this into a structured, translateable & more actionable suggestion.
let mut where_bounds = vec![];
for bound in bounds {
for bound in [bound, bound2].into_iter().chain(matching_candidates) {
let bound_id = bound.def_id();
let bound_span = self
.tcx()
let bound_span = tcx
.associated_items(bound_id)
.find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id)
.and_then(|item| self.tcx().hir().span_if_local(item.def_id));
.find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
.and_then(|item| tcx.hir().span_if_local(item.def_id));
if let Some(bound_span) = bound_span {
err.span_label(
bound_span,
format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),),
);
if let Some(constraint) = &is_equality {
where_bounds.push(format!(
" T: {trait}::{assoc_name} = {constraint}",
trait=bound.print_only_trait_path(),
));
if let Some(binding) = binding {
match binding.kind {
ConvertedBindingKind::Equality(term) => {
// FIXME(#97583): This isn't syntactically well-formed!
where_bounds.push(format!(
" T: {trait}::{assoc_name} = {term}",
trait = bound.print_only_trait_path(),
term = term.node,
));
}
// FIXME: Provide a suggestion.
ConvertedBindingKind::Constraint(_bounds) => {}
}
} else {
err.span_suggestion_verbose(
span.with_hi(assoc_name.span.lo()),
@ -1199,7 +1205,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
} else {
err.note(format!(
"associated type `{ty_param_name}` could derive from `{}`",
"associated {assoc_kind_str} `{assoc_name}` could derive from `{}`",
bound.print_only_trait_path(),
));
}
@ -1220,46 +1226,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Ok(bound)
}
#[instrument(level = "debug", skip(self, all_candidates, ty_name), ret)]
fn one_bound_for_assoc_method(
&self,
all_candidates: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
ty_name: impl Display,
assoc_name: Ident,
span: Span,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
let mut matching_candidates = all_candidates.filter(|r| {
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Fn, assoc_name)
});
let candidate = match matching_candidates.next() {
Some(candidate) => candidate,
None => {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationMissingMethod {
span,
ty_name: ty_name.to_string(),
assoc_name: assoc_name.name,
},
));
}
};
if let Some(conflicting_candidate) = matching_candidates.next() {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationConflictingBound {
span,
ty_name: ty_name.to_string(),
assoc_name: assoc_name.name,
first_bound: candidate.print_only_trait_path(),
second_bound: conflicting_candidate.print_only_trait_path(),
},
));
}
Ok(candidate)
}
// Create a type from a path to an associated type or to an enum variant.
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
// and item_segment is the path segment for `D`. We return a type and a def for
@ -1421,7 +1387,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return Err(guar);
};
self.one_bound_for_assoc_type(
self.one_bound_for_assoc_item(
|| {
traits::supertraits(
tcx,
@ -1430,6 +1396,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
},
kw::SelfUpper,
None,
ty::AssocKind::Type,
assoc_ident,
span,
None,
@ -1510,15 +1477,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
};
let trait_did = bound.def_id();
let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did)
else {
// Assume that if it's not matched, there must be a const defined with the same name
// but it was used in a type position.
let msg = format!("found associated const `{assoc_ident}` when type was expected");
let guar = tcx.sess.struct_span_err(span, msg).emit();
return Err(guar);
};
let assoc_ty_did = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap();
let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound);
if let Some(variant_def_id) = variant_resolution {
@ -1731,8 +1690,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let tcx = self.tcx();
let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block);
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `find_by_name_and_kind`.
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
// `ident` again and again.
let item = tcx.associated_items(scope).in_definition_order().find(|i| {
i.kind.namespace() == Namespace::TypeNS
&& i.ident(tcx).normalize_to_macros_2_0() == ident
@ -2858,3 +2818,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Some(r)
}
}
fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
match kind {
ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant",
ty::AssocKind::Type => "type",
}
}

View file

@ -521,6 +521,8 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
sym::simd_fpowi => (1, 0, vec![param(0), tcx.types.i32], param(0)),
sym::simd_fma => (1, 0, vec![param(0), param(0), param(0)], param(0)),
sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)),
sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)),
sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)),
sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)),
sym::simd_insert => (2, 0, vec![param(0), tcx.types.u32, param(1)], param(0)),
sym::simd_extract => (2, 0, vec![param(0), tcx.types.u32], param(1)),

View file

@ -6,9 +6,121 @@ use rustc_errors::{
MultiSpan,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
use rustc_middle::ty::Ty;
use rustc_span::{symbol::Ident, Span, Symbol};
#[derive(Diagnostic)]
#[diag(hir_analysis_ambiguous_assoc_item)]
pub struct AmbiguousAssocItem<'a> {
#[primary_span]
#[label]
pub span: Span,
pub assoc_kind: &'static str,
pub assoc_name: Ident,
pub ty_param_name: &'a str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_assoc_kind_mismatch)]
pub struct AssocKindMismatch {
#[primary_span]
#[label]
pub span: Span,
pub expected: &'static str,
pub got: &'static str,
#[label(hir_analysis_expected_because_label)]
pub expected_because_label: Option<Span>,
pub assoc_kind: &'static str,
#[note]
pub def_span: Span,
#[label(hir_analysis_bound_on_assoc_const_label)]
pub bound_on_assoc_const_label: Option<Span>,
#[subdiagnostic]
pub wrap_in_braces_sugg: Option<AssocKindMismatchWrapInBracesSugg>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg,
applicability = "maybe-incorrect"
)]
pub struct AssocKindMismatchWrapInBracesSugg {
#[suggestion_part(code = "{{ ")]
pub lo: Span,
#[suggestion_part(code = " }}")]
pub hi: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_assoc_item_not_found, code = "E0220")]
pub struct AssocItemNotFound<'a> {
#[primary_span]
pub span: Span,
pub assoc_name: Ident,
pub assoc_kind: &'static str,
pub ty_param_name: &'a str,
#[subdiagnostic]
pub label: Option<AssocItemNotFoundLabel<'a>>,
#[subdiagnostic]
pub sugg: Option<AssocItemNotFoundSugg<'a>>,
}
#[derive(Subdiagnostic)]
pub enum AssocItemNotFoundLabel<'a> {
#[label(hir_analysis_assoc_item_not_found_label)]
NotFound {
#[primary_span]
span: Span,
},
#[label(hir_analysis_assoc_item_not_found_found_in_other_trait_label)]
FoundInOtherTrait {
#[primary_span]
span: Span,
assoc_kind: &'static str,
trait_name: &'a str,
suggested_name: Symbol,
identically_named: bool,
},
}
#[derive(Subdiagnostic)]
pub enum AssocItemNotFoundSugg<'a> {
#[suggestion(
hir_analysis_assoc_item_not_found_similar_sugg,
code = "{suggested_name}",
applicability = "maybe-incorrect"
)]
Similar {
#[primary_span]
span: Span,
assoc_kind: &'static str,
suggested_name: Symbol,
},
#[suggestion(
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg,
code = "{suggested_name}",
style = "verbose",
applicability = "maybe-incorrect"
)]
SimilarInOtherTrait {
#[primary_span]
span: Span,
assoc_kind: &'static str,
suggested_name: Symbol,
},
#[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")]
Other {
#[primary_span]
span: Span,
#[applicability]
applicability: Applicability,
ty_param_name: &'a str,
assoc_kind: &'static str,
suggested_name: Symbol,
},
}
#[derive(Diagnostic)]
#[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")]
pub struct UnrecognizedAtomicOperation<'a> {
@ -537,27 +649,6 @@ pub(crate) struct ReturnTypeNotationEqualityBound {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_return_type_notation_missing_method)]
pub(crate) struct ReturnTypeNotationMissingMethod {
#[primary_span]
pub span: Span,
pub ty_name: String,
pub assoc_name: Symbol,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_return_type_notation_conflicting_bound)]
#[note]
pub(crate) struct ReturnTypeNotationConflictingBound<'tcx> {
#[primary_span]
pub span: Span,
pub ty_name: String,
pub assoc_name: Symbol,
pub first_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
pub second_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")]
pub(crate) struct PlaceholderNotAllowedItemSignatures {
@ -954,15 +1045,6 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
pub return_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_assoc_bound_on_const)]
#[note]
pub struct AssocBoundOnConst {
#[primary_span]
pub span: Span,
pub descr: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_inherent_ty_outside, code = "E0390")]
#[help]

View file

@ -67,6 +67,28 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
yield_ty
}
// HACK(-Ztrait-solver=next): In the *old* trait solver, we must eagerly
// guide inference on the yield type so that we can handle `AsyncIterator`
// in this block in projection correctly. In the new trait solver, it is
// not a problem.
hir::CoroutineKind::AsyncGen(..) => {
let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span,
});
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(hir::LangItem::Poll, Some(span))),
tcx.mk_args(&[Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(hir::LangItem::Option, Some(span))),
tcx.mk_args(&[yield_ty.into()]),
)
.into()]),
)
}
hir::CoroutineKind::Async(..) => Ty::new_unit(tcx),
};

View file

@ -657,19 +657,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `Closure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `Closure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of an unresolved
// inference variable.
| ty::PredicateKind::Ambiguous
=> None,
| ty::PredicateKind::Ambiguous => None,
},
)
}
@ -771,6 +763,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let args = self.fresh_args_for_item(span, def_id);
let ty = item_ty.instantiate(self.tcx, args);
self.write_args(hir_id, args);
self.write_resolution(hir_id, Ok((def_kind, def_id)));
let code = match lang_item {

View file

@ -1591,10 +1591,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
let sig = self.tcx.fn_sig(assoc.def_id).instantiate_identity();
sig.inputs().skip_binder().get(0).and_then(|first| {
if first.peel_refs() == rcvr_ty.peel_refs() {
None
} else {
let impl_ty = self.tcx.type_of(*impl_did).instantiate_identity();
// if the type of first arg is the same as the current impl type, we should take the first arg into assoc function
if first.peel_refs() == impl_ty {
Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()))
} else {
None
}
})
} else {

View file

@ -35,6 +35,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{AliasRelationDirection, TyVar};
use rustc_middle::ty::{IntType, UintType};
use rustc_span::DUMMY_SP;
@ -459,7 +460,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
ambient_variance,
)?;
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
// Constrain `b_vid` to the generalized type `b_ty`.
if let &ty::Infer(TyVar(b_ty_vid)) = b_ty.kind() {
self.infcx.inner.borrow_mut().type_variables().equate(b_vid, b_ty_vid);
} else {
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
}
if needs_wf {
self.obligations.push(Obligation::new(
@ -484,31 +490,46 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
// cyclic type. We instead delay the unification in case
// the alias can be normalized to something which does not
// mention `?0`.
// FIXME(-Ztrait-solver=next): replace this with `AliasRelate`
let &ty::Alias(kind, data) = a_ty.kind() else {
bug!("generalization should only result in infer vars for aliases");
};
if !self.infcx.next_trait_solver() {
// The old solver only accepts projection predicates for associated types.
match kind {
ty::AliasKind::Projection => {}
ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => {
return Err(TypeError::CyclicTy(a_ty));
if self.infcx.next_trait_solver() {
let (lhs, rhs, direction) = match ambient_variance {
ty::Variance::Invariant => {
(a_ty.into(), b_ty.into(), AliasRelationDirection::Equate)
}
ty::Variance::Covariant => {
(a_ty.into(), b_ty.into(), AliasRelationDirection::Subtype)
}
ty::Variance::Contravariant => {
(b_ty.into(), a_ty.into(), AliasRelationDirection::Subtype)
}
ty::Variance::Bivariant => unreachable!("bivariant generalization"),
};
self.obligations.push(Obligation::new(
self.tcx(),
self.trace.cause.clone(),
self.param_env,
ty::PredicateKind::AliasRelate(lhs, rhs, direction),
));
} else {
match a_ty.kind() {
&ty::Alias(ty::AliasKind::Projection, data) => {
// FIXME: This does not handle subtyping correctly, we could
// instead create a new inference variable for `a_ty`, emitting
// `Projection(a_ty, a_infer)` and `a_infer <: b_ty`.
self.obligations.push(Obligation::new(
self.tcx(),
self.trace.cause.clone(),
self.param_env,
ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
))
}
// The old solver only accepts projection predicates for associated types.
ty::Alias(
ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque,
_,
) => return Err(TypeError::CyclicTy(a_ty)),
_ => bug!("generalizated `{a_ty:?} to infer, not an alias"),
}
}
// FIXME: This does not handle subtyping correctly, we should switch to
// alias-relate in the new solver and could instead create a new inference
// variable for `a_ty`, emitting `Projection(a_ty, a_infer)` and
// `a_infer <: b_ty`.
self.obligations.push(Obligation::new(
self.tcx(),
self.trace.cause.clone(),
self.param_env,
ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
))
} else {
match ambient_variance {
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
@ -519,9 +540,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
a_ty,
b_ty,
),
ty::Variance::Bivariant => {
unreachable!("no code should be generalizing bivariantly (currently)")
}
ty::Variance::Bivariant => unreachable!("bivariant generalization"),
}?;
}

View file

@ -1181,37 +1181,54 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind());
// helper functions
fn equals<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
match (a.kind(), b.kind()) {
(a, b) if *a == *b => true,
(&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_)))
| (
&ty::Infer(ty::InferTy::IntVar(_)),
&ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_)),
)
| (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_)))
| (
&ty::Infer(ty::InferTy::FloatVar(_)),
&ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)),
) => true,
_ => false,
}
}
let recurse = |t1, t2, values: &mut (DiagnosticStyledString, DiagnosticStyledString)| {
let (x1, x2) = self.cmp(t1, t2);
(values.0).0.extend(x1.0);
(values.1).0.extend(x2.0);
};
fn push_ty_ref<'tcx>(
region: ty::Region<'tcx>,
ty: Ty<'tcx>,
mutbl: hir::Mutability,
s: &mut DiagnosticStyledString,
) {
fn fmt_region<'tcx>(region: ty::Region<'tcx>) -> String {
let mut r = region.to_string();
if r == "'_" {
r.clear();
} else {
r.push(' ');
}
s.push_highlighted(format!("&{}{}", r, mutbl.prefix_str()));
s.push_normal(ty.to_string());
format!("&{r}")
}
fn push_ref<'tcx>(
region: ty::Region<'tcx>,
mutbl: hir::Mutability,
s: &mut DiagnosticStyledString,
) {
s.push_highlighted(fmt_region(region));
s.push_highlighted(mutbl.prefix_str());
}
fn cmp_ty_refs<'tcx>(
r1: ty::Region<'tcx>,
mut1: hir::Mutability,
r2: ty::Region<'tcx>,
mut2: hir::Mutability,
ss: &mut (DiagnosticStyledString, DiagnosticStyledString),
) {
let (r1, r2) = (fmt_region(r1), fmt_region(r2));
if r1 != r2 {
ss.0.push_highlighted(r1);
ss.1.push_highlighted(r2);
} else {
ss.0.push_normal(r1);
ss.1.push_normal(r2);
}
if mut1 != mut2 {
ss.0.push_highlighted(mut1.prefix_str());
ss.1.push_highlighted(mut2.prefix_str());
} else {
ss.0.push_normal(mut1.prefix_str());
ss.1.push_normal(mut2.prefix_str());
}
}
// process starts here
@ -1310,9 +1327,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
values.0.push_normal("_");
values.1.push_normal("_");
} else {
let (x1, x2) = self.cmp(ta1, ta2);
(values.0).0.extend(x1.0);
(values.1).0.extend(x2.0);
recurse(ta1, ta2, &mut values);
}
self.push_comma(&mut values.0, &mut values.1, len, i);
}
@ -1418,27 +1433,24 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
// When finding T != &T, highlight only the borrow
(&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(ref_ty1, t2) => {
// When finding `&T != &T`, compare the references, then recurse into pointee type
(&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0);
values.1.push_normal(t2.to_string());
cmp_ty_refs(r1, mutbl1, r2, mutbl2, &mut values);
recurse(ref_ty1, ref_ty2, &mut values);
values
}
(_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(t1, ref_ty2) => {
// When finding T != &T, highlight the borrow
(&ty::Ref(r1, ref_ty1, mutbl1), _) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
values.0.push_normal(t1.to_string());
push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1);
push_ref(r1, mutbl1, &mut values.0);
recurse(ref_ty1, t2, &mut values);
values
}
// When encountering &T != &mut T, highlight only the borrow
(&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2))
if equals(ref_ty1, ref_ty2) =>
{
(_, &ty::Ref(r2, ref_ty2, mutbl2)) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0);
push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1);
push_ref(r2, mutbl2, &mut values.1);
recurse(t1, ref_ty2, &mut values);
values
}
@ -1448,9 +1460,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
(DiagnosticStyledString::normal("("), DiagnosticStyledString::normal("("));
let len = args1.len();
for (i, (left, right)) in args1.iter().zip(args2).enumerate() {
let (x1, x2) = self.cmp(left, right);
(values.0).0.extend(x1.0);
(values.1).0.extend(x2.0);
recurse(left, right, &mut values);
self.push_comma(&mut values.0, &mut values.1, len, i);
}
if len == 1 {

View file

@ -345,37 +345,61 @@ pub struct InferCtxt<'tcx> {
impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
type Interner = TyCtxt<'tcx>;
fn universe_of_ty(&self, ty: ty::InferTy) -> Option<ty::UniverseIndex> {
use InferTy::*;
match ty {
// FIXME(BoxyUwU): this is kind of jank and means that printing unresolved
// ty infers will give you the universe of the var it resolved to not the universe
// it actually had. It also means that if you have a `?0.1` and infer it to `u8` then
// try to print out `?0.1` it will just print `?0`.
TyVar(ty_vid) => match self.probe_ty_var(ty_vid) {
Err(universe) => Some(universe),
Ok(_) => None,
},
IntVar(_) | FloatVar(_) | FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_) => None,
fn interner(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn universe_of_ty(&self, vid: TyVid) -> Option<ty::UniverseIndex> {
// FIXME(BoxyUwU): this is kind of jank and means that printing unresolved
// ty infers will give you the universe of the var it resolved to not the universe
// it actually had. It also means that if you have a `?0.1` and infer it to `u8` then
// try to print out `?0.1` it will just print `?0`.
match self.probe_ty_var(vid) {
Err(universe) => Some(universe),
Ok(_) => None,
}
}
fn universe_of_ct(&self, ct: ty::InferConst) -> Option<ty::UniverseIndex> {
use ty::InferConst::*;
match ct {
// Same issue as with `universe_of_ty`
Var(ct_vid) => match self.probe_const_var(ct_vid) {
Err(universe) => Some(universe),
Ok(_) => None,
},
EffectVar(_) => None,
Fresh(_) => None,
fn universe_of_ct(&self, ct: ConstVid) -> Option<ty::UniverseIndex> {
// Same issue as with `universe_of_ty`
match self.probe_const_var(ct) {
Err(universe) => Some(universe),
Ok(_) => None,
}
}
fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
Some(self.universe_of_region_vid(lt))
}
fn root_ty_var(&self, vid: TyVid) -> TyVid {
self.root_var(vid)
}
fn probe_ty_var(&self, vid: TyVid) -> Option<Ty<'tcx>> {
self.probe_ty_var(vid).ok()
}
fn root_lt_var(&self, vid: ty::RegionVid) -> ty::RegionVid {
self.root_region_var(vid)
}
fn probe_lt_var(&self, vid: ty::RegionVid) -> Option<ty::Region<'tcx>> {
let re = self
.inner
.borrow_mut()
.unwrap_region_constraints()
.opportunistic_resolve_var(self.tcx, vid);
if re.is_var() { None } else { Some(re) }
}
fn root_ct_var(&self, vid: ConstVid) -> ConstVid {
self.root_const_var(vid)
}
fn probe_ct_var(&self, vid: ConstVid) -> Option<ty::Const<'tcx>> {
self.probe_const_var(vid).ok()
}
}
/// See the `error_reporting` module for more details.
@ -1347,6 +1371,10 @@ impl<'tcx> InferCtxt<'tcx> {
self.inner.borrow_mut().type_variables().root_var(var)
}
pub fn root_region_var(&self, var: ty::RegionVid) -> ty::RegionVid {
self.inner.borrow_mut().unwrap_region_constraints().root_var(var)
}
pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid {
self.inner.borrow_mut().const_unification_table().find(var).vid
}

View file

@ -623,6 +623,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
}
}
pub fn root_var(&mut self, vid: ty::RegionVid) -> ty::RegionVid {
let mut ut = self.unification_table_mut(); // FIXME(rust-lang/ena#42): unnecessary mut
ut.find(vid).vid
}
fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> {
match t {
Glb => &mut self.glbs,

View file

@ -229,12 +229,11 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
/// Precondition: `vid` must not have been previously instantiated.
pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) {
let vid = self.root_var(vid);
debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}");
debug_assert!(self.probe(vid).is_unknown());
debug_assert!(
self.eq_relations().probe_value(vid).is_unknown(),
"instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
vid,
ty,
"instantiating type variable `{vid:?}` twice: new-value = {ty:?}, old-value={:?}",
self.eq_relations().probe_value(vid)
);
self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty });

View file

@ -261,9 +261,14 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
fn elaborate(&mut self, elaboratable: &O) {
let tcx = self.visited.tcx;
let bound_predicate = elaboratable.predicate().kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
// We only elaborate clauses.
let Some(clause) = elaboratable.predicate().as_clause() else {
return;
};
let bound_clause = clause.kind();
match bound_clause.skip_binder() {
ty::ClauseKind::Trait(data) => {
// Negative trait bounds do not imply any supertrait bounds
if data.polarity == ty::ImplPolarity::Negative {
return;
@ -280,49 +285,16 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
let obligations =
predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| {
elaboratable.child_with_derived_cause(
clause.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
clause.subst_supertrait(tcx, &bound_clause.rebind(data.trait_ref)),
span,
bound_predicate.rebind(data),
bound_clause.rebind(data),
index,
)
});
debug!(?data, ?obligations, "super_predicates");
self.extend_deduped(obligations);
}
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) => {
// Currently, we do not elaborate WF predicates,
// although we easily could.
}
ty::PredicateKind::ObjectSafe(..) => {
// Currently, we do not elaborate object-safe
// predicates.
}
ty::PredicateKind::Subtype(..) => {
// Currently, we do not "elaborate" predicates like `X <: Y`,
// though conceivably we might.
}
ty::PredicateKind::Coerce(..) => {
// Currently, we do not "elaborate" predicates like `X -> Y`,
// though conceivably we might.
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
// Nothing to elaborate in a projection predicate.
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => {
// Currently, we do not elaborate const-evaluatable
// predicates.
}
ty::PredicateKind::ConstEquate(..) => {
// Currently, we do not elaborate const-equate
// predicates.
}
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {
// Nothing to elaborate from `'a: 'b`.
}
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty_max,
r_min,
))) => {
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => {
// We know that `T: 'a` for some type `T`. We can
// often elaborate this. For example, if we know that
// `[U]: 'a`, that implies that `U: 'a`. Similarly, if
@ -385,15 +357,25 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
}
})
.map(|clause| {
elaboratable.child(bound_predicate.rebind(clause).to_predicate(tcx))
elaboratable.child(bound_clause.rebind(clause).to_predicate(tcx))
}),
);
}
ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::AliasRelate(..) => {
// No
ty::ClauseKind::RegionOutlives(..) => {
// Nothing to elaborate from `'a: 'b`.
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => {
ty::ClauseKind::WellFormed(..) => {
// Currently, we do not elaborate WF predicates,
// although we easily could.
}
ty::ClauseKind::Projection(..) => {
// Nothing to elaborate in a projection predicate.
}
ty::ClauseKind::ConstEvaluatable(..) => {
// Currently, we do not elaborate const-evaluatable
// predicates.
}
ty::ClauseKind::ConstArgHasType(..) => {
// Nothing to elaborate
}
}

View file

@ -316,6 +316,10 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
// Set parallel mode before thread pool creation, which will create `Lock`s.
rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1);
// Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread
let early_handler = EarlyErrorHandler::new(config.opts.error_format);
early_handler.initialize_checked_jobserver();
util::run_in_thread_pool_with_globals(
config.opts.edition,
config.opts.unstable_opts.threads,

View file

@ -27,6 +27,8 @@ use std::sync::Arc;
fn mk_session(matches: getopts::Matches) -> (Session, Cfg) {
let mut early_handler = EarlyErrorHandler::new(ErrorOutputType::default());
early_handler.initialize_checked_jobserver();
let registry = registry::Registry::new(&[]);
let sessopts = build_session_options(&mut early_handler, &matches);
let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);

View file

@ -4,10 +4,14 @@
use std::ops::Range;
use std::str::Chars;
use Mode::*;
#[cfg(test)]
mod tests;
/// Errors and warnings that can occur during string unescaping.
/// Errors and warnings that can occur during string unescaping. They mostly
/// relate to malformed escape sequences, but there are a few that are about
/// other problems.
#[derive(Debug, PartialEq, Eq)]
pub enum EscapeError {
/// Expected 1 char, but 0 were found.
@ -73,25 +77,24 @@ impl EscapeError {
}
}
/// Takes a contents of a literal (without quotes) and produces a
/// sequence of escaped characters or errors.
/// Values are returned through invoking of the provided callback.
/// Takes a contents of a literal (without quotes) and produces a sequence of
/// escaped characters or errors.
///
/// Values are returned by invoking `callback`. For `Char` and `Byte` modes,
/// the callback will be called exactly once.
pub fn unescape_literal<F>(src: &str, mode: Mode, callback: &mut F)
where
F: FnMut(Range<usize>, Result<char, EscapeError>),
{
match mode {
Mode::Char | Mode::Byte => {
Char | Byte => {
let mut chars = src.chars();
let res = unescape_char_or_byte(&mut chars, mode == Mode::Byte);
let res = unescape_char_or_byte(&mut chars, mode);
callback(0..(src.len() - chars.as_str().len()), res);
}
Mode::Str | Mode::ByteStr => unescape_str_common(src, mode, callback),
Mode::RawStr | Mode::RawByteStr => {
unescape_raw_str_or_raw_byte_str(src, mode == Mode::RawByteStr, callback)
}
Mode::CStr | Mode::RawCStr => unreachable!(),
Str | ByteStr => unescape_str_common(src, mode, callback),
RawStr | RawByteStr => unescape_raw_str_or_raw_byte_str(src, mode, callback),
CStr | RawCStr => unreachable!(),
}
}
@ -117,38 +120,44 @@ pub fn unescape_c_string<F>(src: &str, mode: Mode, callback: &mut F)
where
F: FnMut(Range<usize>, Result<CStrUnit, EscapeError>),
{
if mode == Mode::RawCStr {
unescape_raw_str_or_raw_byte_str(
src,
mode.characters_should_be_ascii(),
&mut |r, result| callback(r, result.map(CStrUnit::Char)),
);
} else {
unescape_str_common(src, mode, callback);
match mode {
CStr => {
unescape_str_common(src, mode, callback);
}
RawCStr => {
unescape_raw_str_or_raw_byte_str(src, mode, &mut |r, result| {
callback(r, result.map(CStrUnit::Char))
});
}
Char | Byte | Str | RawStr | ByteStr | RawByteStr => unreachable!(),
}
}
/// Takes a contents of a char literal (without quotes), and returns an
/// unescaped char or an error.
pub fn unescape_char(src: &str) -> Result<char, EscapeError> {
unescape_char_or_byte(&mut src.chars(), false)
unescape_char_or_byte(&mut src.chars(), Char)
}
/// Takes a contents of a byte literal (without quotes), and returns an
/// unescaped byte or an error.
pub fn unescape_byte(src: &str) -> Result<u8, EscapeError> {
unescape_char_or_byte(&mut src.chars(), true).map(byte_from_char)
unescape_char_or_byte(&mut src.chars(), Byte).map(byte_from_char)
}
/// What kind of literal do we parse.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Mode {
Char,
Str,
Byte,
ByteStr,
Str,
RawStr,
ByteStr,
RawByteStr,
CStr,
RawCStr,
}
@ -156,45 +165,42 @@ pub enum Mode {
impl Mode {
pub fn in_double_quotes(self) -> bool {
match self {
Mode::Str
| Mode::ByteStr
| Mode::RawStr
| Mode::RawByteStr
| Mode::CStr
| Mode::RawCStr => true,
Mode::Char | Mode::Byte => false,
Str | RawStr | ByteStr | RawByteStr | CStr | RawCStr => true,
Char | Byte => false,
}
}
/// Non-byte literals should have `\xXX` escapes that are within the ASCII range.
pub fn ascii_escapes_should_be_ascii(self) -> bool {
fn ascii_escapes_should_be_ascii(self) -> bool {
match self {
Mode::Char | Mode::Str | Mode::RawStr => true,
Mode::Byte | Mode::ByteStr | Mode::RawByteStr | Mode::CStr | Mode::RawCStr => false,
Char | Str => true,
Byte | ByteStr | CStr => false,
RawStr | RawByteStr | RawCStr => unreachable!(),
}
}
/// Whether characters within the literal must be within the ASCII range
pub fn characters_should_be_ascii(self) -> bool {
/// Whether characters within the literal must be within the ASCII range.
#[inline]
fn chars_should_be_ascii(self) -> bool {
match self {
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
Mode::Char | Mode::Str | Mode::RawStr | Mode::CStr | Mode::RawCStr => false,
Byte | ByteStr | RawByteStr => true,
Char | Str | RawStr | CStr | RawCStr => false,
}
}
/// Byte literals do not allow unicode escape.
pub fn is_unicode_escape_disallowed(self) -> bool {
fn is_unicode_escape_disallowed(self) -> bool {
match self {
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
Mode::Char | Mode::Str | Mode::RawStr | Mode::CStr | Mode::RawCStr => false,
Byte | ByteStr | RawByteStr => true,
Char | Str | RawStr | CStr | RawCStr => false,
}
}
pub fn prefix_noraw(self) -> &'static str {
match self {
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => "b",
Mode::CStr | Mode::RawCStr => "c",
Mode::Char | Mode::Str | Mode::RawStr => "",
Char | Str | RawStr => "",
Byte | ByteStr | RawByteStr => "b",
CStr | RawCStr => "c",
}
}
}
@ -294,22 +300,21 @@ fn scan_unicode(
}
#[inline]
fn ascii_check(c: char, characters_should_be_ascii: bool) -> Result<char, EscapeError> {
if characters_should_be_ascii && !c.is_ascii() {
// Byte literal can't be a non-ascii character.
fn ascii_check(c: char, chars_should_be_ascii: bool) -> Result<char, EscapeError> {
if chars_should_be_ascii && !c.is_ascii() {
Err(EscapeError::NonAsciiCharInByte)
} else {
Ok(c)
}
}
fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError> {
fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
let c = chars.next().ok_or(EscapeError::ZeroChars)?;
let res = match c {
'\\' => scan_escape(chars, if is_byte { Mode::Byte } else { Mode::Char }),
'\\' => scan_escape(chars, mode),
'\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
'\r' => Err(EscapeError::BareCarriageReturn),
_ => ascii_check(c, is_byte),
_ => ascii_check(c, mode.chars_should_be_ascii()),
}?;
if chars.next().is_some() {
return Err(EscapeError::MoreThanOneChar);
@ -324,6 +329,7 @@ where
F: FnMut(Range<usize>, Result<T, EscapeError>),
{
let mut chars = src.chars();
let chars_should_be_ascii = mode.chars_should_be_ascii(); // get this outside the loop
// The `start` and `end` computation here is complicated because
// `skip_ascii_whitespace` makes us to skip over chars without counting
@ -346,14 +352,12 @@ where
_ => scan_escape::<T>(&mut chars, mode),
}
}
'\n' => Ok(b'\n'.into()),
'\t' => Ok(b'\t'.into()),
'"' => Err(EscapeError::EscapeOnlyChar),
'\r' => Err(EscapeError::BareCarriageReturn),
_ => ascii_check(c, mode.characters_should_be_ascii()).map(Into::into),
_ => ascii_check(c, chars_should_be_ascii).map(Into::into),
};
let end = src.len() - chars.as_str().len();
callback(start..end, res.map(Into::into));
callback(start..end, res);
}
}
@ -387,20 +391,21 @@ where
/// sequence of characters or errors.
/// NOTE: Raw strings do not perform any explicit character escaping, here we
/// only produce errors on bare CR.
fn unescape_raw_str_or_raw_byte_str<F>(src: &str, is_byte: bool, callback: &mut F)
fn unescape_raw_str_or_raw_byte_str<F>(src: &str, mode: Mode, callback: &mut F)
where
F: FnMut(Range<usize>, Result<char, EscapeError>),
{
let mut chars = src.chars();
let chars_should_be_ascii = mode.chars_should_be_ascii(); // get this outside the loop
// The `start` and `end` computation here matches the one in
// `unescape_str_or_byte_str` for consistency, even though this function
// `unescape_str_common` for consistency, even though this function
// doesn't have to worry about skipping any chars.
while let Some(c) = chars.next() {
let start = src.len() - chars.as_str().len() - c.len_utf8();
let res = match c {
'\r' => Err(EscapeError::BareCarriageReturnInRawString),
_ => ascii_check(c, is_byte),
_ => ascii_check(c, chars_should_be_ascii),
};
let end = src.len() - chars.as_str().len();
callback(start..end, res);
@ -410,7 +415,7 @@ where
#[inline]
pub fn byte_from_char(c: char) -> u8 {
let res = c as u32;
debug_assert!(res <= u8::MAX as u32, "guaranteed because of Mode::ByteStr");
debug_assert!(res <= u8::MAX as u32, "guaranteed because of ByteStr");
res as u8
}

View file

@ -1000,8 +1000,10 @@ impl EarlyLintPass for UnusedDocComment {
}
fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
let arm_span = arm.pat.span.with_hi(arm.body.span.hi());
warn_if_doc(cx, arm_span, "match arms", &arm.attrs);
if let Some(body) = &arm.body {
let arm_span = arm.pat.span.with_hi(body.span.hi());
warn_if_doc(cx, arm_span, "match arms", &arm.attrs);
}
}
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {

View file

@ -162,12 +162,8 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
// Explicitly check for lints associated with 'closure_id', since
// it does not have a corresponding AST node
if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk {
if let Some(
ast::CoroutineKind::Async { closure_id, .. }
| ast::CoroutineKind::Gen { closure_id, .. },
) = sig.header.coro_kind
{
self.check_id(closure_id);
if let Some(coroutine_kind) = sig.header.coroutine_kind {
self.check_id(coroutine_kind.closure_id());
}
}
}
@ -227,13 +223,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
// it does not have a corresponding AST node
match e.kind {
ast::ExprKind::Closure(box ast::Closure {
coro_kind:
Some(
ast::CoroutineKind::Async { closure_id, .. }
| ast::CoroutineKind::Gen { closure_id, .. },
),
coroutine_kind: Some(coroutine_kind),
..
}) => self.check_id(closure_id),
}) => {
self.check_id(coroutine_kind.closure_id());
}
_ => {}
}
lint_callback!(self, check_expr_post, e);

View file

@ -1113,15 +1113,17 @@ impl EarlyLintPass for UnusedParens {
}
ExprKind::Match(ref _expr, ref arm) => {
for a in arm {
self.check_unused_delims_expr(
cx,
&a.body,
UnusedDelimsCtx::MatchArmExpr,
false,
None,
None,
true,
);
if let Some(body) = &a.body {
self.check_unused_delims_expr(
cx,
body,
UnusedDelimsCtx::MatchArmExpr,
false,
None,
None,
true,
);
}
}
}
_ => {}

View file

@ -410,7 +410,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
const char *SplitDwarfFile,
const char *OutputObjFile,
const char *DebugInfoCompression,
bool ForceEmulatedTls,
bool UseEmulatedTls,
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
auto OptLevel = fromRust(RustOptLevel);
@ -456,13 +456,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
Options.UseInitArray = UseInitArray;
#if LLVM_VERSION_LT(17, 0)
if (ForceEmulatedTls) {
Options.ExplicitEmulatedTLS = true;
Options.EmulatedTLS = true;
}
#else
Options.EmulatedTLS = ForceEmulatedTls || Trip.hasDefaultEmulatedTLS();
Options.ExplicitEmulatedTLS = true;
#endif
Options.EmulatedTLS = UseEmulatedTls;
if (TrapUnreachable) {
// Tell LLVM to codegen `unreachable` into an explicit trap instruction.

View file

@ -349,10 +349,6 @@ impl ScopeTree {
}
}
pub fn opt_destruction_scope(&self, n: hir::ItemLocalId) -> Option<Scope> {
self.destruction_scopes.get(&n).cloned()
}
pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
assert!(var != lifetime.item_local_id());

View file

@ -76,6 +76,13 @@ impl Debug for CovTerm {
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum CoverageKind {
/// Marks a span that might otherwise not be represented in MIR, so that
/// coverage instrumentation can associate it with its enclosing block/BCB.
///
/// Only used by the `InstrumentCoverage` pass, and has no effect during
/// codegen.
SpanMarker,
/// Marks the point in MIR control flow represented by a coverage counter.
///
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
@ -99,6 +106,7 @@ impl Debug for CoverageKind {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use CoverageKind::*;
match self {
SpanMarker => write!(fmt, "SpanMarker"),
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
}

View file

@ -150,11 +150,17 @@ impl<O> AssertKind<O> {
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
ResumedAfterReturn(CoroutineKind::Coroutine) => "coroutine resumed after completion",
ResumedAfterReturn(CoroutineKind::Async(_)) => "`async fn` resumed after completion",
ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => {
"`async gen fn` resumed after completion"
}
ResumedAfterReturn(CoroutineKind::Gen(_)) => {
"`gen fn` should just keep returning `None` after completion"
}
ResumedAfterPanic(CoroutineKind::Coroutine) => "coroutine resumed after panicking",
ResumedAfterPanic(CoroutineKind::Async(_)) => "`async fn` resumed after panicking",
ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => {
"`async gen fn` resumed after panicking"
}
ResumedAfterPanic(CoroutineKind::Gen(_)) => {
"`gen fn` should just keep returning `None` after panicking"
}
@ -245,6 +251,7 @@ impl<O> AssertKind<O> {
DivisionByZero(_) => middle_assert_divide_by_zero,
RemainderByZero(_) => middle_assert_remainder_by_zero,
ResumedAfterReturn(CoroutineKind::Async(_)) => middle_assert_async_resume_after_return,
ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => todo!(),
ResumedAfterReturn(CoroutineKind::Gen(_)) => {
bug!("gen blocks can be resumed after they return and will keep returning `None`")
}
@ -252,6 +259,7 @@ impl<O> AssertKind<O> {
middle_assert_coroutine_resume_after_return
}
ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic,
ResumedAfterPanic(CoroutineKind::AsyncGen(_)) => todo!(),
ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_gen_resume_after_panic,
ResumedAfterPanic(CoroutineKind::Coroutine) => {
middle_assert_coroutine_resume_after_panic

View file

@ -134,7 +134,6 @@ pub struct Block {
/// This does *not* include labels on loops, e.g. `'label: loop {}`.
pub targeted_by_break: bool,
pub region_scope: region::Scope,
pub opt_destruction_scope: Option<region::Scope>,
/// The span of the block, including the opening braces,
/// the label, and the `unsafe` keyword, if present.
pub span: Span,
@ -193,7 +192,6 @@ pub enum BlockSafety {
#[derive(Clone, Debug, HashStable)]
pub struct Stmt<'tcx> {
pub kind: StmtKind<'tcx>,
pub opt_destruction_scope: Option<region::Scope>,
}
#[derive(Clone, Debug, HashStable)]
@ -1224,12 +1222,12 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
mod size_asserts {
use super::*;
// tidy-alphabetical-start
static_assert_size!(Block, 56);
static_assert_size!(Block, 48);
static_assert_size!(Expr<'_>, 64);
static_assert_size!(ExprKind<'_>, 40);
static_assert_size!(Pat<'_>, 64);
static_assert_size!(PatKind<'_>, 48);
static_assert_size!(Stmt<'_>, 56);
static_assert_size!(Stmt<'_>, 48);
static_assert_size!(StmtKind<'_>, 48);
// tidy-alphabetical-end
}

View file

@ -144,10 +144,14 @@ pub enum SelectionCandidate<'tcx> {
/// generated for an async construct.
FutureCandidate,
/// Implementation of an `Iterator` trait by one of the generator types
/// generated for a gen construct.
/// Implementation of an `Iterator` trait by one of the coroutine types
/// generated for a `gen` construct.
IteratorCandidate,
/// Implementation of an `AsyncIterator` trait by one of the coroutine types
/// generated for a `async gen` construct.
AsyncIteratorCandidate,
/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
FnPointerCandidate {

View file

@ -7,7 +7,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LocalDefId;
use rustc_macros::HashStable;
use rustc_type_ir::{TypeFlags, WithCachedTypeInfo};
use rustc_type_ir::{ConstTy, IntoKind, TypeFlags, WithCachedTypeInfo};
mod int;
mod kind;
@ -26,6 +26,20 @@ use super::sty::ConstKind;
#[rustc_pass_by_value]
pub struct Const<'tcx>(pub(super) Interned<'tcx, WithCachedTypeInfo<ConstData<'tcx>>>);
impl<'tcx> IntoKind for Const<'tcx> {
type Kind = ConstKind<'tcx>;
fn kind(self) -> ConstKind<'tcx> {
self.kind().clone()
}
}
impl<'tcx> ConstTy<TyCtxt<'tcx>> for Const<'tcx> {
fn ty(self) -> Ty<'tcx> {
self.ty()
}
}
/// Typed constant value.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, HashStable, TyEncodable, TyDecodable)]
pub struct ConstData<'tcx> {

View file

@ -39,7 +39,9 @@ use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{FreezeReadGuard, Lock, WorkerLocal};
use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, WorkerLocal};
#[cfg(parallel_compiler)]
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{
DecorateLint, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, MultiSpan,
@ -121,6 +123,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>;
type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>;
type ProjectionPredicate = ty::ProjectionPredicate<'tcx>;
type NormalizesTo = ty::NormalizesTo<'tcx>;
type SubtypePredicate = ty::SubtypePredicate<'tcx>;
type CoercePredicate = ty::CoercePredicate<'tcx>;
type ClosureKind = ty::ClosureKind;
@ -130,6 +133,31 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
) -> (Self::Ty, ty::Mutability) {
(ty, mutbl)
}
fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
self.mk_canonical_var_infos(infos)
}
fn mk_bound_ty(self, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self::Ty {
Ty::new_bound(self, debruijn, ty::BoundTy { var, kind: ty::BoundTyKind::Anon })
}
fn mk_bound_region(self, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self::Region {
Region::new_bound(
self,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon },
)
}
fn mk_bound_const(
self,
debruijn: ty::DebruijnIndex,
var: ty::BoundVar,
ty: Self::Ty,
) -> Self::Const {
Const::new_bound(self, debruijn, var, ty)
}
}
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
@ -552,6 +580,16 @@ pub struct TyCtxt<'tcx> {
gcx: &'tcx GlobalCtxt<'tcx>,
}
// Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution.
#[cfg(parallel_compiler)]
unsafe impl DynSend for TyCtxt<'_> {}
#[cfg(parallel_compiler)]
unsafe impl DynSync for TyCtxt<'_> {}
fn _assert_tcx_fields() {
sync::assert_dyn_sync::<&'_ GlobalCtxt<'_>>();
sync::assert_dyn_send::<&'_ GlobalCtxt<'_>>();
}
impl<'tcx> Deref for TyCtxt<'tcx> {
type Target = &'tcx GlobalCtxt<'tcx>;
#[inline(always)]
@ -824,11 +862,16 @@ impl<'tcx> TyCtxt<'tcx> {
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Coroutine))
}
/// Returns `true` if the node pointed to by `def_id` is a coroutine for a gen construct.
/// Returns `true` if the node pointed to by `def_id` is a coroutine for a `gen` construct.
pub fn coroutine_is_gen(self, def_id: DefId) -> bool {
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Gen(_)))
}
/// Returns `true` if the node pointed to by `def_id` is a coroutine for a `async gen` construct.
pub fn coroutine_is_async_gen(self, def_id: DefId) -> bool {
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::AsyncGen(_)))
}
pub fn stability(self) -> &'tcx stability::Index {
self.stability_index(())
}

View file

@ -272,6 +272,10 @@ impl FlagComputation {
self.add_const(found);
}
ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => {
self.add_alias_ty(alias);
self.add_term(term);
}
ty::PredicateKind::AliasRelate(t1, t2, _) => {
self.add_term(t1);
self.add_term(t2);

View file

@ -1,3 +1,5 @@
use smallvec::SmallVec;
use crate::ty::context::TyCtxt;
use crate::ty::{self, DefId, ParamEnv, Ty};
@ -31,27 +33,31 @@ impl<'tcx> InhabitedPredicate<'tcx> {
/// Returns true if the corresponding type is inhabited in the given
/// `ParamEnv` and module
pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool {
let Ok(result) = self
.apply_inner::<!>(tcx, param_env, &|id| Ok(tcx.is_descendant_of(module_def_id, id)));
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|id| {
Ok(tcx.is_descendant_of(module_def_id, id))
});
result
}
/// Same as `apply`, but returns `None` if self contains a module predicate
pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
self.apply_inner(tcx, param_env, &|_| Err(())).ok()
self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(())).ok()
}
/// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is,
/// privately uninhabited types are considered always uninhabited.
pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool {
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &|_| Ok(true));
let Ok(result) =
self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|_| Ok(true));
result
}
fn apply_inner<E>(
#[instrument(level = "debug", skip(tcx, param_env, in_module), ret)]
fn apply_inner<E: std::fmt::Debug>(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection
in_module: &impl Fn(DefId) -> Result<bool, E>,
) -> Result<bool, E> {
match self {
@ -71,11 +77,25 @@ impl<'tcx> InhabitedPredicate<'tcx> {
match normalized_pred {
// We don't have more information than we started with, so consider inhabited.
Self::GenericType(_) => Ok(true),
pred => pred.apply_inner(tcx, param_env, in_module),
pred => {
// A type which is cyclic when monomorphized can happen here since the
// layout error would only trigger later. See e.g. `tests/ui/sized/recursive-type-2.rs`.
if eval_stack.contains(&t) {
return Ok(true); // Recover; this will error later.
}
eval_stack.push(t);
let ret = pred.apply_inner(tcx, param_env, eval_stack, in_module);
eval_stack.pop();
ret
}
}
}
Self::And([a, b]) => try_and(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
Self::Or([a, b]) => try_or(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
Self::And([a, b]) => {
try_and(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
}
Self::Or([a, b]) => {
try_or(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
}
}
}
@ -197,7 +217,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
// this is basically like `f(a)? && f(b)?` but different in the case of
// `Ok(false) && Err(_) -> Ok(false)`
fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
fn try_and<T, E>(a: T, b: T, mut f: impl FnMut(T) -> Result<bool, E>) -> Result<bool, E> {
let a = f(a);
if matches!(a, Ok(false)) {
return Ok(false);
@ -209,7 +229,7 @@ fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E
}
}
fn try_or<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
fn try_or<T, E>(a: T, b: T, mut f: impl FnMut(T) -> Result<bool, E>) -> Result<bool, E> {
let a = f(a);
if matches!(a, Ok(true)) {
return Ok(true);

View file

@ -103,6 +103,7 @@ impl<'tcx> VariantDef {
}
impl<'tcx> Ty<'tcx> {
#[instrument(level = "debug", skip(tcx), ret)]
pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
match self.kind() {
// For now, unions are always considered inhabited

View file

@ -65,15 +65,10 @@ use std::ops::ControlFlow;
use std::{fmt, str};
pub use crate::ty::diagnostics::*;
pub use rustc_type_ir::AliasKind::*;
pub use rustc_type_ir::ConstKind::{
Bound as BoundCt, Error as ErrorCt, Expr as ExprCt, Infer as InferCt, Param as ParamCt,
Placeholder as PlaceholderCt, Unevaluated, Value,
};
pub use rustc_type_ir::DynKind::*;
pub use rustc_type_ir::InferTy::*;
pub use rustc_type_ir::RegionKind::*;
pub use rustc_type_ir::TyKind::*;
pub use rustc_type_ir::*;
pub use self::binding::BindingMode;
@ -474,6 +469,14 @@ pub struct CReaderCacheKey {
#[rustc_pass_by_value]
pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo<TyKind<'tcx>>>);
impl<'tcx> IntoKind for Ty<'tcx> {
type Kind = TyKind<'tcx>;
fn kind(self) -> TyKind<'tcx> {
self.kind().clone()
}
}
impl EarlyParamRegion {
/// Does this early bound region have a name? Early bound regions normally
/// always have names except when using anonymous lifetimes (`'_`).
@ -553,6 +556,10 @@ impl<'tcx> Predicate<'tcx> {
pub fn allow_normalization(self) -> bool {
match self.kind().skip_binder() {
PredicateKind::Clause(ClauseKind::WellFormed(_)) => false,
// `NormalizesTo` is only used in the new solver, so this shouldn't
// matter. Normalizing `term` would be 'wrong' however, as it changes whether
// `normalizes-to(<T as Trait>::Assoc, <T as Trait>::Assoc)` holds.
PredicateKind::NormalizesTo(..) => false,
PredicateKind::Clause(ClauseKind::Trait(_))
| PredicateKind::Clause(ClauseKind::RegionOutlives(_))
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
@ -1093,6 +1100,33 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
}
}
/// Used by the new solver. Unlike a `ProjectionPredicate` this can only be
/// proven by actually normalizing `alias`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct NormalizesTo<'tcx> {
pub alias: AliasTy<'tcx>,
pub term: Term<'tcx>,
}
impl<'tcx> NormalizesTo<'tcx> {
pub fn self_ty(self) -> Ty<'tcx> {
self.alias.self_ty()
}
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> NormalizesTo<'tcx> {
Self { alias: self.alias.with_self_ty(tcx, self_ty), ..self }
}
pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
self.alias.trait_def_id(tcx)
}
pub fn def_id(self) -> DefId {
self.alias.def_id
}
}
pub trait ToPolyTraitRef<'tcx> {
fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
}
@ -1274,6 +1308,12 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyProjectionPredicate<'tcx> {
}
}
impl<'tcx> ToPredicate<'tcx> for NormalizesTo<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
PredicateKind::NormalizesTo(self).to_predicate(tcx)
}
}
impl<'tcx> Predicate<'tcx> {
pub fn to_opt_poly_trait_pred(self) -> Option<PolyTraitPredicate<'tcx>> {
let predicate = self.kind();
@ -1281,6 +1321,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
@ -1300,6 +1341,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Trait(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
@ -1506,34 +1548,42 @@ pub struct Placeholder<T> {
pub type PlaceholderRegion = Placeholder<BoundRegion>;
impl rustc_type_ir::Placeholder for PlaceholderRegion {
fn universe(&self) -> UniverseIndex {
impl PlaceholderLike for PlaceholderRegion {
fn universe(self) -> UniverseIndex {
self.universe
}
fn var(&self) -> BoundVar {
fn var(self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
Placeholder { universe: ui, ..self }
}
fn new(ui: UniverseIndex, var: BoundVar) -> Self {
Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::BrAnon } }
}
}
pub type PlaceholderType = Placeholder<BoundTy>;
impl rustc_type_ir::Placeholder for PlaceholderType {
fn universe(&self) -> UniverseIndex {
impl PlaceholderLike for PlaceholderType {
fn universe(self) -> UniverseIndex {
self.universe
}
fn var(&self) -> BoundVar {
fn var(self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
Placeholder { universe: ui, ..self }
}
fn new(ui: UniverseIndex, var: BoundVar) -> Self {
Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
@ -1545,18 +1595,22 @@ pub struct BoundConst<'tcx> {
pub type PlaceholderConst = Placeholder<BoundVar>;
impl rustc_type_ir::Placeholder for PlaceholderConst {
fn universe(&self) -> UniverseIndex {
impl PlaceholderLike for PlaceholderConst {
fn universe(self) -> UniverseIndex {
self.universe
}
fn var(&self) -> BoundVar {
fn var(self) -> BoundVar {
self.bound
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
Placeholder { universe: ui, ..self }
}
fn new(ui: UniverseIndex, var: BoundVar) -> Self {
Placeholder { universe: ui, bound: var }
}
}
/// When type checking, we use the `ParamEnv` to track

View file

@ -2814,6 +2814,7 @@ define_print! {
p!("the constant `", print(c1), "` equals `", print(c2), "`")
}
ty::PredicateKind::Ambiguous => p!("ambiguous"),
ty::PredicateKind::NormalizesTo(data) => p!(print(data)),
ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)),
}
}
@ -2945,6 +2946,12 @@ define_print_and_forward_display! {
p!(print(self.term))
}
ty::NormalizesTo<'tcx> {
p!(print(self.alias), " normalizes-to ");
cx.reset_type_limit();
p!(print(self.term))
}
ty::Term<'tcx> {
match self.unpack() {
ty::TermKind::Ty(ty) => p!(print(ty)),

View file

@ -173,6 +173,12 @@ impl<'tcx> fmt::Debug for ty::ProjectionPredicate<'tcx> {
}
}
impl<'tcx> fmt::Debug for ty::NormalizesTo<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NormalizesTo({:?}, {:?})", self.alias, self.term)
}
}
impl<'tcx> fmt::Debug for ty::Predicate<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.kind())

View file

@ -6,7 +6,7 @@ use crate::infer::canonical::Canonical;
use crate::ty::visit::ValidateBoundVars;
use crate::ty::InferTy::*;
use crate::ty::{
self, AdtDef, Discr, Term, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable,
self, AdtDef, Discr, IntoKind, Term, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor,
};
use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
@ -1477,6 +1477,14 @@ impl ParamConst {
#[rustc_pass_by_value]
pub struct Region<'tcx>(pub Interned<'tcx, RegionKind<'tcx>>);
impl<'tcx> IntoKind for Region<'tcx> {
type Kind = RegionKind<'tcx>;
fn kind(self) -> RegionKind<'tcx> {
*self
}
}
impl<'tcx> Region<'tcx> {
#[inline]
pub fn new_early_param(

View file

@ -732,6 +732,7 @@ impl<'tcx> TyCtxt<'tcx> {
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
match coroutine_kind {
rustc_hir::CoroutineKind::Async(..) => "async closure",
rustc_hir::CoroutineKind::AsyncGen(..) => "async gen closure",
rustc_hir::CoroutineKind::Coroutine => "coroutine",
rustc_hir::CoroutineKind::Gen(..) => "gen closure",
}
@ -752,6 +753,7 @@ impl<'tcx> TyCtxt<'tcx> {
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
match coroutine_kind {
rustc_hir::CoroutineKind::Async(..) => "an",
rustc_hir::CoroutineKind::AsyncGen(..) => "an",
rustc_hir::CoroutineKind::Coroutine => "a",
rustc_hir::CoroutineKind::Gen(..) => "a",
}
@ -781,9 +783,9 @@ impl<'tcx> TyCtxt<'tcx> {
}
pub fn expected_const_effect_param_for_body(self, def_id: LocalDefId) -> ty::Const<'tcx> {
// if the callee does have the param, we need to equate the param to some const
// value no matter whether the effects feature is enabled in the local crate,
// because inference will fail if we don't.
// FIXME(effects): This is suspicious and should probably not be done,
// especially now that we enforce host effects and then properly handle
// effect vars during fallback.
let mut host_always_on =
!self.features().effects || self.sess.opts.unstable_opts.unleash_the_miri_inside_of_you;

View file

@ -17,6 +17,7 @@ rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }

View file

@ -237,15 +237,6 @@ mir_build_non_const_path = runtime values cannot be referenced in patterns
mir_build_non_exhaustive_match_all_arms_guarded =
match arms with guards don't count towards exhaustivity
mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly
.help = ensure that all variants are matched explicitly by adding the suggested match arms
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
mir_build_non_exhaustive_omitted_pattern_lint_on_arm = the lint level must be set on the whole match
.help = it no longer has any effect to set the lint level on an individual match arm
.label = remove this attribute
.suggestion = set the lint level on the whole match
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
.def_note = `{$peeled_ty}` defined here
.type_note = the matched value is of type `{$ty}`
@ -260,10 +251,6 @@ mir_build_non_partial_eq_match =
mir_build_nontrivial_structural_match =
to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints
.range = ... with this range
.note = you likely meant to write mutually exclusive ranges
mir_build_pattern_not_covered = refutable pattern in {$origin}
.pattern_ty = the matched value is of type `{$pattern_ty}`
@ -317,13 +304,6 @@ mir_build_unconditional_recursion = function cannot return without recursing
mir_build_unconditional_recursion_call_site_label = recursive call site
mir_build_uncovered = {$count ->
[1] pattern `{$witness_1}`
[2] patterns `{$witness_1}` and `{$witness_2}`
[3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}`
*[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
} not covered
mir_build_union_field_requires_unsafe =
access to union field is unsafe and requires unsafe block
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior

View file

@ -13,32 +13,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ast_block: BlockId,
source_info: SourceInfo,
) -> BlockAnd<()> {
let Block {
region_scope,
opt_destruction_scope,
span,
ref stmts,
expr,
targeted_by_break,
safety_mode,
} = self.thir[ast_block];
let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode } =
self.thir[ast_block];
let expr = expr.map(|expr| &self.thir[expr]);
self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
if targeted_by_break {
this.in_breakable_scope(None, destination, span, |this| {
Some(this.ast_block_stmts(
destination,
block,
span,
stmts,
expr,
safety_mode,
region_scope,
))
})
} else {
this.ast_block_stmts(
self.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
if targeted_by_break {
this.in_breakable_scope(None, destination, span, |this| {
Some(this.ast_block_stmts(
destination,
block,
span,
@ -46,9 +27,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr,
safety_mode,
region_scope,
)
}
})
))
})
} else {
this.ast_block_stmts(
destination,
block,
span,
stmts,
expr,
safety_mode,
region_scope,
)
}
})
}
@ -92,20 +83,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let source_info = this.source_info(span);
for stmt in stmts {
let Stmt { ref kind, opt_destruction_scope } = this.thir[*stmt];
let Stmt { ref kind } = this.thir[*stmt];
match kind {
StmtKind::Expr { scope, expr } => {
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
let si = (*scope, source_info);
unpack!(
block = this.in_opt_scope(
opt_destruction_scope.map(|de| (de, source_info)),
|this| {
let si = (*scope, source_info);
this.in_scope(si, LintLevel::Inherited, |this| {
this.stmt_expr(block, &this.thir[*expr], Some(*scope))
})
}
)
block = this.in_scope(si, LintLevel::Inherited, |this| {
this.stmt_expr(block, &this.thir[*expr], Some(*scope))
})
);
}
StmtKind::Let {
@ -221,43 +207,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let init = &this.thir[*initializer];
let initializer_span = init.span;
let scope = (*init_scope, source_info);
let failure = unpack!(
block = this.in_opt_scope(
opt_destruction_scope.map(|de| (de, source_info)),
|this| {
let scope = (*init_scope, source_info);
this.in_scope(scope, *lint_level, |this| {
this.declare_bindings(
visibility_scope,
remainder_span,
pattern,
None,
Some((Some(&destination), initializer_span)),
);
this.visit_primary_bindings(
pattern,
UserTypeProjections::none(),
&mut |this, _, _, _, node, span, _, _| {
this.storage_live_binding(
block,
node,
span,
OutsideGuard,
true,
);
},
);
this.ast_let_else(
block = this.in_scope(scope, *lint_level, |this| {
this.declare_bindings(
visibility_scope,
remainder_span,
pattern,
None,
Some((Some(&destination), initializer_span)),
);
this.visit_primary_bindings(
pattern,
UserTypeProjections::none(),
&mut |this, _, _, _, node, span, _, _| {
this.storage_live_binding(
block,
init,
initializer_span,
*else_block,
&last_remainder_scope,
pattern,
)
})
}
)
node,
span,
OutsideGuard,
true,
);
},
);
this.ast_let_else(
block,
init,
initializer_span,
*else_block,
&last_remainder_scope,
pattern,
)
})
);
this.cfg.goto(failure, source_info, failure_entry);
@ -298,25 +279,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let Some(init) = initializer {
let init = &this.thir[*init];
let initializer_span = init.span;
let scope = (*init_scope, source_info);
unpack!(
block = this.in_opt_scope(
opt_destruction_scope.map(|de| (de, source_info)),
|this| {
let scope = (*init_scope, source_info);
this.in_scope(scope, *lint_level, |this| {
this.declare_bindings(
visibility_scope,
remainder_span,
pattern,
None,
Some((None, initializer_span)),
);
this.expr_into_pattern(block, pattern, init)
// irrefutable pattern
})
},
)
block = this.in_scope(scope, *lint_level, |this| {
this.declare_bindings(
visibility_scope,
remainder_span,
pattern,
None,
Some((None, initializer_span)),
);
this.expr_into_pattern(block, &pattern, init)
// irrefutable pattern
})
)
} else {
let scope = (*init_scope, source_info);

View file

@ -101,6 +101,19 @@ impl<'tcx> CFG<'tcx> {
self.push(block, stmt);
}
/// Adds a dummy statement whose only role is to associate a span with its
/// enclosing block for the purposes of coverage instrumentation.
///
/// This results in more accurate coverage reports for certain kinds of
/// syntax (e.g. `continue` or `if !`) that would otherwise not appear in MIR.
pub(crate) fn push_coverage_span_marker(&mut self, block: BasicBlock, source_info: SourceInfo) {
let kind = StatementKind::Coverage(Box::new(Coverage {
kind: coverage::CoverageKind::SpanMarker,
}));
let stmt = Statement { source_info, kind };
self.push(block, stmt);
}
pub(crate) fn terminate(
&mut self,
block: BasicBlock,

View file

@ -90,6 +90,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let local_scope = this.local_scope();
let (success_block, failure_block) =
this.in_if_then_scope(local_scope, expr_span, |this| {
// Help out coverage instrumentation by injecting a dummy statement with
// the original condition's span (including `!`). This fixes #115468.
if this.tcx.sess.instrument_coverage() {
this.cfg.push_coverage_span_marker(block, this.source_info(expr_span));
}
this.then_else_break(
block,
&this.thir[arg],

View file

@ -90,7 +90,6 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::{Expr, LintLevel};
use rustc_middle::ty::Ty;
use rustc_session::lint::Level;
use rustc_span::{Span, DUMMY_SP};
@ -537,27 +536,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
(then_block, else_block)
}
pub(crate) fn in_opt_scope<F, R>(
&mut self,
opt_scope: Option<(region::Scope, SourceInfo)>,
f: F,
) -> BlockAnd<R>
where
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
{
debug!("in_opt_scope(opt_scope={:?})", opt_scope);
if let Some(region_scope) = opt_scope {
self.push_scope(region_scope);
}
let mut block;
let rv = unpack!(block = f(self));
if let Some(region_scope) = opt_scope {
unpack!(block = self.pop_scope(region_scope, block));
}
debug!("in_scope: exiting opt_scope={:?} block={:?}", opt_scope, block);
block.and(rv)
}
/// Convenience wrapper that pushes a scope and then executes `f`
/// to build its contents, popping the scope afterwards.
#[instrument(skip(self, f), level = "debug")]
@ -660,14 +638,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
(None, Some(_)) => {
panic!("`return`, `become` and `break` with value and must have a destination")
}
(None, None) if self.tcx.sess.instrument_coverage() => {
// Unlike `break` and `return`, which push an `Assign` statement to MIR, from which
// a Coverage code region can be generated, `continue` needs no `Assign`; but
// without one, the `InstrumentCoverage` MIR pass cannot generate a code region for
// `continue`. Coverage will be missing unless we add a dummy `Assign` to MIR.
self.add_dummy_assignment(span, block, source_info);
(None, None) => {
if self.tcx.sess.instrument_coverage() {
// Normally we wouldn't build any MIR in this case, but that makes it
// harder for coverage instrumentation to extract a relevant span for
// `continue` expressions. So here we inject a dummy statement with the
// desired span.
self.cfg.push_coverage_span_marker(block, source_info);
}
}
(None, None) => {}
}
let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
@ -723,14 +702,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume);
}
// Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
// statement.
fn add_dummy_assignment(&mut self, span: Span, block: BasicBlock, source_info: SourceInfo) {
let local_decl = LocalDecl::new(Ty::new_unit(self.tcx), span);
let temp_place = Place::from(self.local_decls.push(local_decl));
self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx);
}
fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.

View file

@ -1,15 +1,12 @@
use crate::{
fluent_generated as fluent,
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
};
use crate::fluent_generated as fluent;
use rustc_errors::DiagnosticArgValue;
use rustc_errors::{
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::thir::Pat;
use rustc_middle::ty::{self, Ty};
use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
@ -812,94 +809,6 @@ pub struct NonPartialEqMatch<'tcx> {
pub non_peq_ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_overlapping_range_endpoints)]
#[note]
pub struct OverlappingRangeEndpoints<'tcx> {
#[label(mir_build_range)]
pub range: Span,
#[subdiagnostic]
pub overlap: Vec<Overlap<'tcx>>,
}
pub struct Overlap<'tcx> {
pub span: Span,
pub range: Pat<'tcx>,
}
impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
where
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
{
let Overlap { span, range } = self;
// FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
// does not support `#[subdiagnostic(eager)]`...
let message = format!("this range overlaps on `{range}`...");
diag.span_label(span, message);
}
}
#[derive(LintDiagnostic)]
#[diag(mir_build_non_exhaustive_omitted_pattern)]
#[help]
#[note]
pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
pub scrut_ty: Ty<'tcx>,
#[subdiagnostic]
pub uncovered: Uncovered<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
#[help]
pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
#[label]
pub lint_span: Span,
#[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
pub suggest_lint_on_match: Option<Span>,
pub lint_level: &'static str,
pub lint_name: &'static str,
}
#[derive(Subdiagnostic)]
#[label(mir_build_uncovered)]
pub(crate) struct Uncovered<'tcx> {
#[primary_span]
span: Span,
count: usize,
witness_1: Pat<'tcx>,
witness_2: Pat<'tcx>,
witness_3: Pat<'tcx>,
remainder: usize,
}
impl<'tcx> Uncovered<'tcx> {
pub fn new<'p>(
span: Span,
cx: &MatchCheckCtxt<'p, 'tcx>,
witnesses: Vec<WitnessPat<'tcx>>,
) -> Self {
let witness_1 = witnesses.get(0).unwrap().to_diagnostic_pat(cx);
Self {
span,
count: witnesses.len(),
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
witness_2: witnesses
.get(1)
.map(|w| w.to_diagnostic_pat(cx))
.unwrap_or_else(|| witness_1.clone()),
witness_3: witnesses
.get(2)
.map(|w| w.to_diagnostic_pat(cx))
.unwrap_or_else(|| witness_1.clone()),
witness_1,
remainder: witnesses.len().saturating_sub(3),
}
}
}
#[derive(Diagnostic)]
#[diag(mir_build_pattern_not_covered, code = "E0005")]
pub(crate) struct PatternNotCovered<'s, 'tcx> {

View file

@ -13,15 +13,12 @@ impl<'tcx> Cx<'tcx> {
// We have to eagerly lower the "spine" of the statements
// in order to get the lexical scoping correctly.
let stmts = self.mirror_stmts(block.hir_id.local_id, block.stmts);
let opt_destruction_scope =
self.region_scope_tree.opt_destruction_scope(block.hir_id.local_id);
let block = Block {
targeted_by_break: block.targeted_by_break,
region_scope: region::Scope {
id: block.hir_id.local_id,
data: region::ScopeData::Node,
},
opt_destruction_scope,
span: block.span,
stmts,
expr: block.expr.map(|expr| self.mirror_expr(expr)),
@ -49,7 +46,6 @@ impl<'tcx> Cx<'tcx> {
.enumerate()
.filter_map(|(index, stmt)| {
let hir_id = stmt.hir_id;
let opt_dxn_ext = self.region_scope_tree.opt_destruction_scope(hir_id.local_id);
match stmt.kind {
hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => {
let stmt = Stmt {
@ -60,7 +56,6 @@ impl<'tcx> Cx<'tcx> {
},
expr: self.mirror_expr(expr),
},
opt_destruction_scope: opt_dxn_ext,
};
Some(self.thir.stmts.push(stmt))
}
@ -122,7 +117,6 @@ impl<'tcx> Cx<'tcx> {
lint_level: LintLevel::Explicit(local.hir_id),
span,
},
opt_destruction_scope: opt_dxn_ext,
};
Some(self.thir.stmts.push(stmt))
}

View file

@ -54,7 +54,7 @@ impl<'tcx> Cx<'tcx> {
trace!(?expr.ty, "after adjustments");
// Next, wrap this up in the expr's scope.
// Finally, wrap this up in the expr's scope.
expr = Expr {
temp_lifetime: expr.temp_lifetime,
ty: expr.ty,
@ -66,22 +66,6 @@ impl<'tcx> Cx<'tcx> {
},
};
// Finally, create a destruction scope, if any.
if let Some(region_scope) =
self.region_scope_tree.opt_destruction_scope(hir_expr.hir_id.local_id)
{
expr = Expr {
temp_lifetime: expr.temp_lifetime,
ty: expr.ty,
span: hir_expr.span,
kind: ExprKind::Scope {
region_scope,
value: self.thir.exprs.push(expr),
lint_level: LintLevel::Inherited,
},
};
}
// OK, all done!
self.thir.exprs.push(expr)
}

View file

@ -1,7 +1,9 @@
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
use super::usefulness::{
compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
};
use rustc_pattern_analysis::constructor::Constructor;
use rustc_pattern_analysis::cx::MatchCheckCtxt;
use rustc_pattern_analysis::errors::Uncovered;
use rustc_pattern_analysis::pat::{DeconstructedPat, WitnessPat};
use rustc_pattern_analysis::usefulness::{Usefulness, UsefulnessReport};
use rustc_pattern_analysis::{analyze_match, MatchArm};
use crate::errors::*;
@ -42,7 +44,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
for param in thir.params.iter() {
if let Some(box ref pattern) = param.pat {
visitor.check_binding_is_irrefutable(pattern, "function argument", None);
visitor.check_binding_is_irrefutable(pattern, "function argument", None, None);
}
}
visitor.error
@ -254,10 +256,11 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
}
ExprKind::Let { box ref pat, expr } => {
let expr = &self.thir()[expr];
self.with_let_source(LetSource::None, |this| {
this.visit_expr(&this.thir()[expr]);
this.visit_expr(expr);
});
Ok(Some((ex.span, self.is_let_irrefutable(pat)?)))
Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
}
_ => {
self.with_let_source(LetSource::None, |this| {
@ -283,39 +286,118 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
check_borrow_conflicts_in_at_patterns(self, pat);
check_for_bindings_named_same_as_variants(self, pat, refutable);
});
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
}
}
/// Inspects the match scrutinee expression to determine whether the place it evaluates to may
/// hold invalid data.
fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
use ExprKind::*;
match &scrutinee.kind {
// Both pointers and references can validly point to a place with invalid data.
Deref { .. } => false,
// Inherit validity of the parent place, unless the parent is an union.
Field { lhs, .. } => {
let lhs = &self.thir()[*lhs];
match lhs.ty.kind() {
ty::Adt(def, _) if def.is_union() => false,
_ => self.is_known_valid_scrutinee(lhs),
}
}
// Essentially a field access.
Index { lhs, .. } => {
let lhs = &self.thir()[*lhs];
self.is_known_valid_scrutinee(lhs)
}
// No-op.
Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
// Casts don't cause a load.
NeverToAny { source }
| Cast { source }
| Use { source }
| PointerCoercion { source, .. }
| PlaceTypeAscription { source, .. }
| ValueTypeAscription { source, .. } => {
self.is_known_valid_scrutinee(&self.thir()[*source])
}
// These diverge.
Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
// These are statements that evaluate to `()`.
Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
// These evaluate to a value.
AddressOf { .. }
| Adt { .. }
| Array { .. }
| Binary { .. }
| Block { .. }
| Borrow { .. }
| Box { .. }
| Call { .. }
| Closure { .. }
| ConstBlock { .. }
| ConstParam { .. }
| If { .. }
| Literal { .. }
| LogicalOp { .. }
| Loop { .. }
| Match { .. }
| NamedConst { .. }
| NonHirLiteral { .. }
| OffsetOf { .. }
| Repeat { .. }
| StaticRef { .. }
| ThreadLocalRef { .. }
| Tuple { .. }
| Unary { .. }
| UpvarRef { .. }
| VarRef { .. }
| ZstLiteral { .. }
| Yield { .. } => true,
}
}
fn new_cx(
&self,
refutability: RefutableFlag,
match_span: Option<Span>,
whole_match_span: Option<Span>,
scrutinee: Option<&Expr<'tcx>>,
scrut_span: Span,
) -> MatchCheckCtxt<'p, 'tcx> {
let refutable = match refutability {
Irrefutable => false,
Refutable => true,
};
// If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
// require validity.
let known_valid_scrutinee =
scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
MatchCheckCtxt {
tcx: self.tcx,
param_env: self.param_env,
module: self.tcx.parent_module(self.lint_level).to_def_id(),
pattern_arena: self.pattern_arena,
match_lint_level: self.lint_level,
match_span,
whole_match_span,
scrut_span,
refutable,
known_valid_scrutinee,
}
}
#[instrument(level = "trace", skip(self))]
fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
assert!(self.let_source != LetSource::None);
let scrut = scrutinee.map(|id| &self.thir[id]);
if let LetSource::PlainLet = self.let_source {
self.check_binding_is_irrefutable(pat, "local binding", Some(span))
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
} else {
let Ok(refutability) = self.is_let_irrefutable(pat) else { return };
let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
if matches!(refutability, Irrefutable) {
report_irrefutable_let_patterns(
self.tcx,
@ -336,7 +418,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
expr_span: Span,
) {
let scrut = &self.thir[scrut];
let cx = self.new_cx(Refutable, Some(expr_span), scrut.span);
let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
let mut tarms = Vec::with_capacity(arms.len());
for &arm in arms {
@ -353,7 +435,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
}
let scrut_ty = scrut.ty;
let report = compute_match_usefulness(&cx, &tarms, scrut_ty);
let report = analyze_match(&cx, &tarms, scrut_ty);
match source {
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
@ -377,7 +459,12 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
let [pat_field] = &subpatterns[..] else { bug!() };
self.check_binding_is_irrefutable(&pat_field.pattern, "`for` loop binding", None);
self.check_binding_is_irrefutable(
&pat_field.pattern,
"`for` loop binding",
None,
None,
);
} else {
self.error = Err(report_non_exhaustive_match(
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
@ -457,16 +544,21 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
&mut self,
pat: &Pat<'tcx>,
refutability: RefutableFlag,
scrut: Option<&Expr<'tcx>>,
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
let cx = self.new_cx(refutability, None, pat.span);
let cx = self.new_cx(refutability, None, scrut, pat.span);
let pat = self.lower_pattern(&cx, pat)?;
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
let report = compute_match_usefulness(&cx, &arms, pat.ty());
let report = analyze_match(&cx, &arms, pat.ty());
Ok((cx, report))
}
fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result<RefutableFlag, ErrorGuaranteed> {
let (cx, report) = self.analyze_binding(pat, Refutable)?;
fn is_let_irrefutable(
&mut self,
pat: &Pat<'tcx>,
scrut: Option<&Expr<'tcx>>,
) -> Result<RefutableFlag, ErrorGuaranteed> {
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
// This also reports unreachable sub-patterns.
report_arm_reachability(&cx, &report);
@ -476,10 +568,16 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
}
#[instrument(level = "trace", skip(self))]
fn check_binding_is_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
fn check_binding_is_irrefutable(
&mut self,
pat: &Pat<'tcx>,
origin: &str,
scrut: Option<&Expr<'tcx>>,
sp: Option<Span>,
) {
let pattern_ty = pat.ty;
let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable) else { return };
let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
let witnesses = report.non_exhaustiveness_witnesses;
if witnesses.is_empty() {
// The pattern is irrefutable.
@ -749,18 +847,18 @@ fn report_arm_reachability<'p, 'tcx>(
);
};
use Reachability::*;
use Usefulness::*;
let mut catchall = None;
for (arm, is_useful) in report.arm_usefulness.iter() {
match is_useful {
Unreachable => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
Reachable(unreachables) if unreachables.is_empty() => {}
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
Reachable(unreachables) => {
let mut unreachables = unreachables.clone();
Redundant => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
Useful(redundant_spans) if redundant_spans.is_empty() => {}
// The arm is reachable, but contains redundant subpatterns (from or-patterns).
Useful(redundant_spans) => {
let mut redundant_spans = redundant_spans.clone();
// Emit lints in the order in which they occur in the file.
unreachables.sort_unstable();
for span in unreachables {
redundant_spans.sort_unstable();
for span in redundant_spans {
report_unreachable_pattern(span, arm.hir_id, None);
}
}
@ -828,7 +926,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
pattern = if witnesses.len() < 4 {
witnesses
.iter()
.map(|witness| witness.to_diagnostic_pat(cx).to_string())
.map(|witness| cx.hoist_witness_pat(witness).to_string())
.collect::<Vec<String>>()
.join(" | ")
} else {
@ -852,7 +950,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
if !is_empty_match {
let mut non_exhaustive_tys = FxHashSet::default();
// Look at the first witness.
collect_non_exhaustive_tys(cx.tcx, &witnesses[0], &mut non_exhaustive_tys);
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
for ty in non_exhaustive_tys {
if ty.is_ptr_sized_integral() {
@ -987,13 +1085,13 @@ fn joined_uncovered_patterns<'p, 'tcx>(
witnesses: &[WitnessPat<'tcx>],
) -> String {
const LIMIT: usize = 3;
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_diagnostic_pat(cx).to_string();
let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string();
match witnesses {
[] => bug!(),
[witness] => format!("`{}`", witness.to_diagnostic_pat(cx)),
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
[head @ .., tail] if head.len() < LIMIT => {
let head: Vec<_> = head.iter().map(pat_to_str).collect();
format!("`{}` and `{}`", head.join("`, `"), tail.to_diagnostic_pat(cx))
format!("`{}` and `{}`", head.join("`, `"), cx.hoist_witness_pat(tail))
}
_ => {
let (head, tail) = witnesses.split_at(LIMIT);
@ -1004,7 +1102,7 @@ fn joined_uncovered_patterns<'p, 'tcx>(
}
fn collect_non_exhaustive_tys<'tcx>(
tcx: TyCtxt<'tcx>,
cx: &MatchCheckCtxt<'_, 'tcx>,
pat: &WitnessPat<'tcx>,
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
) {
@ -1012,13 +1110,13 @@ fn collect_non_exhaustive_tys<'tcx>(
non_exhaustive_tys.insert(pat.ty());
}
if let Constructor::IntRange(range) = pat.ctor() {
if range.is_beyond_boundaries(pat.ty(), tcx) {
if cx.is_range_beyond_boundaries(range, pat.ty()) {
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
non_exhaustive_tys.insert(pat.ty());
}
}
pat.iter_fields()
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
}
fn report_adt_defined_here<'tcx>(

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,8 @@
mod check_match;
mod const_to_pat;
pub(crate) mod deconstruct_pat;
mod usefulness;
pub(crate) use self::check_match::check_match;
pub(crate) use self::usefulness::MatchCheckCtxt;
use crate::errors::*;
use crate::thir::util::UserAnnotatedTyHelpers;

View file

@ -91,23 +91,11 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
}
fn print_block(&mut self, block_id: BlockId, depth_lvl: usize) {
let Block {
targeted_by_break,
opt_destruction_scope,
span,
region_scope,
stmts,
expr,
safety_mode,
} = &self.thir.blocks[block_id];
let Block { targeted_by_break, span, region_scope, stmts, expr, safety_mode } =
&self.thir.blocks[block_id];
print_indented!(self, "Block {", depth_lvl);
print_indented!(self, format!("targeted_by_break: {}", targeted_by_break), depth_lvl + 1);
print_indented!(
self,
format!("opt_destruction_scope: {:?}", opt_destruction_scope),
depth_lvl + 1
);
print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
print_indented!(self, format!("safety_mode: {:?}", safety_mode), depth_lvl + 1);
@ -133,14 +121,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
}
fn print_stmt(&mut self, stmt_id: StmtId, depth_lvl: usize) {
let Stmt { kind, opt_destruction_scope } = &self.thir.stmts[stmt_id];
let Stmt { kind } = &self.thir.stmts[stmt_id];
print_indented!(self, "Stmt {", depth_lvl);
print_indented!(
self,
format!("opt_destruction_scope: {:?}", opt_destruction_scope),
depth_lvl + 1
);
match kind {
StmtKind::Expr { scope, expr } => {

View file

@ -196,7 +196,7 @@ impl Direction for Backward {
{
results.reset_to_block_entry(state, block);
vis.visit_block_end(results, state, block_data, block);
vis.visit_block_end(state);
// Terminator
let loc = Location { block, statement_index: block_data.statements.len() };
@ -214,7 +214,7 @@ impl Direction for Backward {
vis.visit_statement_after_primary_effect(results, state, stmt, loc);
}
vis.visit_block_start(results, state, block_data, block);
vis.visit_block_start(state);
}
fn join_state_into_successors_of<'tcx, A>(
@ -449,7 +449,7 @@ impl Direction for Forward {
{
results.reset_to_block_entry(state, block);
vis.visit_block_start(results, state, block_data, block);
vis.visit_block_start(state);
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
let loc = Location { block, statement_index };
@ -466,7 +466,7 @@ impl Direction for Forward {
results.reconstruct_terminator_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(results, state, term, loc);
vis.visit_block_end(results, state, block_data, block);
vis.visit_block_end(state);
}
fn join_state_into_successors_of<'tcx, A>(

View file

@ -545,25 +545,13 @@ where
{
type FlowState = A::Domain;
fn visit_block_start(
&mut self,
_results: &mut Results<'tcx, A>,
state: &Self::FlowState,
_block_data: &mir::BasicBlockData<'tcx>,
_block: BasicBlock,
) {
fn visit_block_start(&mut self, state: &Self::FlowState) {
if A::Direction::IS_FORWARD {
self.prev_state.clone_from(state);
}
}
fn visit_block_end(
&mut self,
_results: &mut Results<'tcx, A>,
state: &Self::FlowState,
_block_data: &mir::BasicBlockData<'tcx>,
_block: BasicBlock,
) {
fn visit_block_end(&mut self, state: &Self::FlowState) {
if A::Direction::IS_BACKWARD {
self.prev_state.clone_from(state);
}

Some files were not shown because too many files have changed in this diff Show more