Merge from rustc
This commit is contained in:
commit
c232e94bc1
686 changed files with 8145 additions and 2479 deletions
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
|
|
@ -364,8 +364,8 @@ jobs:
|
|||
os: macos-13
|
||||
- name: dist-aarch64-apple
|
||||
env:
|
||||
SCRIPT: "./x.py dist bootstrap --include-default-paths --stage 2"
|
||||
RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --host=aarch64-apple-darwin --target=aarch64-apple-darwin --enable-full-tools --enable-sanitizers --enable-profiler --disable-docs --set rust.jemalloc"
|
||||
SCRIPT: "./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin"
|
||||
RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin"
|
||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||
SELECT_XCODE: /Applications/Xcode_13.4.1.app
|
||||
USE_XCODE_CLANG: 1
|
||||
|
|
@ -375,8 +375,20 @@ jobs:
|
|||
NO_DEBUG_ASSERTIONS: 1
|
||||
NO_OVERFLOW_CHECKS: 1
|
||||
DIST_REQUIRE_ALL_TOOLS: 1
|
||||
JEMALLOC_SYS_WITH_LG_PAGE: 14
|
||||
os: macos-13
|
||||
os: macos-14
|
||||
- name: aarch64-apple
|
||||
env:
|
||||
SCRIPT: "./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin"
|
||||
RUST_CONFIGURE_ARGS: "--enable-sanitizers --enable-profiler --set rust.jemalloc"
|
||||
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
|
||||
SELECT_XCODE: /Applications/Xcode_13.4.1.app
|
||||
USE_XCODE_CLANG: 1
|
||||
MACOSX_DEPLOYMENT_TARGET: 11.0
|
||||
MACOSX_STD_DEPLOYMENT_TARGET: 11.0
|
||||
NO_LLVM_ASSERTIONS: 1
|
||||
NO_DEBUG_ASSERTIONS: 1
|
||||
NO_OVERFLOW_CHECKS: 1
|
||||
os: macos-14
|
||||
- name: x86_64-msvc
|
||||
env:
|
||||
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler"
|
||||
|
|
|
|||
|
|
@ -5321,6 +5321,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"core",
|
||||
"getopts",
|
||||
"libc",
|
||||
"panic_abort",
|
||||
"panic_unwind",
|
||||
"std",
|
||||
|
|
|
|||
|
|
@ -123,9 +123,6 @@ 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
|
||||
|
||||
ast_lowering_previously_used_here = previously used here
|
||||
|
||||
ast_lowering_register1 = register `{$reg1_name}`
|
||||
|
|
|
|||
|
|
@ -326,13 +326,6 @@ pub struct MisplacedRelaxTraitBound {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic, Clone, Copy)]
|
||||
#[diag(ast_lowering_not_supported_for_lifetime_binder_async_closure)]
|
||||
pub struct NotSupportedForLifetimeBinderAsyncClosure {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_match_arm_with_no_body)]
|
||||
pub struct MatchArmWithNoBody {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use super::errors::{
|
||||
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
|
||||
ClosureCannotBeStatic, CoroutineTooManyParameters,
|
||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
|
||||
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
|
||||
UnderscoreExprLhsAssign,
|
||||
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
|
||||
};
|
||||
use super::ResolverAstLoweringExt;
|
||||
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
|
||||
|
|
@ -1028,30 +1029,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn_decl_span: Span,
|
||||
fn_arg_span: Span,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
if let &ClosureBinder::For { span, .. } = binder {
|
||||
self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
|
||||
}
|
||||
|
||||
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
|
||||
|
||||
assert_matches!(
|
||||
coroutine_kind,
|
||||
CoroutineKind::Async { .. },
|
||||
"only async closures are supported currently"
|
||||
);
|
||||
|
||||
let body = self.with_new_scopes(fn_decl_span, |this| {
|
||||
let inner_decl =
|
||||
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
|
||||
|
||||
// Transform `async |x: u8| -> X { ... }` into
|
||||
// `|x: u8| || -> X { ... }`.
|
||||
let body_id = this.lower_body(|this| {
|
||||
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
|
||||
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);
|
||||
Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
|
||||
decl,
|
||||
&inner_decl,
|
||||
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
|
||||
body.span,
|
||||
coroutine_kind,
|
||||
hir::CoroutineSource::Closure,
|
||||
async_ret_ty,
|
||||
);
|
||||
|
||||
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
|
||||
|
|
@ -1062,15 +1060,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
body_id
|
||||
});
|
||||
|
||||
let outer_decl =
|
||||
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
|
||||
|
||||
let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
|
||||
// We need to lower the declaration outside the new scope, because we
|
||||
// have to conserve the state of being inside a loop condition for the
|
||||
// closure argument types.
|
||||
let fn_decl =
|
||||
self.lower_fn_decl(&outer_decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
|
||||
self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
|
||||
|
||||
let c = self.arena.alloc(hir::Closure {
|
||||
def_id: self.local_def_id(closure_id),
|
||||
|
|
@ -1081,7 +1076,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
body,
|
||||
fn_decl_span: self.lower_span(fn_decl_span),
|
||||
fn_arg_span: Some(self.lower_span(fn_arg_span)),
|
||||
kind: hir::ClosureKind::Closure,
|
||||
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
|
||||
// knows that a `FnDecl` output type like `-> &str` actually means
|
||||
// "coroutine that returns &str", rather than directly returning a `&str`.
|
||||
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
|
||||
constness: hir::Constness::NotConst,
|
||||
});
|
||||
hir::ExprKind::Closure(c)
|
||||
|
|
|
|||
|
|
@ -498,8 +498,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
let res =
|
||||
self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
|
||||
let res = self.lower_import_res(id, path.span);
|
||||
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
|
||||
hir::ItemKind::Use(path, hir::UseKind::Single)
|
||||
}
|
||||
|
|
@ -535,7 +534,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// for that we return the `{}` import (called the
|
||||
// `ListStem`).
|
||||
|
||||
let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None };
|
||||
let span = prefix.span.to(path.span);
|
||||
let prefix = Path { segments, span, tokens: None };
|
||||
|
||||
// Add all the nested `PathListItem`s to the HIR.
|
||||
for &(ref use_tree, id) in trees {
|
||||
|
|
@ -569,9 +569,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
});
|
||||
}
|
||||
|
||||
let res =
|
||||
self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
|
||||
let path = self.lower_use_path(res, &prefix, ParamMode::Explicit);
|
||||
let path = if trees.is_empty() && !prefix.segments.is_empty() {
|
||||
// For empty lists we need to lower the prefix so it is checked for things
|
||||
// like stability later.
|
||||
let res = self.lower_import_res(id, span);
|
||||
self.lower_use_path(res, &prefix, ParamMode::Explicit)
|
||||
} else {
|
||||
// For non-empty lists we can just drop all the data, the prefix is already
|
||||
// present in HIR as a part of nested imports.
|
||||
self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span })
|
||||
};
|
||||
hir::ItemKind::Use(path, hir::UseKind::ListStem)
|
||||
}
|
||||
}
|
||||
|
|
@ -1091,7 +1098,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
body.span,
|
||||
coroutine_kind,
|
||||
hir::CoroutineSource::Fn,
|
||||
None,
|
||||
);
|
||||
|
||||
// FIXME(async_fn_track_caller): Can this be moved above?
|
||||
|
|
@ -1113,7 +1119,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
body_span: Span,
|
||||
coroutine_kind: CoroutineKind,
|
||||
coroutine_source: hir::CoroutineSource,
|
||||
return_type_hint: Option<hir::FnRetTy<'hir>>,
|
||||
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
|
||||
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
|
||||
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
|
||||
|
|
@ -1283,12 +1288,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
};
|
||||
let closure_id = coroutine_kind.closure_id();
|
||||
let coroutine_expr = self.make_desugared_coroutine_expr(
|
||||
// FIXME(async_closures): This should only move locals,
|
||||
// and not upvars. Capturing closure upvars by ref doesn't
|
||||
// work right now anyways, so whatever.
|
||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||
// The default capture mode here is by-ref. Later on during upvar analysis,
|
||||
// we will force the captured arguments to by-move, but for async closures,
|
||||
// we want to make sure that we avoid unnecessarily moving captures, or else
|
||||
// all async closures would default to `FnOnce` as their calling mode.
|
||||
CaptureBy::Ref,
|
||||
closure_id,
|
||||
return_type_hint,
|
||||
None,
|
||||
body_span,
|
||||
desugaring_kind,
|
||||
coroutine_source,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
|
|
@ -63,7 +64,7 @@ use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
|||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{DesugaringKind, Span, DUMMY_SP};
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::collections::hash_map::Entry;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
|
|
@ -298,7 +299,6 @@ enum ImplTraitPosition {
|
|||
Path,
|
||||
Variable,
|
||||
Trait,
|
||||
AsyncBlock,
|
||||
Bound,
|
||||
Generic,
|
||||
ExternFnParam,
|
||||
|
|
@ -325,7 +325,6 @@ impl std::fmt::Display for ImplTraitPosition {
|
|||
ImplTraitPosition::Path => "paths",
|
||||
ImplTraitPosition::Variable => "the type of variable bindings",
|
||||
ImplTraitPosition::Trait => "traits",
|
||||
ImplTraitPosition::AsyncBlock => "async blocks",
|
||||
ImplTraitPosition::Bound => "bounds",
|
||||
ImplTraitPosition::Generic => "generics",
|
||||
ImplTraitPosition::ExternFnParam => "`extern fn` parameters",
|
||||
|
|
@ -751,8 +750,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res())
|
||||
}
|
||||
|
||||
fn expect_full_res_from_use(&mut self, id: NodeId) -> impl Iterator<Item = Res<NodeId>> {
|
||||
self.resolver.get_import_res(id).present_items()
|
||||
fn lower_import_res(&mut self, id: NodeId, span: Span) -> SmallVec<[Res; 3]> {
|
||||
let res = self.resolver.get_import_res(id).present_items();
|
||||
let res: SmallVec<_> = res.map(|res| self.lower_res(res)).collect();
|
||||
if res.is_empty() {
|
||||
self.dcx().span_delayed_bug(span, "no resolution for an import");
|
||||
return smallvec![Res::Err];
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> {
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
p: &Path,
|
||||
param_mode: ParamMode,
|
||||
) -> &'hir hir::UsePath<'hir> {
|
||||
assert!((1..=3).contains(&res.len()));
|
||||
self.arena.alloc(hir::UsePath {
|
||||
res,
|
||||
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
|
||||
|
|
|
|||
|
|
@ -858,7 +858,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
use crate::session_diagnostics::CaptureVarCause::*;
|
||||
match kind {
|
||||
hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
|
||||
hir::ClosureKind::Closure => MoveUseInClosure { var_span },
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
MoveUseInClosure { var_span }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -905,7 +907,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(_) => {
|
||||
BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }
|
||||
}
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }
|
||||
}
|
||||
}
|
||||
|
|
@ -1056,7 +1058,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
var_span,
|
||||
is_single_var: true,
|
||||
},
|
||||
hir::ClosureKind::Closure => BorrowUsePlaceClosure {
|
||||
hir::ClosureKind::Closure
|
||||
| hir::ClosureKind::CoroutineClosure(_) => BorrowUsePlaceClosure {
|
||||
place: desc_place,
|
||||
var_span,
|
||||
is_single_var: true,
|
||||
|
|
@ -1140,7 +1143,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
var_span,
|
||||
is_single_var: false,
|
||||
},
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
|
||||
}
|
||||
}
|
||||
|
|
@ -1158,7 +1161,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(_) => {
|
||||
FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
|
||||
}
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
|
||||
}
|
||||
}
|
||||
|
|
@ -1175,7 +1178,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(_) => {
|
||||
SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
|
||||
}
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
SecondBorrowUsePlaceClosure { place: desc_place, var_span }
|
||||
}
|
||||
}
|
||||
|
|
@ -2942,7 +2945,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
use crate::session_diagnostics::CaptureVarCause::*;
|
||||
match kind {
|
||||
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
|
||||
hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
BorrowUseInClosure { var_span }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -2958,7 +2963,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
use crate::session_diagnostics::CaptureVarCause::*;
|
||||
match kind {
|
||||
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
|
||||
hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
BorrowUseInClosure { var_span }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -614,7 +614,7 @@ impl UseSpans<'_> {
|
|||
PartialAssignment => AssignPartInCoroutine { path_span },
|
||||
});
|
||||
}
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
err.subdiagnostic(match action {
|
||||
Borrow => BorrowInClosure { path_span },
|
||||
MatchOn | Use => UseInClosure { path_span },
|
||||
|
|
@ -1253,7 +1253,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(_) => {
|
||||
CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
|
||||
}
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1472,7 +1472,7 @@ fn suggest_ampmut<'tcx>(
|
|||
}
|
||||
|
||||
fn is_closure_or_coroutine(ty: Ty<'_>) -> bool {
|
||||
ty.is_closure() || ty.is_coroutine()
|
||||
ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
|
||||
}
|
||||
|
||||
/// Given a field that needs to be mutable, returns a span where the " mut " could go.
|
||||
|
|
|
|||
|
|
@ -324,9 +324,13 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
ty::BoundRegionKind::BrEnv => {
|
||||
let def_ty = self.regioncx.universal_regions().defining_ty;
|
||||
|
||||
let DefiningTy::Closure(_, args) = def_ty else {
|
||||
// Can't have BrEnv in functions, constants or coroutines.
|
||||
bug!("BrEnv outside of closure.");
|
||||
let closure_kind = match def_ty {
|
||||
DefiningTy::Closure(_, args) => args.as_closure().kind(),
|
||||
DefiningTy::CoroutineClosure(_, args) => args.as_coroutine_closure().kind(),
|
||||
_ => {
|
||||
// Can't have BrEnv in functions, constants or coroutines.
|
||||
bug!("BrEnv outside of closure.");
|
||||
}
|
||||
};
|
||||
let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) =
|
||||
tcx.hir().expect_expr(self.mir_hir_id()).kind
|
||||
|
|
@ -334,21 +338,18 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
bug!("Closure is not defined by a closure expr");
|
||||
};
|
||||
let region_name = self.synthesize_region_name();
|
||||
|
||||
let closure_kind_ty = args.as_closure().kind_ty();
|
||||
let note = match closure_kind_ty.to_opt_closure_kind() {
|
||||
Some(ty::ClosureKind::Fn) => {
|
||||
let note = match closure_kind {
|
||||
ty::ClosureKind::Fn => {
|
||||
"closure implements `Fn`, so references to captured variables \
|
||||
can't escape the closure"
|
||||
}
|
||||
Some(ty::ClosureKind::FnMut) => {
|
||||
ty::ClosureKind::FnMut => {
|
||||
"closure implements `FnMut`, so references to captured variables \
|
||||
can't escape the closure"
|
||||
}
|
||||
Some(ty::ClosureKind::FnOnce) => {
|
||||
ty::ClosureKind::FnOnce => {
|
||||
bug!("BrEnv in a `FnOnce` closure");
|
||||
}
|
||||
None => bug!("Closure kind not inferred in borrow check"),
|
||||
};
|
||||
|
||||
Some(RegionName {
|
||||
|
|
@ -692,7 +693,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineSource::Closure,
|
||||
)) => " of async closure",
|
||||
))
|
||||
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
|
||||
" of async closure"
|
||||
}
|
||||
|
||||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
|
|
@ -719,7 +723,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Gen,
|
||||
hir::CoroutineSource::Closure,
|
||||
)) => " of gen closure",
|
||||
))
|
||||
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => {
|
||||
" of gen closure"
|
||||
}
|
||||
|
||||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Gen,
|
||||
|
|
@ -743,7 +750,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::AsyncGen,
|
||||
hir::CoroutineSource::Closure,
|
||||
)) => " of async gen closure",
|
||||
))
|
||||
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::AsyncGen) => {
|
||||
" of async gen closure"
|
||||
}
|
||||
|
||||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::AsyncGen,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
|
|
@ -1303,7 +1304,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// moved into the closure and subsequently used by the closure,
|
||||
// in order to populate our used_mut set.
|
||||
match **aggregate_kind {
|
||||
AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) => {
|
||||
AggregateKind::Closure(def_id, _)
|
||||
| AggregateKind::CoroutineClosure(def_id, _)
|
||||
| AggregateKind::Coroutine(def_id, _) => {
|
||||
let def_id = def_id.expect_local();
|
||||
let BorrowCheckResult { used_mut_upvars, .. } =
|
||||
self.infcx.tcx.mir_borrowck(def_id);
|
||||
|
|
@ -1609,6 +1612,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::CoroutineClosure(_, _)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
|
|
@ -1633,7 +1637,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
ty::Closure(_, _) | ty::Coroutine(_, _) | ty::Tuple(_) => (),
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::Tuple(_) => (),
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ pub(crate) fn is_upvar_field_projection<'tcx>(
|
|||
match place_ref.last_projection() {
|
||||
Some((place_base, ProjectionElem::Field(field, _ty))) => {
|
||||
let base_ty = place_base.ty(body, tcx).ty;
|
||||
if (base_ty.is_closure() || base_ty.is_coroutine())
|
||||
if (base_ty.is_closure() || base_ty.is_coroutine() || base_ty.is_coroutine_closure())
|
||||
&& (!by_ref || upvars[field.index()].is_by_ref())
|
||||
{
|
||||
Some(field)
|
||||
|
|
|
|||
|
|
@ -5,10 +5,13 @@ use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelega
|
|||
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
|
||||
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
|
||||
use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_trait_selection::solve::deeply_normalize;
|
||||
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
|
||||
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
|
||||
|
||||
use crate::{
|
||||
constraints::OutlivesConstraint,
|
||||
|
|
@ -33,6 +36,7 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
|
|||
/// our special inference variable there, we would mess that up.
|
||||
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
|
||||
implicit_region_bound: ty::Region<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
locations: Locations,
|
||||
span: Span,
|
||||
|
|
@ -47,6 +51,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
|
||||
implicit_region_bound: ty::Region<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
locations: Locations,
|
||||
span: Span,
|
||||
|
|
@ -59,6 +64,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
universal_regions,
|
||||
region_bound_pairs,
|
||||
implicit_region_bound,
|
||||
param_env,
|
||||
known_type_outlives_obligations,
|
||||
locations,
|
||||
span,
|
||||
|
|
@ -137,36 +143,68 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
// Extract out various useful fields we'll need below.
|
||||
let ConstraintConversion {
|
||||
tcx,
|
||||
infcx,
|
||||
region_bound_pairs,
|
||||
implicit_region_bound,
|
||||
known_type_outlives_obligations,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
let ty::OutlivesPredicate(k1, r2) = predicate;
|
||||
match k1.unpack() {
|
||||
GenericArgKind::Lifetime(r1) => {
|
||||
let r1_vid = self.to_region_vid(r1);
|
||||
let r2_vid = self.to_region_vid(r2);
|
||||
self.add_outlives(r1_vid, r2_vid, constraint_category);
|
||||
let mut outlives_predicates = vec![(predicate, constraint_category)];
|
||||
for iteration in 0.. {
|
||||
if outlives_predicates.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
GenericArgKind::Type(t1) => {
|
||||
// we don't actually use this for anything, but
|
||||
// the `TypeOutlives` code needs an origin.
|
||||
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
|
||||
|
||||
TypeOutlives::new(
|
||||
&mut *self,
|
||||
tcx,
|
||||
region_bound_pairs,
|
||||
Some(implicit_region_bound),
|
||||
known_type_outlives_obligations,
|
||||
)
|
||||
.type_must_outlive(origin, t1, r2, constraint_category);
|
||||
if !self.tcx.recursion_limit().value_within_limit(iteration) {
|
||||
bug!(
|
||||
"FIXME(-Znext-solver): Overflowed when processing region obligations: {outlives_predicates:#?}"
|
||||
);
|
||||
}
|
||||
|
||||
GenericArgKind::Const(_) => unreachable!(),
|
||||
let mut next_outlives_predicates = vec![];
|
||||
for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
|
||||
match k1.unpack() {
|
||||
GenericArgKind::Lifetime(r1) => {
|
||||
let r1_vid = self.to_region_vid(r1);
|
||||
let r2_vid = self.to_region_vid(r2);
|
||||
self.add_outlives(r1_vid, r2_vid, constraint_category);
|
||||
}
|
||||
|
||||
GenericArgKind::Type(mut t1) => {
|
||||
// Normalize the type we receive from a `TypeOutlives` obligation
|
||||
// in the new trait solver.
|
||||
if infcx.next_trait_solver() {
|
||||
t1 = self.normalize_and_add_type_outlives_constraints(
|
||||
t1,
|
||||
&mut next_outlives_predicates,
|
||||
);
|
||||
}
|
||||
|
||||
// we don't actually use this for anything, but
|
||||
// the `TypeOutlives` code needs an origin.
|
||||
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
|
||||
|
||||
TypeOutlives::new(
|
||||
&mut *self,
|
||||
tcx,
|
||||
region_bound_pairs,
|
||||
Some(implicit_region_bound),
|
||||
known_type_outlives_obligations,
|
||||
)
|
||||
.type_must_outlive(
|
||||
origin,
|
||||
t1,
|
||||
r2,
|
||||
constraint_category,
|
||||
);
|
||||
}
|
||||
|
||||
GenericArgKind::Const(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
outlives_predicates = next_outlives_predicates;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,6 +270,42 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
debug!("add_type_test(type_test={:?})", type_test);
|
||||
self.constraints.type_tests.push(type_test);
|
||||
}
|
||||
|
||||
fn normalize_and_add_type_outlives_constraints(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
next_outlives_predicates: &mut Vec<(
|
||||
ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>,
|
||||
ConstraintCategory<'tcx>,
|
||||
)>,
|
||||
) -> Ty<'tcx> {
|
||||
let result = CustomTypeOp::new(
|
||||
|ocx| {
|
||||
deeply_normalize(
|
||||
ocx.infcx.at(&ObligationCause::dummy_with_span(self.span), self.param_env),
|
||||
ty,
|
||||
)
|
||||
.map_err(|_| NoSolution)
|
||||
},
|
||||
"normalize type outlives obligation",
|
||||
)
|
||||
.fully_perform(self.infcx, self.span);
|
||||
|
||||
match result {
|
||||
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
|
||||
if let Some(constraints) = constraints {
|
||||
assert!(
|
||||
constraints.member_constraints.is_empty(),
|
||||
"no member constraints expected from normalizing: {:#?}",
|
||||
constraints.member_constraints
|
||||
);
|
||||
next_outlives_predicates.extend(constraints.outlives.iter().copied());
|
||||
}
|
||||
ty
|
||||
}
|
||||
Err(_) => ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'tcx> {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ use rustc_infer::infer::region_constraints::GenericKind;
|
|||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::traits::query::OutlivesBound;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
use rustc_span::{ErrorGuaranteed, DUMMY_SP};
|
||||
use rustc_trait_selection::solve::deeply_normalize;
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
|
||||
use std::rc::Rc;
|
||||
use type_op::TypeOpOutput;
|
||||
|
|
@ -52,7 +55,6 @@ pub(crate) struct CreateResult<'tcx> {
|
|||
pub(crate) fn create<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
implicit_region_bound: ty::Region<'tcx>,
|
||||
universal_regions: &Rc<UniversalRegions<'tcx>>,
|
||||
constraints: &mut MirTypeckRegionConstraints<'tcx>,
|
||||
|
|
@ -60,7 +62,6 @@ pub(crate) fn create<'tcx>(
|
|||
UniversalRegionRelationsBuilder {
|
||||
infcx,
|
||||
param_env,
|
||||
known_type_outlives_obligations,
|
||||
implicit_region_bound,
|
||||
constraints,
|
||||
universal_regions: universal_regions.clone(),
|
||||
|
|
@ -178,7 +179,6 @@ impl UniversalRegionRelations<'_> {
|
|||
struct UniversalRegionRelationsBuilder<'this, 'tcx> {
|
||||
infcx: &'this InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
universal_regions: Rc<UniversalRegions<'tcx>>,
|
||||
implicit_region_bound: ty::Region<'tcx>,
|
||||
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
|
||||
|
|
@ -222,6 +222,32 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
|
|||
self.relate_universal_regions(fr, fr_fn_body);
|
||||
}
|
||||
|
||||
// Normalize the assumptions we use to borrowck the program.
|
||||
let mut constraints = vec![];
|
||||
let mut known_type_outlives_obligations = vec![];
|
||||
for bound in param_env.caller_bounds() {
|
||||
let Some(mut outlives) = bound.as_type_outlives_clause() else { continue };
|
||||
|
||||
// In the new solver, normalize the type-outlives obligation assumptions.
|
||||
if self.infcx.next_trait_solver() {
|
||||
match deeply_normalize(
|
||||
self.infcx.at(&ObligationCause::misc(span, defining_ty_def_id), self.param_env),
|
||||
outlives,
|
||||
) {
|
||||
Ok(normalized_outlives) => {
|
||||
outlives = normalized_outlives;
|
||||
}
|
||||
Err(e) => {
|
||||
self.infcx.err_ctxt().report_fulfillment_errors(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
known_type_outlives_obligations.push(outlives);
|
||||
}
|
||||
let known_type_outlives_obligations =
|
||||
self.infcx.tcx.arena.alloc_slice(&known_type_outlives_obligations);
|
||||
|
||||
let unnormalized_input_output_tys = self
|
||||
.universal_regions
|
||||
.unnormalized_input_tys
|
||||
|
|
@ -239,7 +265,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
|
|||
// the `relations` is built.
|
||||
let mut normalized_inputs_and_output =
|
||||
Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
|
||||
let mut constraints = vec![];
|
||||
for ty in unnormalized_input_output_tys {
|
||||
debug!("build: input_or_output={:?}", ty);
|
||||
// We add implied bounds from both the unnormalized and normalized ty.
|
||||
|
|
@ -304,7 +329,19 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
|
|||
}
|
||||
|
||||
for c in constraints {
|
||||
self.push_region_constraints(c, span);
|
||||
constraint_conversion::ConstraintConversion::new(
|
||||
self.infcx,
|
||||
&self.universal_regions,
|
||||
&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
param_env,
|
||||
known_type_outlives_obligations,
|
||||
Locations::All(span),
|
||||
span,
|
||||
ConstraintCategory::Internal,
|
||||
self.constraints,
|
||||
)
|
||||
.convert_all(c);
|
||||
}
|
||||
|
||||
CreateResult {
|
||||
|
|
@ -313,30 +350,12 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
|
|||
outlives: self.outlives.freeze(),
|
||||
inverse_outlives: self.inverse_outlives.freeze(),
|
||||
}),
|
||||
known_type_outlives_obligations: self.known_type_outlives_obligations,
|
||||
known_type_outlives_obligations,
|
||||
region_bound_pairs: self.region_bound_pairs,
|
||||
normalized_inputs_and_output,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, data), level = "debug")]
|
||||
fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) {
|
||||
debug!("constraints generated: {:#?}", data);
|
||||
|
||||
constraint_conversion::ConstraintConversion::new(
|
||||
self.infcx,
|
||||
&self.universal_regions,
|
||||
&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.known_type_outlives_obligations,
|
||||
Locations::All(span),
|
||||
span,
|
||||
ConstraintCategory::Internal,
|
||||
self.constraints,
|
||||
)
|
||||
.convert_all(data);
|
||||
}
|
||||
|
||||
/// Update the type of a single local, which should represent
|
||||
/// either the return type of the MIR or one of its arguments. At
|
||||
/// the same time, compute and add any implied bounds that come
|
||||
|
|
|
|||
|
|
@ -7,13 +7,18 @@
|
|||
//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
|
||||
//! contain revealed `impl Trait` values).
|
||||
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_infer::infer::BoundRegionConversionTime;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
use crate::renumber::RegionCtxt;
|
||||
use crate::universal_regions::{DefiningTy, UniversalRegions};
|
||||
|
||||
use super::{Locations, TypeChecker};
|
||||
|
||||
|
|
@ -23,9 +28,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
#[instrument(skip(self, body), level = "debug")]
|
||||
pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
|
||||
let mir_def_id = body.source.def_id().expect_local();
|
||||
|
||||
if !self.tcx().is_closure_or_coroutine(mir_def_id.to_def_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let user_provided_poly_sig = self.tcx().closure_user_provided_sig(mir_def_id);
|
||||
|
||||
// Instantiate the canonicalized variables from user-provided signature
|
||||
|
|
@ -34,12 +41,75 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
// so that they represent the view from "inside" the closure.
|
||||
let user_provided_sig = self
|
||||
.instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig);
|
||||
let user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
body.span,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
user_provided_sig,
|
||||
);
|
||||
|
||||
// FIXME(async_closures): It's kind of wacky that we must apply this
|
||||
// transformation here, since we do the same thing in HIR typeck.
|
||||
// Maybe we could just fix up the canonicalized signature during HIR typeck?
|
||||
if let DefiningTy::CoroutineClosure(_, args) =
|
||||
self.borrowck_context.universal_regions.defining_ty
|
||||
{
|
||||
assert_matches!(
|
||||
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
|
||||
Some(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineSource::Closure
|
||||
)),
|
||||
"this needs to be modified if we're lowering non-async closures"
|
||||
);
|
||||
// Make sure to use the args from `DefiningTy` so the right NLL region vids are prepopulated
|
||||
// into the type.
|
||||
let args = args.as_coroutine_closure();
|
||||
let tupled_upvars_ty = ty::CoroutineClosureSignature::tupled_upvars_by_closure_kind(
|
||||
self.tcx(),
|
||||
args.kind(),
|
||||
Ty::new_tup(self.tcx(), user_provided_sig.inputs()),
|
||||
args.tupled_upvars_ty(),
|
||||
args.coroutine_captures_by_ref_ty(),
|
||||
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(body.span), || {
|
||||
RegionCtxt::Unknown
|
||||
}),
|
||||
);
|
||||
|
||||
let next_ty_var = || {
|
||||
self.infcx.next_ty_var(TypeVariableOrigin {
|
||||
span: body.span,
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
})
|
||||
};
|
||||
let output_ty = Ty::new_coroutine(
|
||||
self.tcx(),
|
||||
self.tcx().coroutine_for_closure(mir_def_id),
|
||||
ty::CoroutineArgs::new(
|
||||
self.tcx(),
|
||||
ty::CoroutineArgsParts {
|
||||
parent_args: args.parent_args(),
|
||||
kind_ty: Ty::from_closure_kind(self.tcx(), args.kind()),
|
||||
return_ty: user_provided_sig.output(),
|
||||
tupled_upvars_ty,
|
||||
// For async closures, none of these can be annotated, so just fill
|
||||
// them with fresh ty vars.
|
||||
resume_ty: next_ty_var(),
|
||||
yield_ty: next_ty_var(),
|
||||
witness: next_ty_var(),
|
||||
},
|
||||
)
|
||||
.args,
|
||||
);
|
||||
|
||||
user_provided_sig = self.tcx().mk_fn_sig(
|
||||
user_provided_sig.inputs().iter().copied(),
|
||||
output_ty,
|
||||
user_provided_sig.c_variadic,
|
||||
user_provided_sig.unsafety,
|
||||
user_provided_sig.abi,
|
||||
);
|
||||
}
|
||||
|
||||
let is_coroutine_with_implicit_resume_ty = self.tcx().is_coroutine(mir_def_id.to_def_id())
|
||||
&& user_provided_sig.inputs().is_empty();
|
||||
|
||||
|
|
|
|||
|
|
@ -156,10 +156,6 @@ pub(crate) fn type_check<'mir, 'tcx>(
|
|||
} = free_region_relations::create(
|
||||
infcx,
|
||||
param_env,
|
||||
// FIXME(-Znext-solver): These are unnormalized. Normalize them.
|
||||
infcx.tcx.arena.alloc_from_iter(
|
||||
param_env.caller_bounds().iter().filter_map(|clause| clause.as_type_outlives_clause()),
|
||||
),
|
||||
implicit_region_bound,
|
||||
universal_regions,
|
||||
&mut constraints,
|
||||
|
|
@ -808,6 +804,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
}),
|
||||
};
|
||||
}
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
return match args.as_coroutine_closure().upvar_tys().get(field.index()) {
|
||||
Some(&ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: args.as_coroutine_closure().upvar_tys().len(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
ty::Coroutine(_, args) => {
|
||||
// Only prefix fields (upvars and current state) are
|
||||
// accessible without a variant index.
|
||||
|
|
@ -1136,6 +1140,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
self.borrowck_context.universal_regions,
|
||||
self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
self.known_type_outlives_obligations,
|
||||
locations,
|
||||
locations.span(self.body),
|
||||
|
|
@ -1875,6 +1880,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
}),
|
||||
}
|
||||
}
|
||||
AggregateKind::CoroutineClosure(_, args) => {
|
||||
match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) {
|
||||
Some(ty) => Ok(*ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: args.as_coroutine_closure().upvar_tys().len(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
AggregateKind::Array(ty) => Ok(ty),
|
||||
AggregateKind::Tuple => {
|
||||
unreachable!("This should have been covered in check_rvalues");
|
||||
|
|
@ -2478,6 +2491,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
AggregateKind::Tuple => None,
|
||||
AggregateKind::Closure(_, _) => None,
|
||||
AggregateKind::Coroutine(_, _) => None,
|
||||
AggregateKind::CoroutineClosure(_, _) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -2705,7 +2719,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
// desugaring. A closure gets desugared to a struct, and
|
||||
// these extra requirements are basically like where
|
||||
// clauses on the struct.
|
||||
AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args) => (
|
||||
AggregateKind::Closure(def_id, args)
|
||||
| AggregateKind::CoroutineClosure(def_id, args)
|
||||
| AggregateKind::Coroutine(def_id, args) => (
|
||||
def_id,
|
||||
self.prove_closure_bounds(
|
||||
tcx,
|
||||
|
|
@ -2740,6 +2756,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
self.borrowck_context.universal_regions,
|
||||
self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
self.known_type_outlives_obligations,
|
||||
locations,
|
||||
DUMMY_SP, // irrelevant; will be overridden.
|
||||
|
|
@ -2754,10 +2771,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id);
|
||||
|
||||
let parent_args = match tcx.def_kind(def_id) {
|
||||
DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => {
|
||||
args.as_coroutine().parent_args()
|
||||
// We don't want to dispatch on 3 different kind of closures here, so take
|
||||
// advantage of the fact that the `parent_args` is the same length as the
|
||||
// `typeck_root_args`.
|
||||
DefKind::Closure => {
|
||||
// FIXME(async_closures): It may be useful to add a debug assert here
|
||||
// to actually call `type_of` and check the `parent_args` are the same
|
||||
// length as the `typeck_root_args`.
|
||||
&args[..typeck_root_args.len()]
|
||||
}
|
||||
DefKind::Closure => args.as_closure().parent_args(),
|
||||
DefKind::InlineConst => args.as_inline_const().parent_args(),
|
||||
other => bug!("unexpected item {:?}", other),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -97,6 +97,13 @@ pub enum DefiningTy<'tcx> {
|
|||
/// `ClosureArgs::coroutine_return_ty`.
|
||||
Coroutine(DefId, GenericArgsRef<'tcx>),
|
||||
|
||||
/// The MIR is a special kind of closure that returns coroutines.
|
||||
///
|
||||
/// See the documentation on `CoroutineClosureSignature` for details
|
||||
/// on how to construct the callable signature of the coroutine from
|
||||
/// its args.
|
||||
CoroutineClosure(DefId, GenericArgsRef<'tcx>),
|
||||
|
||||
/// The MIR is a fn item with the given `DefId` and args. The signature
|
||||
/// of the function can be bound then with the `fn_sig` query.
|
||||
FnDef(DefId, GenericArgsRef<'tcx>),
|
||||
|
|
@ -119,6 +126,7 @@ impl<'tcx> DefiningTy<'tcx> {
|
|||
pub fn upvar_tys(self) -> &'tcx ty::List<Ty<'tcx>> {
|
||||
match self {
|
||||
DefiningTy::Closure(_, args) => args.as_closure().upvar_tys(),
|
||||
DefiningTy::CoroutineClosure(_, args) => args.as_coroutine_closure().upvar_tys(),
|
||||
DefiningTy::Coroutine(_, args) => args.as_coroutine().upvar_tys(),
|
||||
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => {
|
||||
ty::List::empty()
|
||||
|
|
@ -131,7 +139,9 @@ impl<'tcx> DefiningTy<'tcx> {
|
|||
/// user's code.
|
||||
pub fn implicit_inputs(self) -> usize {
|
||||
match self {
|
||||
DefiningTy::Closure(..) | DefiningTy::Coroutine(..) => 1,
|
||||
DefiningTy::Closure(..)
|
||||
| DefiningTy::CoroutineClosure(..)
|
||||
| DefiningTy::Coroutine(..) => 1,
|
||||
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0,
|
||||
}
|
||||
}
|
||||
|
|
@ -147,6 +157,7 @@ impl<'tcx> DefiningTy<'tcx> {
|
|||
pub fn def_id(&self) -> DefId {
|
||||
match *self {
|
||||
DefiningTy::Closure(def_id, ..)
|
||||
| DefiningTy::CoroutineClosure(def_id, ..)
|
||||
| DefiningTy::Coroutine(def_id, ..)
|
||||
| DefiningTy::FnDef(def_id, ..)
|
||||
| DefiningTy::Const(def_id, ..)
|
||||
|
|
@ -355,6 +366,9 @@ impl<'tcx> UniversalRegions<'tcx> {
|
|||
err.note(format!("late-bound region is {:?}", self.to_region_vid(r)));
|
||||
});
|
||||
}
|
||||
DefiningTy::CoroutineClosure(..) => {
|
||||
todo!()
|
||||
}
|
||||
DefiningTy::Coroutine(def_id, args) => {
|
||||
let v = with_no_trimmed_paths!(
|
||||
args[tcx.generics_of(def_id).parent_count..]
|
||||
|
|
@ -568,6 +582,9 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
match *defining_ty.kind() {
|
||||
ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args),
|
||||
ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args),
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
DefiningTy::CoroutineClosure(def_id, args)
|
||||
}
|
||||
ty::FnDef(def_id, args) => DefiningTy::FnDef(def_id, args),
|
||||
_ => span_bug!(
|
||||
tcx.def_span(self.mir_def),
|
||||
|
|
@ -623,6 +640,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
let identity_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id);
|
||||
let fr_args = match defining_ty {
|
||||
DefiningTy::Closure(_, args)
|
||||
| DefiningTy::CoroutineClosure(_, args)
|
||||
| DefiningTy::Coroutine(_, args)
|
||||
| DefiningTy::InlineConst(_, args) => {
|
||||
// In the case of closures, we rely on the fact that
|
||||
|
|
@ -702,6 +720,55 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
ty::Binder::dummy(inputs_and_output)
|
||||
}
|
||||
|
||||
// Construct the signature of the CoroutineClosure for the purposes of borrowck.
|
||||
// This is pretty straightforward -- we:
|
||||
// 1. first grab the `coroutine_closure_sig`,
|
||||
// 2. compute the self type (`&`/`&mut`/no borrow),
|
||||
// 3. flatten the tupled_input_tys,
|
||||
// 4. construct the correct generator type to return with
|
||||
// `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`.
|
||||
// Then we wrap it all up into a list of inputs and output.
|
||||
DefiningTy::CoroutineClosure(def_id, args) => {
|
||||
assert_eq!(self.mir_def.to_def_id(), def_id);
|
||||
let closure_sig = args.as_coroutine_closure().coroutine_closure_sig();
|
||||
let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
|
||||
closure_sig
|
||||
.bound_vars()
|
||||
.iter()
|
||||
.chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
|
||||
);
|
||||
let br = ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind: ty::BrEnv,
|
||||
};
|
||||
let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br);
|
||||
let closure_kind = args.as_coroutine_closure().kind();
|
||||
|
||||
let closure_ty = tcx.closure_env_ty(
|
||||
Ty::new_coroutine_closure(tcx, def_id, args),
|
||||
closure_kind,
|
||||
env_region,
|
||||
);
|
||||
|
||||
let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields();
|
||||
let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
args.as_coroutine_closure().parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
closure_kind,
|
||||
env_region,
|
||||
args.as_coroutine_closure().tupled_upvars_ty(),
|
||||
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
|
||||
);
|
||||
|
||||
ty::Binder::bind_with_vars(
|
||||
tcx.mk_type_list_from_iter(
|
||||
iter::once(closure_ty).chain(inputs).chain(iter::once(output)),
|
||||
),
|
||||
bound_vars,
|
||||
)
|
||||
}
|
||||
|
||||
DefiningTy::FnDef(def_id, _) => {
|
||||
let sig = tcx.fn_sig(def_id).instantiate_identity();
|
||||
let sig = indices.fold_to_region_vids(tcx, sig);
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
|
|||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
|
||||
func.add_attribute(FnAttribute::Cold);
|
||||
}
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
|
||||
func.add_attribute(FnAttribute::ReturnsTwice);
|
||||
}
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
|
||||
func.add_attribute(FnAttribute::Pure);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
|
|||
// FIXME(eddyb) producing readable type names for trait objects can result
|
||||
// in problematically distinct types due to HRTB and subtyping (see #47638).
|
||||
// ty::Dynamic(..) |
|
||||
ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
|
||||
ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
|
||||
if !cx.sess().fewer_names() =>
|
||||
{
|
||||
let mut name = with_no_trimmed_paths!(layout.ty.to_string());
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ codegen_llvm_invalid_minimum_alignment_not_power_of_two =
|
|||
codegen_llvm_invalid_minimum_alignment_too_large =
|
||||
invalid minimum global alignment: {$align} is too large
|
||||
|
||||
codegen_llvm_invalid_target_feature_prefix = target feature `{$feature}` must begin with a `+` or `-`"
|
||||
|
||||
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
|
||||
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
|
||||
|
||||
|
|
|
|||
|
|
@ -356,9 +356,6 @@ pub fn from_fn_attrs<'ll, 'tcx>(
|
|||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
|
||||
to_add.push(AttributeKind::Cold.create_attr(cx.llcx));
|
||||
}
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
|
||||
to_add.push(AttributeKind::ReturnsTwice.create_attr(cx.llcx));
|
||||
}
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
|
||||
to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,3 +253,9 @@ pub struct MismatchedDataLayout<'a> {
|
|||
pub llvm_target: &'a str,
|
||||
pub llvm_layout: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_invalid_target_feature_prefix)]
|
||||
pub(crate) struct InvalidTargetFeaturePrefix<'a> {
|
||||
pub feature: &'a str,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,6 @@ pub enum AttributeKind {
|
|||
SanitizeMemory = 22,
|
||||
NonLazyBind = 23,
|
||||
OptimizeNone = 24,
|
||||
ReturnsTwice = 25,
|
||||
ReadNone = 26,
|
||||
SanitizeHWAddress = 28,
|
||||
WillReturn = 29,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::back::write::create_informational_target_machine;
|
||||
use crate::errors::{
|
||||
PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature,
|
||||
UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||
InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable,
|
||||
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
|
||||
};
|
||||
use crate::llvm;
|
||||
use libc::c_int;
|
||||
|
|
@ -511,7 +511,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
|||
sess.target
|
||||
.features
|
||||
.split(',')
|
||||
.filter(|v| !v.is_empty() && backend_feature_name(v).is_some())
|
||||
.filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some())
|
||||
.map(String::from),
|
||||
);
|
||||
|
||||
|
|
@ -535,7 +535,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
|||
}
|
||||
};
|
||||
|
||||
let feature = backend_feature_name(s)?;
|
||||
let feature = backend_feature_name(sess, s)?;
|
||||
// Warn against use of LLVM specific feature names and unstable features on the CLI.
|
||||
if diagnostics {
|
||||
let feature_state = supported_features.iter().find(|&&(v, _)| v == feature);
|
||||
|
|
@ -611,11 +611,11 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
|
|||
/// Returns a feature name for the given `+feature` or `-feature` string.
|
||||
///
|
||||
/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
|
||||
fn backend_feature_name(s: &str) -> Option<&str> {
|
||||
fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> {
|
||||
// features must start with a `+` or `-`.
|
||||
let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| {
|
||||
bug!("target feature `{}` must begin with a `+` or `-`", s);
|
||||
});
|
||||
let feature = s
|
||||
.strip_prefix(&['+', '-'][..])
|
||||
.unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s }));
|
||||
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
|
||||
// are not passed down to LLVM.
|
||||
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
|
||||
|
|
|
|||
|
|
@ -123,6 +123,17 @@ impl CodegenCx<'_, '_> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Match clang by only supporting COFF and ELF for now.
|
||||
if self.tcx.sess.target.is_like_osx {
|
||||
return false;
|
||||
}
|
||||
|
||||
// With pie relocation model calls of functions defined in the translation
|
||||
// unit can use copy relocations.
|
||||
if self.tcx.sess.relocation_model() == RelocModel::Pie && !is_declaration {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Thread-local variables generally don't support copy relocations.
|
||||
let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval)
|
||||
.is_some_and(|v| llvm::LLVMIsThreadLocal(v) == llvm::True);
|
||||
|
|
@ -130,18 +141,12 @@ impl CodegenCx<'_, '_> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Match clang by only supporting COFF and ELF for now.
|
||||
if self.tcx.sess.target.is_like_osx {
|
||||
return false;
|
||||
// Respect the direct-access-external-data to override default behavior if present.
|
||||
if let Some(direct) = self.tcx.sess.direct_access_external_data() {
|
||||
return direct;
|
||||
}
|
||||
|
||||
// Static relocation model should force copy relocations everywhere.
|
||||
if self.tcx.sess.relocation_model() == RelocModel::Static {
|
||||
return true;
|
||||
}
|
||||
|
||||
// With pie relocation model calls of functions defined in the translation
|
||||
// unit can use copy relocations.
|
||||
self.tcx.sess.relocation_model() == RelocModel::Pie && !is_declaration
|
||||
self.tcx.sess.relocation_model() == RelocModel::Static
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ fn uncached_llvm_type<'a, 'tcx>(
|
|||
// FIXME(eddyb) producing readable type names for trait objects can result
|
||||
// in problematically distinct types due to HRTB and subtyping (see #47638).
|
||||
// ty::Dynamic(..) |
|
||||
ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
|
||||
ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
|
||||
// For performance reasons we use names only when emitting LLVM IR.
|
||||
if !cx.sess().fewer_names() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1810,7 +1810,7 @@ impl Translate for SharedEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for SharedEmitter {
|
||||
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
|
||||
fn emit_diagnostic(&mut self, diag: rustc_errors::Diagnostic) {
|
||||
let args: FxHashMap<DiagnosticArgName, DiagnosticArgValue> =
|
||||
diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect();
|
||||
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
||||
|
|
|
|||
|
|
@ -103,9 +103,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
match name {
|
||||
sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
|
||||
sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
|
||||
sym::ffi_returns_twice => {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE
|
||||
}
|
||||
sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
|
||||
sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
|
||||
sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND,
|
||||
|
|
|
|||
|
|
@ -398,7 +398,9 @@ fn push_debuginfo_type_name<'tcx>(
|
|||
// processing
|
||||
visited.remove(&t);
|
||||
}
|
||||
ty::Closure(def_id, args) | ty::Coroutine(def_id, args, ..) => {
|
||||
ty::Closure(def_id, args)
|
||||
| ty::CoroutineClosure(def_id, args)
|
||||
| ty::Coroutine(def_id, args, ..) => {
|
||||
// Name will be "{closure_env#0}<T1, T2, ...>", "{coroutine_env#0}<T1, T2, ...>", or
|
||||
// "{async_fn_env#0}<T1, T2, ...>", etc.
|
||||
// In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
|
||||
|
|
@ -768,6 +770,8 @@ fn push_closure_or_coroutine_name<'tcx>(
|
|||
|
||||
// Truncate the args to the length of the above generics. This will cut off
|
||||
// anything closure- or coroutine-specific.
|
||||
// FIXME(async_closures): This is probably not going to be correct w.r.t.
|
||||
// multiple coroutine flavors. Maybe truncate to (parent + 1)?
|
||||
let args = args.truncate_to(tcx, generics);
|
||||
push_generic_params_internal(tcx, args, enclosing_fn_def_id, output, visited);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::mem;
|
||||
|
||||
use either::{Left, Right};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
|
|
@ -24,12 +22,13 @@ use crate::interpret::{
|
|||
};
|
||||
|
||||
// Returns a pointer to where the result lives
|
||||
#[instrument(level = "trace", skip(ecx, body), ret)]
|
||||
fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||
debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env);
|
||||
trace!(?ecx.param_env);
|
||||
let tcx = *ecx.tcx;
|
||||
assert!(
|
||||
cid.promoted.is_some()
|
||||
|
|
@ -75,11 +74,8 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
|||
None => InternKind::Constant,
|
||||
}
|
||||
};
|
||||
let check_alignment = mem::replace(&mut ecx.machine.check_alignment, CheckAlignment::No); // interning doesn't need to respect alignment
|
||||
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
||||
ecx.machine.check_alignment = check_alignment;
|
||||
|
||||
debug!("eval_body_using_ecx done: {:?}", ret);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
|||
| ty::Infer(_)
|
||||
// FIXME(oli-obk): we can probably encode closures just like structs
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType),
|
||||
}
|
||||
|
|
@ -301,6 +302,7 @@ pub fn valtree_to_const_value<'tcx>(
|
|||
| ty::Placeholder(..)
|
||||
| ty::Infer(_)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::FnPtr(_)
|
||||
|
|
|
|||
|
|
@ -1007,6 +1007,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
| ty::CoroutineWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_) => true,
|
||||
|
||||
|
|
|
|||
|
|
@ -41,13 +41,12 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
|
|||
/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be*
|
||||
/// already mutable (as a sanity check).
|
||||
///
|
||||
/// `recursive_alloc` is called for all recursively encountered allocations.
|
||||
/// Returns an iterator over all relocations referred to by this allocation.
|
||||
fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
|
||||
ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
|
||||
alloc_id: AllocId,
|
||||
mutability: Mutability,
|
||||
mut recursive_alloc: impl FnMut(&InterpCx<'mir, 'tcx, M>, CtfeProvenance),
|
||||
) -> Result<(), ()> {
|
||||
) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
|
||||
trace!("intern_shallow {:?}", alloc_id);
|
||||
// remove allocation
|
||||
let Some((_kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else {
|
||||
|
|
@ -65,14 +64,10 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
|
|||
assert_eq!(alloc.mutability, Mutability::Mut);
|
||||
}
|
||||
}
|
||||
// record child allocations
|
||||
for &(_, prov) in alloc.provenance().ptrs().iter() {
|
||||
recursive_alloc(ecx, prov);
|
||||
}
|
||||
// link the alloc id to the actual allocation
|
||||
let alloc = ecx.tcx.mk_const_alloc(alloc);
|
||||
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
Ok(())
|
||||
Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
|
||||
}
|
||||
|
||||
/// How a constant value should be interned.
|
||||
|
|
@ -128,12 +123,16 @@ pub fn intern_const_alloc_recursive<
|
|||
}
|
||||
};
|
||||
|
||||
// Initialize recursive interning.
|
||||
// Intern the base allocation, and initialize todo list for recursive interning.
|
||||
let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
|
||||
let mut todo = vec![(base_alloc_id, base_mutability)];
|
||||
// First we intern the base allocation, as it requires a different mutability.
|
||||
// This gives us the initial set of nested allocations, which will then all be processed
|
||||
// recursively in the loop below.
|
||||
let mut todo: Vec<_> =
|
||||
intern_shallow(ecx, base_alloc_id, base_mutability).unwrap().map(|prov| prov).collect();
|
||||
// We need to distinguish "has just been interned" from "was already in `tcx`",
|
||||
// so we track this in a separate set.
|
||||
let mut just_interned = FxHashSet::default();
|
||||
let mut just_interned: FxHashSet<_> = std::iter::once(base_alloc_id).collect();
|
||||
// Whether we encountered a bad mutable pointer.
|
||||
// We want to first report "dangling" and then "mutable", so we need to delay reporting these
|
||||
// errors.
|
||||
|
|
@ -147,52 +146,56 @@ pub fn intern_const_alloc_recursive<
|
|||
// raw pointers, so we cannot rely on validation to catch them -- and since interning runs
|
||||
// before validation, and interning doesn't know the type of anything, this means we can't show
|
||||
// better errors. Maybe we should consider doing validation before interning in the future.
|
||||
while let Some((alloc_id, mutability)) = todo.pop() {
|
||||
while let Some(prov) = todo.pop() {
|
||||
let alloc_id = prov.alloc_id();
|
||||
// Crucially, we check this *before* checking whether the `alloc_id`
|
||||
// has already been interned. The point of this check is to ensure that when
|
||||
// there are multiple pointers to the same allocation, they are *all* immutable.
|
||||
// Therefore it would be bad if we only checked the first pointer to any given
|
||||
// allocation.
|
||||
// (It is likely not possible to actually have multiple pointers to the same allocation,
|
||||
// so alternatively we could also check that and ICE if there are multiple such pointers.)
|
||||
if intern_kind != InternKind::Promoted
|
||||
&& inner_mutability == Mutability::Not
|
||||
&& !prov.immutable()
|
||||
{
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some()
|
||||
&& !just_interned.contains(&alloc_id)
|
||||
{
|
||||
// This is a pointer to some memory from another constant. We encounter mutable
|
||||
// pointers to such memory since we do not always track immutability through
|
||||
// these "global" pointers. Allowing them is harmless; the point of these checks
|
||||
// during interning is to justify why we intern the *new* allocations immutably,
|
||||
// so we can completely ignore existing allocations. We also don't need to add
|
||||
// this to the todo list, since after all it is already interned.
|
||||
continue;
|
||||
}
|
||||
// Found a mutable pointer inside a const where inner allocations should be
|
||||
// immutable. We exclude promoteds from this, since things like `&mut []` and
|
||||
// `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
|
||||
// on the promotion analysis not screwing up to ensure that it is sound to intern
|
||||
// promoteds as immutable.
|
||||
found_bad_mutable_pointer = true;
|
||||
}
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||
// Already interned.
|
||||
debug_assert!(!ecx.memory.alloc_map.contains_key(&alloc_id));
|
||||
continue;
|
||||
}
|
||||
just_interned.insert(alloc_id);
|
||||
intern_shallow(ecx, alloc_id, mutability, |ecx, prov| {
|
||||
let alloc_id = prov.alloc_id();
|
||||
if intern_kind != InternKind::Promoted
|
||||
&& inner_mutability == Mutability::Not
|
||||
&& !prov.immutable()
|
||||
{
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some()
|
||||
&& !just_interned.contains(&alloc_id)
|
||||
{
|
||||
// This is a pointer to some memory from another constant. We encounter mutable
|
||||
// pointers to such memory since we do not always track immutability through
|
||||
// these "global" pointers. Allowing them is harmless; the point of these checks
|
||||
// during interning is to justify why we intern the *new* allocations immutably,
|
||||
// so we can completely ignore existing allocations. We also don't need to add
|
||||
// this to the todo list, since after all it is already interned.
|
||||
return;
|
||||
}
|
||||
// Found a mutable pointer inside a const where inner allocations should be
|
||||
// immutable. We exclude promoteds from this, since things like `&mut []` and
|
||||
// `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
|
||||
// on the promotion analysis not screwing up to ensure that it is sound to intern
|
||||
// promoteds as immutable.
|
||||
found_bad_mutable_pointer = true;
|
||||
}
|
||||
// We always intern with `inner_mutability`, and furthermore we ensured above that if
|
||||
// that is "immutable", then there are *no* mutable pointers anywhere in the newly
|
||||
// interned memory -- justifying that we can indeed intern immutably. However this also
|
||||
// means we can *not* easily intern immutably here if `prov.immutable()` is true and
|
||||
// `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
|
||||
// we'd have to somehow check that they are *all* immutable before deciding that this
|
||||
// allocation can be made immutable. In the future we could consider analyzing all
|
||||
// pointers before deciding which allocations can be made immutable; but for now we are
|
||||
// okay with losing some potential for immutability here. This can anyway only affect
|
||||
// `static mut`.
|
||||
todo.push((alloc_id, inner_mutability));
|
||||
})
|
||||
.map_err(|()| {
|
||||
// We always intern with `inner_mutability`, and furthermore we ensured above that if
|
||||
// that is "immutable", then there are *no* mutable pointers anywhere in the newly
|
||||
// interned memory -- justifying that we can indeed intern immutably. However this also
|
||||
// means we can *not* easily intern immutably here if `prov.immutable()` is true and
|
||||
// `inner_mutability` is `Mut`: there might be other pointers to that allocation, and
|
||||
// we'd have to somehow check that they are *all* immutable before deciding that this
|
||||
// allocation can be made immutable. In the future we could consider analyzing all
|
||||
// pointers before deciding which allocations can be made immutable; but for now we are
|
||||
// okay with losing some potential for immutability here. This can anyway only affect
|
||||
// `static mut`.
|
||||
todo.extend(intern_shallow(ecx, alloc_id, inner_mutability).map_err(|()| {
|
||||
ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
|
||||
})?;
|
||||
})?);
|
||||
}
|
||||
if found_bad_mutable_pointer {
|
||||
return Err(ecx
|
||||
|
|
@ -220,13 +223,13 @@ pub fn intern_const_alloc_for_constprop<
|
|||
return Ok(());
|
||||
}
|
||||
// Move allocation to `tcx`.
|
||||
intern_shallow(ecx, alloc_id, Mutability::Not, |_ecx, _| {
|
||||
for _ in intern_shallow(ecx, alloc_id, Mutability::Not).map_err(|()| err_ub!(DeadLocal))? {
|
||||
// We are not doing recursive interning, so we don't currently support provenance.
|
||||
// (If this assertion ever triggers, we should just implement a
|
||||
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
|
||||
panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance")
|
||||
})
|
||||
.map_err(|()| err_ub!(DeadLocal).into())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
|
||||
|
|
@ -247,15 +250,14 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
|
|||
let dest = self.allocate(layout, MemoryKind::Stack)?;
|
||||
f(self, &dest.clone().into())?;
|
||||
let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance
|
||||
intern_shallow(self, alloc_id, Mutability::Not, |ecx, prov| {
|
||||
for prov in intern_shallow(self, alloc_id, Mutability::Not).unwrap() {
|
||||
// We are not doing recursive interning, so we don't currently support provenance.
|
||||
// (If this assertion ever triggers, we should just implement a
|
||||
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
|
||||
if !ecx.tcx.try_get_global_alloc(prov.alloc_id()).is_some() {
|
||||
if !self.tcx.try_get_global_alloc(prov.alloc_id()).is_some() {
|
||||
panic!("`intern_with_temp_alloc` with nested allocations");
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
Ok(alloc_id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
|
|||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::CoroutineClosure(_, _)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
|
|
|
|||
|
|
@ -545,6 +545,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
ty::InstanceDef::VTableShim(..)
|
||||
| ty::InstanceDef::ReifyShim(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| ty::InstanceDef::CoroutineKindShim { .. }
|
||||
| ty::InstanceDef::FnPtrShim(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
|
|
|
|||
|
|
@ -644,6 +644,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
| ty::Str
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..) => Ok(false),
|
||||
// Some types only occur during typechecking, they have no layout.
|
||||
// We should not see them here and we could not check them anyway.
|
||||
|
|
@ -992,10 +993,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// Complain about any other kind of error -- those are bad because we'd like to
|
||||
// report them in a way that shows *where* in the value the issue lies.
|
||||
Err(err) => {
|
||||
bug!(
|
||||
"Unexpected Undefined Behavior error during validation: {}",
|
||||
self.format_error(err)
|
||||
);
|
||||
bug!("Unexpected error during validation: {}", self.format_error(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use std::mem;
|
|||
use std::ops::{ControlFlow, Deref};
|
||||
|
||||
use super::ops::{self, NonConstOp, Status};
|
||||
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
|
||||
use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
|
||||
use super::resolver::FlowSensitiveAnalysis;
|
||||
use super::{ConstCx, Qualif};
|
||||
use crate::const_eval::is_unstable_const_fn;
|
||||
|
|
@ -149,37 +149,10 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
|
|||
|
||||
let return_loc = ccx.body.terminator_loc(return_block);
|
||||
|
||||
let custom_eq = match ccx.const_kind() {
|
||||
// We don't care whether a `const fn` returns a value that is not structurally
|
||||
// matchable. Functions calls are opaque and always use type-based qualification, so
|
||||
// this value should never be used.
|
||||
hir::ConstContext::ConstFn => true,
|
||||
|
||||
// If we know that all values of the return type are structurally matchable, there's no
|
||||
// need to run dataflow.
|
||||
// Opaque types do not participate in const generics or pattern matching, so we can safely count them out.
|
||||
_ if ccx.body.return_ty().has_opaque_types()
|
||||
|| !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) =>
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
hir::ConstContext::Const { .. } | hir::ConstContext::Static(_) => {
|
||||
let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx)
|
||||
.into_engine(ccx.tcx, ccx.body)
|
||||
.iterate_to_fixpoint()
|
||||
.into_results_cursor(ccx.body);
|
||||
|
||||
cursor.seek_after_primary_effect(return_loc);
|
||||
cursor.get().contains(RETURN_PLACE)
|
||||
}
|
||||
};
|
||||
|
||||
ConstQualifs {
|
||||
needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
|
||||
needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc),
|
||||
has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
|
||||
custom_eq,
|
||||
tainted_by_errors,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_middle::mir::*;
|
|||
use rustc_middle::traits::BuiltinImplSource;
|
||||
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
|
||||
use rustc_trait_selection::traits::{
|
||||
self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
|
||||
ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
|
||||
};
|
||||
|
||||
use super::ConstCx;
|
||||
|
|
@ -24,7 +24,6 @@ pub fn in_any_value_of_ty<'tcx>(
|
|||
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
|
||||
needs_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,
|
||||
}
|
||||
}
|
||||
|
|
@ -213,35 +212,6 @@ impl Qualif for NeedsNonConstDrop {
|
|||
}
|
||||
}
|
||||
|
||||
/// A constant that cannot be used as part of a pattern in a `match` expression.
|
||||
pub struct CustomEq;
|
||||
|
||||
impl Qualif for CustomEq {
|
||||
const ANALYSIS_NAME: &'static str = "flow_custom_eq";
|
||||
|
||||
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
|
||||
qualifs.custom_eq
|
||||
}
|
||||
|
||||
fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
// If *any* component of a composite data type does not implement `Structural{Partial,}Eq`,
|
||||
// we know that at least some values of that type are not structural-match. I say "some"
|
||||
// because that component may be part of an enum variant (e.g.,
|
||||
// `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
|
||||
// structural-match (`Option::None`).
|
||||
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
|
||||
}
|
||||
|
||||
fn in_adt_inherently<'tcx>(
|
||||
cx: &ConstCx<'_, 'tcx>,
|
||||
def: AdtDef<'tcx>,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
) -> bool {
|
||||
let ty = Ty::new_adt(cx.tcx, def, args);
|
||||
!ty.is_structural_eq_shallow(cx.tcx)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.
|
||||
|
||||
/// Returns `true` if this `Rvalue` contains qualif `Q`.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
|||
let body_abi = match body_ty.kind() {
|
||||
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
|
||||
ty::Closure(..) => Abi::RustCall,
|
||||
ty::CoroutineClosure(..) => Abi::RustCall,
|
||||
ty::Coroutine(..) => Abi::Rust,
|
||||
_ => {
|
||||
span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
|
||||
|
|
@ -665,6 +666,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
};
|
||||
check_equal(self, location, f_ty);
|
||||
}
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
let args = args.as_coroutine_closure();
|
||||
let Some(&f_ty) = args.upvar_tys().get(f.as_usize()) else {
|
||||
fail_out_of_bounds(self, location);
|
||||
return;
|
||||
};
|
||||
check_equal(self, location, f_ty);
|
||||
}
|
||||
&ty::Coroutine(def_id, args) => {
|
||||
let f_ty = if let Some(var) = parent_ty.variant_index {
|
||||
let gen_body = if def_id == self.body.source.def_id() {
|
||||
|
|
@ -861,6 +870,20 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
AggregateKind::CoroutineClosure(_, args) => {
|
||||
let upvars = args.as_coroutine_closure().upvar_tys();
|
||||
if upvars.len() != fields.len() {
|
||||
self.fail(
|
||||
location,
|
||||
"coroutine-closure has the wrong number of initialized fields",
|
||||
);
|
||||
}
|
||||
for (src, dest) in std::iter::zip(fields, upvars) {
|
||||
if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) {
|
||||
self.fail(location, "coroutine-closure field has the wrong type");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Rvalue::Ref(_, BorrowKind::Fake, _) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
| ty::FnDef(def_id, args)
|
||||
| ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
|
||||
| ty::Closure(def_id, args)
|
||||
| ty::CoroutineClosure(def_id, args)
|
||||
| ty::Coroutine(def_id, args) => self.print_def_path(def_id, args),
|
||||
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ use rustc_data_structures::profiling::{
|
|||
get_resident_set_size, print_time_passes_entry, TimePassesFormat,
|
||||
};
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, PResult};
|
||||
use rustc_errors::{
|
||||
markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult,
|
||||
};
|
||||
use rustc_feature::find_gated_cfg;
|
||||
use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
|
||||
use rustc_interface::{interface, Queries};
|
||||
|
|
@ -1231,11 +1233,10 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
|
|||
/// The compiler currently unwinds with a special sentinel value to abort
|
||||
/// compilation on fatal errors. This function catches that sentinel and turns
|
||||
/// the panic into a `Result` instead.
|
||||
pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuaranteed> {
|
||||
pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, FatalError> {
|
||||
catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
|
||||
if value.is::<rustc_errors::FatalErrorMarker>() {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
FatalError
|
||||
} else {
|
||||
panic::resume_unwind(value);
|
||||
}
|
||||
|
|
@ -1245,9 +1246,9 @@ pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuarantee
|
|||
/// Variant of `catch_fatal_errors` for the `interface::Result` return type
|
||||
/// that also computes the exit code.
|
||||
pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
|
||||
match catch_fatal_errors(f).flatten() {
|
||||
Ok(()) => EXIT_SUCCESS,
|
||||
Err(_) => EXIT_FAILURE,
|
||||
match catch_fatal_errors(f) {
|
||||
Ok(Ok(())) => EXIT_SUCCESS,
|
||||
_ => EXIT_FAILURE,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
|
||||
`#[ffi_returns_twice]` was used on something other than a foreign function
|
||||
declaration.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0724
|
||||
```compile_fail
|
||||
#![feature(ffi_returns_twice)]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
|
|
@ -15,7 +18,7 @@ pub fn foo() {}
|
|||
For example, we might correct the previous example by declaring
|
||||
the function inside of an `extern` block.
|
||||
|
||||
```
|
||||
```compile_fail
|
||||
#![feature(ffi_returns_twice)]
|
||||
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ impl From<Cow<'static, str>> for DiagnosticMessage {
|
|||
}
|
||||
}
|
||||
|
||||
/// A workaround for "good path" ICEs when formatting types in disabled lints.
|
||||
/// A workaround for good_path_delayed_bug ICEs when formatting types in disabled lints.
|
||||
///
|
||||
/// Delays formatting until `.into(): DiagnosticMessage` is used.
|
||||
pub struct DelayDm<F>(pub F);
|
||||
|
|
|
|||
|
|
@ -44,15 +44,15 @@ impl Translate for AnnotateSnippetEmitter {
|
|||
|
||||
impl Emitter for AnnotateSnippetEmitter {
|
||||
/// The entry point for the diagnostics generation
|
||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||
let fluent_args = to_fluent_args(diag.args());
|
||||
|
||||
let mut children = diag.children.clone();
|
||||
let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
|
||||
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
||||
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||
|
||||
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
||||
&mut primary_span,
|
||||
&mut children,
|
||||
&mut diag.span,
|
||||
&mut diag.children,
|
||||
&diag.level,
|
||||
self.macro_backtrace,
|
||||
);
|
||||
|
|
@ -62,9 +62,9 @@ impl Emitter for AnnotateSnippetEmitter {
|
|||
&diag.messages,
|
||||
&fluent_args,
|
||||
&diag.code,
|
||||
&primary_span,
|
||||
&children,
|
||||
suggestions,
|
||||
&diag.span,
|
||||
&diag.children,
|
||||
&suggestions,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +85,11 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
|
|||
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
|
||||
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
||||
match level {
|
||||
Level::Bug | Level::DelayedBug(_) | Level::Fatal | Level::Error => AnnotationType::Error,
|
||||
Level::Bug
|
||||
| Level::Fatal
|
||||
| Level::Error
|
||||
| Level::DelayedBug
|
||||
| Level::GoodPathDelayedBug => AnnotationType::Error,
|
||||
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
||||
Level::Note | Level::OnceNote => AnnotationType::Note,
|
||||
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::snippet::Style;
|
||||
use crate::{
|
||||
CodeSuggestion, DelayedBugKind, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee,
|
||||
ErrCode, Level, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart,
|
||||
SuggestionStyle,
|
||||
CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level,
|
||||
MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
|
||||
|
|
@ -235,19 +234,16 @@ impl Diagnostic {
|
|||
|
||||
pub fn is_error(&self) -> bool {
|
||||
match self.level {
|
||||
Level::Bug
|
||||
| Level::DelayedBug(DelayedBugKind::Normal)
|
||||
| Level::Fatal
|
||||
| Level::Error
|
||||
| Level::FailureNote => true,
|
||||
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
|
||||
|
||||
Level::ForceWarning(_)
|
||||
Level::GoodPathDelayedBug
|
||||
| Level::ForceWarning(_)
|
||||
| Level::Warning
|
||||
| Level::DelayedBug(DelayedBugKind::GoodPath)
|
||||
| Level::Note
|
||||
| Level::OnceNote
|
||||
| Level::Help
|
||||
| Level::OnceHelp
|
||||
| Level::FailureNote
|
||||
| Level::Allow
|
||||
| Level::Expect(_) => false,
|
||||
}
|
||||
|
|
@ -306,11 +302,11 @@ impl Diagnostic {
|
|||
#[track_caller]
|
||||
pub fn downgrade_to_delayed_bug(&mut self) {
|
||||
assert!(
|
||||
self.is_error(),
|
||||
matches!(self.level, Level::Error | Level::DelayedBug),
|
||||
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
|
||||
self.level
|
||||
);
|
||||
self.level = Level::DelayedBug(DelayedBugKind::Normal);
|
||||
self.level = Level::DelayedBug;
|
||||
}
|
||||
|
||||
/// Appends a labeled span to the diagnostic.
|
||||
|
|
|
|||
|
|
@ -99,16 +99,20 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
|||
}
|
||||
|
||||
/// `ErrorGuaranteed::emit_producing_guarantee` uses this.
|
||||
// FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
|
||||
fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
|
||||
let diag = self.take_diag();
|
||||
|
||||
// Only allow a guarantee if the `level` wasn't switched to a
|
||||
// non-error. The field isn't `pub`, but the whole `Diagnostic` can be
|
||||
// overwritten with a new one, thanks to `DerefMut`.
|
||||
// The only error levels that produce `ErrorGuaranteed` are
|
||||
// `Error` and `DelayedBug`. But `DelayedBug` should never occur here
|
||||
// because delayed bugs have their level changed to `Bug` when they are
|
||||
// actually printed, so they produce an ICE.
|
||||
//
|
||||
// (Also, even though `level` isn't `pub`, the whole `Diagnostic` could
|
||||
// be overwritten with a new one thanks to `DerefMut`. So this assert
|
||||
// protects against that, too.)
|
||||
assert!(
|
||||
diag.is_error(),
|
||||
"emitted non-error ({:?}) diagnostic from `DiagnosticBuilder<ErrorGuaranteed>`",
|
||||
matches!(diag.level, Level::Error | Level::DelayedBug),
|
||||
"invalid diagnostic level ({:?})",
|
||||
diag.level,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ pub type DynEmitter = dyn Emitter + DynSend;
|
|||
/// Emitter trait for emitting errors.
|
||||
pub trait Emitter: Translate {
|
||||
/// Emit a structured diagnostic.
|
||||
fn emit_diagnostic(&mut self, diag: &Diagnostic);
|
||||
fn emit_diagnostic(&mut self, diag: Diagnostic);
|
||||
|
||||
/// Emit a notification that an artifact has been output.
|
||||
/// Currently only supported for the JSON format.
|
||||
|
|
@ -230,17 +230,17 @@ pub trait Emitter: Translate {
|
|||
///
|
||||
/// * If the current `Diagnostic` has only one visible `CodeSuggestion`,
|
||||
/// we format the `help` suggestion depending on the content of the
|
||||
/// substitutions. In that case, we return the modified span only.
|
||||
/// substitutions. In that case, we modify the span and clear the
|
||||
/// suggestions.
|
||||
///
|
||||
/// * If the current `Diagnostic` has multiple suggestions,
|
||||
/// we return the original `primary_span` and the original suggestions.
|
||||
fn primary_span_formatted<'a>(
|
||||
/// we leave `primary_span` and the suggestions untouched.
|
||||
fn primary_span_formatted(
|
||||
&mut self,
|
||||
diag: &'a Diagnostic,
|
||||
primary_span: &mut MultiSpan,
|
||||
suggestions: &mut Vec<CodeSuggestion>,
|
||||
fluent_args: &FluentArgs<'_>,
|
||||
) -> (MultiSpan, &'a [CodeSuggestion]) {
|
||||
let mut primary_span = diag.span.clone();
|
||||
let suggestions = diag.suggestions.as_deref().unwrap_or(&[]);
|
||||
) {
|
||||
if let Some((sugg, rest)) = suggestions.split_first() {
|
||||
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
|
||||
if rest.is_empty() &&
|
||||
|
|
@ -287,16 +287,15 @@ pub trait Emitter: Translate {
|
|||
primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg);
|
||||
|
||||
// We return only the modified primary_span
|
||||
(primary_span, &[])
|
||||
suggestions.clear();
|
||||
} else {
|
||||
// if there are multiple suggestions, print them all in full
|
||||
// to be consistent. We could try to figure out if we can
|
||||
// make one (or the first one) inline, but that would give
|
||||
// undue importance to a semi-random suggestion
|
||||
(primary_span, suggestions)
|
||||
}
|
||||
} else {
|
||||
(primary_span, suggestions)
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -518,16 +517,15 @@ impl Emitter for HumanEmitter {
|
|||
self.sm.as_ref()
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||
let fluent_args = to_fluent_args(diag.args());
|
||||
|
||||
let mut children = diag.children.clone();
|
||||
let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
|
||||
debug!("emit_diagnostic: suggestions={:?}", suggestions);
|
||||
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
||||
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||
|
||||
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
|
||||
&mut primary_span,
|
||||
&mut children,
|
||||
&mut diag.span,
|
||||
&mut diag.children,
|
||||
&diag.level,
|
||||
self.macro_backtrace,
|
||||
);
|
||||
|
|
@ -537,9 +535,9 @@ impl Emitter for HumanEmitter {
|
|||
&diag.messages,
|
||||
&fluent_args,
|
||||
&diag.code,
|
||||
&primary_span,
|
||||
&children,
|
||||
suggestions,
|
||||
&diag.span,
|
||||
&diag.children,
|
||||
&suggestions,
|
||||
self.track_diagnostics.then_some(&diag.emitted_at),
|
||||
);
|
||||
}
|
||||
|
|
@ -576,9 +574,8 @@ impl Emitter for SilentEmitter {
|
|||
None
|
||||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
|
||||
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||
if diag.level == Level::Fatal {
|
||||
let mut diag = diag.clone();
|
||||
diag.note(self.fatal_note.clone());
|
||||
self.fatal_dcx.emit_diagnostic(diag);
|
||||
}
|
||||
|
|
@ -2116,6 +2113,7 @@ impl HumanEmitter {
|
|||
}
|
||||
if !self.short_message {
|
||||
for child in children {
|
||||
assert!(child.level.can_be_top_or_sub().1);
|
||||
let span = &child.span;
|
||||
if let Err(err) = self.emit_messages_default_inner(
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ impl Translate for JsonEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for JsonEmitter {
|
||||
fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
|
||||
fn emit_diagnostic(&mut self, diag: crate::Diagnostic) {
|
||||
let data = Diagnostic::from_errors_diagnostic(diag, self);
|
||||
let result = self.emit(EmitTyped::Diagnostic(data));
|
||||
if let Err(e) = result {
|
||||
|
|
@ -201,7 +201,7 @@ impl Emitter for JsonEmitter {
|
|||
}
|
||||
FutureBreakageItem {
|
||||
diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
|
||||
&diag, self,
|
||||
diag, self,
|
||||
)),
|
||||
}
|
||||
})
|
||||
|
|
@ -340,7 +340,7 @@ struct UnusedExterns<'a, 'b, 'c> {
|
|||
}
|
||||
|
||||
impl Diagnostic {
|
||||
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
||||
fn from_errors_diagnostic(diag: crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
||||
let args = to_fluent_args(diag.args());
|
||||
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
||||
let translated_message =
|
||||
|
|
@ -382,6 +382,28 @@ impl Diagnostic {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let translated_message = je.translate_messages(&diag.messages, &args);
|
||||
|
||||
let code = if let Some(code) = diag.code {
|
||||
Some(DiagnosticCode {
|
||||
code: code.to_string(),
|
||||
explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(),
|
||||
})
|
||||
} else if let Some(IsLint { name, .. }) = &diag.is_lint {
|
||||
Some(DiagnosticCode { code: name.to_string(), explanation: None })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let level = diag.level.to_str();
|
||||
let spans = DiagnosticSpan::from_multispan(&diag.span, &args, je);
|
||||
let children = diag
|
||||
.children
|
||||
.iter()
|
||||
.map(|c| Diagnostic::from_sub_diagnostic(c, &args, je))
|
||||
.chain(sugg)
|
||||
.collect();
|
||||
|
||||
let buf = BufWriter::default();
|
||||
let output = buf.clone();
|
||||
je.json_rendered
|
||||
|
|
@ -398,30 +420,12 @@ impl Diagnostic {
|
|||
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
|
||||
let output = String::from_utf8(output).unwrap();
|
||||
|
||||
let translated_message = je.translate_messages(&diag.messages, &args);
|
||||
|
||||
let code = if let Some(code) = diag.code {
|
||||
Some(DiagnosticCode {
|
||||
code: code.to_string(),
|
||||
explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(),
|
||||
})
|
||||
} else if let Some(IsLint { name, .. }) = &diag.is_lint {
|
||||
Some(DiagnosticCode { code: name.to_string(), explanation: None })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Diagnostic {
|
||||
message: translated_message.to_string(),
|
||||
code,
|
||||
level: diag.level.to_str(),
|
||||
spans: DiagnosticSpan::from_multispan(&diag.span, &args, je),
|
||||
children: diag
|
||||
.children
|
||||
.iter()
|
||||
.map(|c| Diagnostic::from_sub_diagnostic(c, &args, je))
|
||||
.chain(sugg)
|
||||
.collect(),
|
||||
level,
|
||||
spans,
|
||||
children,
|
||||
rendered: Some(output),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ struct DiagCtxtInner {
|
|||
has_printed: bool,
|
||||
|
||||
emitter: Box<DynEmitter>,
|
||||
span_delayed_bugs: Vec<DelayedDiagnostic>,
|
||||
delayed_bugs: Vec<DelayedDiagnostic>,
|
||||
good_path_delayed_bugs: Vec<DelayedDiagnostic>,
|
||||
/// This flag indicates that an expected diagnostic was emitted and suppressed.
|
||||
/// This is used for the `good_path_delayed_bugs` check.
|
||||
|
|
@ -523,8 +523,7 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
|
|||
pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
|
||||
AtomicRef::new(&(default_track_diagnostic as _));
|
||||
|
||||
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
|
||||
pub enum DelayedBugKind {
|
||||
enum DelayedBugKind {
|
||||
Normal,
|
||||
GoodPath,
|
||||
}
|
||||
|
|
@ -557,11 +556,6 @@ impl Drop for DiagCtxtInner {
|
|||
self.flush_delayed(DelayedBugKind::Normal)
|
||||
}
|
||||
|
||||
// FIXME(eddyb) this explains what `good_path_delayed_bugs` are!
|
||||
// They're `span_delayed_bugs` but for "require some diagnostic happened"
|
||||
// instead of "require some error happened". Sadly that isn't ideal, as
|
||||
// lints can be `#[allow]`'d, potentially leading to this triggering.
|
||||
// Also, "good path" should be replaced with a better naming.
|
||||
if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
|
||||
self.flush_delayed(DelayedBugKind::GoodPath);
|
||||
}
|
||||
|
|
@ -608,7 +602,7 @@ impl DiagCtxt {
|
|||
deduplicated_warn_count: 0,
|
||||
has_printed: false,
|
||||
emitter,
|
||||
span_delayed_bugs: Vec::new(),
|
||||
delayed_bugs: Vec::new(),
|
||||
good_path_delayed_bugs: Vec::new(),
|
||||
suppressed_expected_diag: false,
|
||||
taught_diagnostics: Default::default(),
|
||||
|
|
@ -665,7 +659,7 @@ impl DiagCtxt {
|
|||
inner.has_printed = false;
|
||||
|
||||
// actually free the underlying memory (which `clear` would not do)
|
||||
inner.span_delayed_bugs = Default::default();
|
||||
inner.delayed_bugs = Default::default();
|
||||
inner.good_path_delayed_bugs = Default::default();
|
||||
inner.taught_diagnostics = Default::default();
|
||||
inner.emitted_diagnostic_codes = Default::default();
|
||||
|
|
@ -714,7 +708,7 @@ impl DiagCtxt {
|
|||
}
|
||||
|
||||
/// Emit all stashed diagnostics.
|
||||
pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
|
||||
pub fn emit_stashed_diagnostics(&self) {
|
||||
self.inner.borrow_mut().emit_stashed_diagnostics()
|
||||
}
|
||||
|
||||
|
|
@ -866,8 +860,7 @@ impl DiagCtxt {
|
|||
/// directly).
|
||||
#[track_caller]
|
||||
pub fn delayed_bug(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
|
||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg)
|
||||
.emit()
|
||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit()
|
||||
}
|
||||
|
||||
/// Like `delayed_bug`, but takes an additional span.
|
||||
|
|
@ -880,15 +873,12 @@ impl DiagCtxt {
|
|||
sp: impl Into<MultiSpan>,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
) -> ErrorGuaranteed {
|
||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg)
|
||||
.with_span(sp)
|
||||
.emit()
|
||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit()
|
||||
}
|
||||
|
||||
// FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
|
||||
// where the explanation of what "good path" is (also, it should be renamed).
|
||||
/// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`.
|
||||
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
||||
DiagnosticBuilder::<()>::new(self, DelayedBug(DelayedBugKind::GoodPath), msg).emit()
|
||||
DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -941,8 +931,9 @@ impl DiagCtxt {
|
|||
/// This excludes lint errors and delayed bugs.
|
||||
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors().then(|| {
|
||||
// FIXME(nnethercote) find a way to store an `ErrorGuaranteed`.
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
ErrorGuaranteed::unchecked_error_guaranteed()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -952,8 +943,9 @@ impl DiagCtxt {
|
|||
let inner = self.inner.borrow();
|
||||
let result = inner.has_errors() || inner.lint_err_count > 0;
|
||||
result.then(|| {
|
||||
// FIXME(nnethercote) find a way to store an `ErrorGuaranteed`.
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
ErrorGuaranteed::unchecked_error_guaranteed()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -962,10 +954,11 @@ impl DiagCtxt {
|
|||
pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
let inner = self.inner.borrow();
|
||||
let result =
|
||||
inner.has_errors() || inner.lint_err_count > 0 || !inner.span_delayed_bugs.is_empty();
|
||||
inner.has_errors() || inner.lint_err_count > 0 || !inner.delayed_bugs.is_empty();
|
||||
result.then(|| {
|
||||
// FIXME(nnethercote) find a way to store an `ErrorGuaranteed`.
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
ErrorGuaranteed::unchecked_error_guaranteed()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -991,9 +984,13 @@ impl DiagCtxt {
|
|||
|
||||
match (errors.len(), warnings.len()) {
|
||||
(0, 0) => return,
|
||||
(0, _) => inner
|
||||
.emitter
|
||||
.emit_diagnostic(&Diagnostic::new(Warning, DiagnosticMessage::Str(warnings))),
|
||||
(0, _) => {
|
||||
// Use `inner.emitter` directly, otherwise the warning might not be emitted, e.g.
|
||||
// with a configuration like `--cap-lints allow --force-warn bare_trait_objects`.
|
||||
inner
|
||||
.emitter
|
||||
.emit_diagnostic(Diagnostic::new(Warning, DiagnosticMessage::Str(warnings)));
|
||||
}
|
||||
(_, 0) => {
|
||||
inner.emit_diagnostic(Diagnostic::new(Fatal, errors));
|
||||
}
|
||||
|
|
@ -1057,7 +1054,7 @@ impl DiagCtxt {
|
|||
}
|
||||
|
||||
pub fn force_print_diagnostic(&self, db: Diagnostic) {
|
||||
self.inner.borrow_mut().emitter.emit_diagnostic(&db);
|
||||
self.inner.borrow_mut().emitter.emit_diagnostic(db);
|
||||
}
|
||||
|
||||
pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
||||
|
|
@ -1222,9 +1219,8 @@ impl DiagCtxt {
|
|||
// `DiagCtxtInner::foo`.
|
||||
impl DiagCtxtInner {
|
||||
/// Emit all stashed diagnostics.
|
||||
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
fn emit_stashed_diagnostics(&mut self) {
|
||||
let has_errors = self.has_errors();
|
||||
let mut reported = None;
|
||||
for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
|
||||
// Decrement the count tracking the stash; emitting will increment it.
|
||||
if diag.is_error() {
|
||||
|
|
@ -1241,50 +1237,25 @@ impl DiagCtxtInner {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let reported_this = self.emit_diagnostic(diag);
|
||||
reported = reported.or(reported_this);
|
||||
self.emit_diagnostic(diag);
|
||||
}
|
||||
reported
|
||||
}
|
||||
|
||||
// Return value is only `Some` if the level is `Error` or `DelayedBug`.
|
||||
fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
|
||||
// The `LintExpectationId` can be stable or unstable depending on when it was created.
|
||||
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
|
||||
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
|
||||
// a stable one by the `LintLevelsBuilder`.
|
||||
if let Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() {
|
||||
self.unstable_expect_diagnostics.push(diagnostic);
|
||||
return None;
|
||||
}
|
||||
assert!(diagnostic.level.can_be_top_or_sub().0);
|
||||
|
||||
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
||||
// once *any* errors were emitted (and truncate `span_delayed_bugs`
|
||||
// when an error is first emitted, also), but maybe there's a case
|
||||
// in which that's not sound? otherwise this is really inefficient.
|
||||
match diagnostic.level {
|
||||
DelayedBug(_) if self.flags.eagerly_emit_delayed_bugs => {
|
||||
diagnostic.level = Error;
|
||||
}
|
||||
DelayedBug(DelayedBugKind::Normal) => {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
self.span_delayed_bugs
|
||||
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
#[allow(deprecated)]
|
||||
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||
}
|
||||
DelayedBug(DelayedBugKind::GoodPath) => {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
self.good_path_delayed_bugs
|
||||
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
|
||||
// The `LintExpectationId` can be stable or unstable depending on when it was created.
|
||||
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
|
||||
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
|
||||
// a stable one by the `LintLevelsBuilder`.
|
||||
if let LintExpectationId::Unstable { .. } = expectation_id {
|
||||
self.unstable_expect_diagnostics.push(diagnostic);
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// This must come after the possible promotion of `DelayedBug` to
|
||||
// `Error` above.
|
||||
if matches!(diagnostic.level, Error | Fatal) && self.treat_next_err_as_bug() {
|
||||
diagnostic.level = Bug;
|
||||
self.suppressed_expected_diag = true;
|
||||
self.fulfilled_expectations.insert(expectation_id.normalize());
|
||||
}
|
||||
|
||||
if diagnostic.has_future_breakage() {
|
||||
|
|
@ -1295,21 +1266,45 @@ impl DiagCtxtInner {
|
|||
self.future_breakage_diagnostics.push(diagnostic.clone());
|
||||
}
|
||||
|
||||
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
|
||||
self.suppressed_expected_diag = true;
|
||||
self.fulfilled_expectations.insert(expectation_id.normalize());
|
||||
if matches!(diagnostic.level, DelayedBug | GoodPathDelayedBug)
|
||||
&& self.flags.eagerly_emit_delayed_bugs
|
||||
{
|
||||
diagnostic.level = Error;
|
||||
}
|
||||
|
||||
if diagnostic.level == Warning && !self.flags.can_emit_warnings {
|
||||
if diagnostic.has_future_breakage() {
|
||||
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
||||
match diagnostic.level {
|
||||
// This must come after the possible promotion of `DelayedBug`/`GoodPathDelayedBug` to
|
||||
// `Error` above.
|
||||
Fatal | Error if self.treat_next_err_as_bug() => {
|
||||
diagnostic.level = Bug;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if matches!(diagnostic.level, Expect(_) | Allow) {
|
||||
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
||||
return None;
|
||||
DelayedBug => {
|
||||
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
||||
// once *any* errors were emitted (and truncate `delayed_bugs`
|
||||
// when an error is first emitted, also), but maybe there's a case
|
||||
// in which that's not sound? otherwise this is really inefficient.
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
self.delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
#[allow(deprecated)]
|
||||
return Some(ErrorGuaranteed::unchecked_error_guaranteed());
|
||||
}
|
||||
GoodPathDelayedBug => {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
self.good_path_delayed_bugs
|
||||
.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
||||
return None;
|
||||
}
|
||||
Warning if !self.flags.can_emit_warnings => {
|
||||
if diagnostic.has_future_breakage() {
|
||||
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
||||
}
|
||||
return None;
|
||||
}
|
||||
Allow | Expect(_) => {
|
||||
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut guaranteed = None;
|
||||
|
|
@ -1325,11 +1320,16 @@ impl DiagCtxtInner {
|
|||
!self.emitted_diagnostics.insert(diagnostic_hash)
|
||||
};
|
||||
|
||||
let level = diagnostic.level;
|
||||
let is_error = diagnostic.is_error();
|
||||
let is_lint = diagnostic.is_lint.is_some();
|
||||
|
||||
// Only emit the diagnostic if we've been asked to deduplicate or
|
||||
// haven't already emitted an equivalent diagnostic.
|
||||
if !(self.flags.deduplicate_diagnostics && already_emitted) {
|
||||
debug!(?diagnostic);
|
||||
debug!(?self.emitted_diagnostics);
|
||||
|
||||
let already_emitted_sub = |sub: &mut SubDiagnostic| {
|
||||
debug!(?sub);
|
||||
if sub.level != OnceNote && sub.level != OnceHelp {
|
||||
|
|
@ -1341,7 +1341,6 @@ impl DiagCtxtInner {
|
|||
debug!(?diagnostic_hash);
|
||||
!self.emitted_diagnostics.insert(diagnostic_hash)
|
||||
};
|
||||
|
||||
diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
|
||||
if already_emitted {
|
||||
diagnostic.note(
|
||||
|
|
@ -1349,26 +1348,28 @@ impl DiagCtxtInner {
|
|||
);
|
||||
}
|
||||
|
||||
self.emitter.emit_diagnostic(&diagnostic);
|
||||
if diagnostic.is_error() {
|
||||
if is_error {
|
||||
self.deduplicated_err_count += 1;
|
||||
} else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
|
||||
self.deduplicated_warn_count += 1;
|
||||
}
|
||||
self.has_printed = true;
|
||||
|
||||
self.emitter.emit_diagnostic(diagnostic);
|
||||
}
|
||||
if diagnostic.is_error() {
|
||||
if diagnostic.is_lint.is_some() {
|
||||
|
||||
if is_error {
|
||||
if is_lint {
|
||||
self.lint_err_count += 1;
|
||||
} else {
|
||||
self.err_count += 1;
|
||||
}
|
||||
self.panic_if_treat_err_as_bug();
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
guaranteed = Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
if level == Level::Error {
|
||||
guaranteed = Some(ErrorGuaranteed::unchecked_error_guaranteed());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1397,12 +1398,12 @@ impl DiagCtxtInner {
|
|||
fn flush_delayed(&mut self, kind: DelayedBugKind) {
|
||||
let (bugs, note1) = match kind {
|
||||
DelayedBugKind::Normal => (
|
||||
std::mem::take(&mut self.span_delayed_bugs),
|
||||
"no errors encountered even though `span_delayed_bug` issued",
|
||||
std::mem::take(&mut self.delayed_bugs),
|
||||
"no errors encountered even though delayed bugs were created",
|
||||
),
|
||||
DelayedBugKind::GoodPath => (
|
||||
std::mem::take(&mut self.good_path_delayed_bugs),
|
||||
"no warnings or errors encountered even though `good_path_delayed_bugs` issued",
|
||||
"no warnings or errors encountered even though good path delayed bugs were created",
|
||||
),
|
||||
};
|
||||
let note2 = "those delayed bugs will now be shown as internal compiler errors";
|
||||
|
|
@ -1441,8 +1442,8 @@ impl DiagCtxtInner {
|
|||
let mut bug =
|
||||
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
|
||||
|
||||
// "Undelay" the `DelayedBug`s (into plain `Bug`s).
|
||||
if !matches!(bug.level, DelayedBug(_)) {
|
||||
// "Undelay" the delayed bugs (into plain `Bug`s).
|
||||
if !matches!(bug.level, DelayedBug | GoodPathDelayedBug) {
|
||||
// NOTE(eddyb) not panicking here because we're already producing
|
||||
// an ICE, and the more information the merrier.
|
||||
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
|
||||
|
|
@ -1508,34 +1509,58 @@ impl DelayedDiagnostic {
|
|||
}
|
||||
}
|
||||
|
||||
/// Level is_error EmissionGuarantee Top-level Sub Used in lints?
|
||||
/// ----- -------- ----------------- --------- --- --------------
|
||||
/// Bug yes BugAbort yes - -
|
||||
/// Fatal yes FatalAbort/FatalError(*) yes - -
|
||||
/// Error yes ErrorGuaranteed yes - yes
|
||||
/// DelayedBug yes ErrorGuaranteed yes - -
|
||||
/// GoodPathDelayedBug - () yes - -
|
||||
/// ForceWarning - () yes - lint-only
|
||||
/// Warning - () yes yes yes
|
||||
/// Note - () rare yes -
|
||||
/// OnceNote - () - yes lint-only
|
||||
/// Help - () rare yes -
|
||||
/// OnceHelp - () - yes lint-only
|
||||
/// FailureNote - () rare - -
|
||||
/// Allow - () yes - lint-only
|
||||
/// Expect - () yes - lint-only
|
||||
///
|
||||
/// (*) `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is
|
||||
/// occasionally used.
|
||||
///
|
||||
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
|
||||
pub enum Level {
|
||||
/// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `BugAbort`.
|
||||
Bug,
|
||||
|
||||
/// An error that causes an immediate abort. Used for things like configuration errors,
|
||||
/// internal overflows, some file operation errors.
|
||||
Fatal,
|
||||
|
||||
/// An error in the code being compiled, which prevents compilation from finishing. This is the
|
||||
/// most common case.
|
||||
Error,
|
||||
|
||||
/// This is a strange one: lets you register an error without emitting it. If compilation ends
|
||||
/// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
|
||||
/// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
|
||||
/// that should only be reached when compiling erroneous code.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `ErrorGuaranteed` for `Normal` delayed bugs, and `()` for
|
||||
/// `GoodPath` delayed bugs.
|
||||
DelayedBug(DelayedBugKind),
|
||||
DelayedBug,
|
||||
|
||||
/// An error that causes an immediate abort. Used for things like configuration errors,
|
||||
/// internal overflows, some file operation errors.
|
||||
/// Like `DelayedBug`, but weaker: lets you register an error without emitting it. If
|
||||
/// compilation ends without any other diagnostics being emitted (and without an expected lint
|
||||
/// being suppressed), this will be emitted as a bug. Otherwise, it will be silently dropped.
|
||||
/// I.e. "expect other diagnostics are emitted (or suppressed)" semantics. Useful on code paths
|
||||
/// that should only be reached when emitting diagnostics, e.g. for expensive one-time
|
||||
/// diagnostic formatting operations.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case
|
||||
/// that is occasionally used, where it is `FatalError`.
|
||||
Fatal,
|
||||
|
||||
/// An error in the code being compiled, which prevents compilation from finishing. This is the
|
||||
/// most common case.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `ErrorGuaranteed`.
|
||||
Error,
|
||||
/// FIXME(nnethercote) good path delayed bugs are semantically strange: if printed they produce
|
||||
/// an ICE, but they don't satisfy `is_error` and they don't guarantee an error is emitted.
|
||||
/// Plus there's the extra complication with expected (suppressed) lints. They have limited
|
||||
/// use, and are used in very few places, and "good path" isn't a good name. It would be good
|
||||
/// to remove them.
|
||||
GoodPathDelayedBug,
|
||||
|
||||
/// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
|
||||
/// from finishing.
|
||||
|
|
@ -1545,45 +1570,28 @@ pub enum Level {
|
|||
ForceWarning(Option<LintExpectationId>),
|
||||
|
||||
/// A warning about the code being compiled. Does not prevent compilation from finishing.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
Warning,
|
||||
|
||||
/// A message giving additional context. Rare, because notes are more commonly attached to other
|
||||
/// diagnostics such as errors.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
/// A message giving additional context.
|
||||
Note,
|
||||
|
||||
/// A note that is only emitted once. Rare, mostly used in circumstances relating to lints.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
/// A note that is only emitted once.
|
||||
OnceNote,
|
||||
|
||||
/// A message suggesting how to fix something. Rare, because help messages are more commonly
|
||||
/// attached to other diagnostics such as errors.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
/// A message suggesting how to fix something.
|
||||
Help,
|
||||
|
||||
/// A help that is only emitted once. Rare.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
/// A help that is only emitted once.
|
||||
OnceHelp,
|
||||
|
||||
/// Similar to `Note`, but used in cases where compilation has failed. Rare.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
/// Similar to `Note`, but used in cases where compilation has failed. When printed for human
|
||||
/// consumption, it doesn't have any kind of `note:` label.
|
||||
FailureNote,
|
||||
|
||||
/// Only used for lints.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
Allow,
|
||||
|
||||
/// Only used for lints.
|
||||
///
|
||||
/// Its `EmissionGuarantee` is `()`.
|
||||
Expect(LintExpectationId),
|
||||
}
|
||||
|
||||
|
|
@ -1597,7 +1605,7 @@ impl Level {
|
|||
fn color(self) -> ColorSpec {
|
||||
let mut spec = ColorSpec::new();
|
||||
match self {
|
||||
Bug | DelayedBug(_) | Fatal | Error => {
|
||||
Bug | Fatal | Error | DelayedBug | GoodPathDelayedBug => {
|
||||
spec.set_fg(Some(Color::Red)).set_intense(true);
|
||||
}
|
||||
ForceWarning(_) | Warning => {
|
||||
|
|
@ -1617,7 +1625,7 @@ impl Level {
|
|||
|
||||
pub fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
Bug | DelayedBug(_) => "error: internal compiler error",
|
||||
Bug | DelayedBug | GoodPathDelayedBug => "error: internal compiler error",
|
||||
Fatal | Error => "error",
|
||||
ForceWarning(_) | Warning => "warning",
|
||||
Note | OnceNote => "note",
|
||||
|
|
@ -1637,6 +1645,19 @@ impl Level {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Can this level be used in a top-level diagnostic message and/or a
|
||||
// subdiagnostic message?
|
||||
fn can_be_top_or_sub(&self) -> (bool, bool) {
|
||||
match self {
|
||||
Bug | DelayedBug | Fatal | Error | GoodPathDelayedBug | ForceWarning(_)
|
||||
| FailureNote | Allow | Expect(_) => (true, false),
|
||||
|
||||
Warning | Note | Help => (true, true),
|
||||
|
||||
OnceNote | OnceHelp => (false, true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
|
||||
|
|
|
|||
|
|
@ -440,9 +440,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
experimental!(optimize),
|
||||
),
|
||||
|
||||
gated!(
|
||||
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
|
||||
),
|
||||
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
|
||||
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
|
||||
gated!(
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ declare_features! (
|
|||
/// Allows `#[doc(include = "some-file")]`.
|
||||
(removed, external_doc, "1.54.0", Some(44732),
|
||||
Some("use #[doc = include_str!(\"filename\")] instead, which handles macro invocations")),
|
||||
/// Allows using `#[ffi_returns_twice]` on foreign functions.
|
||||
(removed, ffi_returns_twice, "CURRENT_RUSTC_VERSION", Some(58314),
|
||||
Some("being investigated by the ffi-unwind project group")),
|
||||
/// Allows generators to be cloned.
|
||||
(removed, generator_clone, "1.65.0", Some(95360), Some("renamed to `coroutine_clone`")),
|
||||
/// Allows defining generators.
|
||||
|
|
|
|||
|
|
@ -465,8 +465,6 @@ declare_features! (
|
|||
(unstable, ffi_const, "1.45.0", Some(58328)),
|
||||
/// Allows the use of `#[ffi_pure]` on foreign functions.
|
||||
(unstable, ffi_pure, "1.45.0", Some(58329)),
|
||||
/// Allows using `#[ffi_returns_twice]` on foreign functions.
|
||||
(unstable, ffi_returns_twice, "1.34.0", Some(58314)),
|
||||
/// Allows using `#[repr(align(...))]` on function items
|
||||
(unstable, fn_align, "1.53.0", Some(82232)),
|
||||
/// Support delegating implementation of functions to other already implemented functions.
|
||||
|
|
|
|||
|
|
@ -952,6 +952,11 @@ pub enum ClosureKind {
|
|||
/// usage (e.g. `let x = || { yield (); }`) or from a desugared expression
|
||||
/// (e.g. `async` and `gen` blocks).
|
||||
Coroutine(CoroutineKind),
|
||||
/// This is a coroutine-closure, which is a special sugared closure that
|
||||
/// returns one of the sugared coroutine (`async`/`gen`/`async gen`). It
|
||||
/// additionally allows capturing the coroutine's upvars by ref, and therefore
|
||||
/// needs to be specially treated during analysis and borrowck.
|
||||
CoroutineClosure(CoroutineDesugaring),
|
||||
}
|
||||
|
||||
/// A block of statements `{ .. }`, which may have a label (in this case the
|
||||
|
|
@ -3698,6 +3703,7 @@ impl<'hir> Node<'hir> {
|
|||
expect_generic_param, &'hir GenericParam<'hir>, Node::GenericParam(n), n;
|
||||
expect_crate, &'hir Mod<'hir>, Node::Crate(n), n;
|
||||
expect_infer, &'hir InferArg, Node::Infer(n), n;
|
||||
expect_closure, &'hir Closure<'hir>, Node::Expr(Expr { kind: ExprKind::Closure(n), .. }), n;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ language_item_table! {
|
|||
AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AsyncFnKindHelper, sym::async_fn_kind_helper,async_fn_kind_helper, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use rustc_errors::{
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::traits::FulfillmentError;
|
||||
use rustc_middle::query::Key;
|
||||
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;
|
||||
|
|
@ -859,6 +860,56 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
self.set_tainted_by_errors(err.emit());
|
||||
}
|
||||
|
||||
/// On ambiguous associated type, look for an associated function whose name matches the
|
||||
/// extended path and, if found, emit an E0223 error with a structured suggestion.
|
||||
/// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195)
|
||||
pub(crate) fn maybe_report_similar_assoc_fn(
|
||||
&self,
|
||||
span: Span,
|
||||
qself_ty: Ty<'tcx>,
|
||||
qself: &hir::Ty<'_>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = self.tcx();
|
||||
if let Some((_, node)) = tcx.hir().parent_iter(qself.hir_id).skip(1).next()
|
||||
&& let hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::Path(hir::QPath::TypeRelative(
|
||||
hir::Ty {
|
||||
kind:
|
||||
hir::TyKind::Path(hir::QPath::TypeRelative(
|
||||
_,
|
||||
hir::PathSegment { ident: ident2, .. },
|
||||
)),
|
||||
..
|
||||
},
|
||||
hir::PathSegment { ident: ident3, .. },
|
||||
)),
|
||||
..
|
||||
}) = node
|
||||
&& let Some(ty_def_id) = qself_ty.ty_def_id()
|
||||
&& let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id)
|
||||
&& let name = format!("{ident2}_{ident3}")
|
||||
&& let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx
|
||||
.associated_items(inherent_impl)
|
||||
.filter_by_name_unhygienic(Symbol::intern(&name))
|
||||
.next()
|
||||
{
|
||||
let reported =
|
||||
struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type")
|
||||
.with_span_suggestion_verbose(
|
||||
ident2.span.to(ident3.span),
|
||||
format!("there is an associated function with a similar name: `{name}`"),
|
||||
name,
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.emit();
|
||||
self.set_tainted_by_errors(reported);
|
||||
Err(reported)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an error regarding forbidden type binding associations
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
)
|
||||
.emit() // Already reported in an earlier stage.
|
||||
} else {
|
||||
self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
|
||||
|
||||
let traits: Vec<_> =
|
||||
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ impl<'tcx> InherentCollect<'tcx> {
|
|||
}
|
||||
ty::FnDef(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Bound(..)
|
||||
|
|
|
|||
|
|
@ -244,6 +244,7 @@ fn do_orphan_check_impl<'tcx>(
|
|||
| ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
|
||||
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Bound(..)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
impl_trait_ref,
|
||||
impl_polarity,
|
||||
coroutine_kind,
|
||||
coroutine_for_closure,
|
||||
collect_mod_item_types,
|
||||
is_type_alias_impl_trait,
|
||||
..*providers
|
||||
|
|
@ -1531,6 +1532,29 @@ fn coroutine_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<hir::CoroutineK
|
|||
}
|
||||
}
|
||||
|
||||
fn coroutine_for_closure(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DefId {
|
||||
let &rustc_hir::Closure { kind: hir::ClosureKind::CoroutineClosure(_), body, .. } =
|
||||
tcx.hir_node_by_def_id(def_id).expect_closure()
|
||||
else {
|
||||
bug!()
|
||||
};
|
||||
|
||||
let &hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::Closure(&rustc_hir::Closure {
|
||||
def_id,
|
||||
kind: hir::ClosureKind::Coroutine(_),
|
||||
..
|
||||
}),
|
||||
..
|
||||
} = tcx.hir().body(body).value
|
||||
else {
|
||||
bug!()
|
||||
};
|
||||
|
||||
def_id.to_def_id()
|
||||
}
|
||||
|
||||
fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
|
||||
match tcx.hir_node_by_def_id(def_id) {
|
||||
Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. }) => {
|
||||
|
|
|
|||
|
|
@ -344,11 +344,25 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
kind: hir::ExprKind::Closure(hir::Closure { kind, .. }), ..
|
||||
}) = node
|
||||
{
|
||||
// See `ClosureArgsParts`, `CoroutineArgsParts`, and `CoroutineClosureArgsParts`
|
||||
// for info on the usage of each of these fields.
|
||||
let dummy_args = match kind {
|
||||
ClosureKind::Closure => &["<closure_kind>", "<closure_signature>", "<upvars>"][..],
|
||||
ClosureKind::Coroutine(_) => {
|
||||
&["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
|
||||
}
|
||||
ClosureKind::Coroutine(_) => &[
|
||||
"<coroutine_kind>",
|
||||
"<resume_ty>",
|
||||
"<yield_ty>",
|
||||
"<return_ty>",
|
||||
"<witness>",
|
||||
"<upvars>",
|
||||
][..],
|
||||
ClosureKind::CoroutineClosure(_) => &[
|
||||
"<closure_kind>",
|
||||
"<closure_signature_parts>",
|
||||
"<upvars>",
|
||||
"<bound_captures_by_ref>",
|
||||
"<witness>",
|
||||
][..],
|
||||
};
|
||||
|
||||
params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {
|
||||
|
|
|
|||
|
|
@ -235,8 +235,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
|||
// leaf type -- noop
|
||||
}
|
||||
|
||||
ty::FnDef(..) | ty::Coroutine(..) | ty::Closure(..) => {
|
||||
bug!("Unexpected closure type in variance computation");
|
||||
ty::FnDef(..) | ty::Coroutine(..) | ty::Closure(..) | ty::CoroutineClosure(..) => {
|
||||
bug!("Unexpected coroutine/closure type in variance computation");
|
||||
}
|
||||
|
||||
ty::Ref(region, ty, mutbl) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use crate::coercion::{AsCoercionSite, CoerceMany};
|
||||
use crate::{Diverges, Expectation, FnCtxt, Needs};
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir::{
|
||||
self as hir,
|
||||
def::{CtorOf, DefKind, Res},
|
||||
ExprKind, PatKind,
|
||||
};
|
||||
use rustc_hir_pretty::ty_to_string;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::traits::Obligation;
|
||||
|
|
@ -273,7 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// Returns `true` if there was an error forcing the coercion to the `()` type.
|
||||
pub(super) fn if_fallback_coercion<T>(
|
||||
&self,
|
||||
span: Span,
|
||||
if_span: Span,
|
||||
cond_expr: &'tcx hir::Expr<'tcx>,
|
||||
then_expr: &'tcx hir::Expr<'tcx>,
|
||||
coercion: &mut CoerceMany<'tcx, '_, T>,
|
||||
) -> bool
|
||||
|
|
@ -283,29 +288,106 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// If this `if` expr is the parent's function return expr,
|
||||
// the cause of the type coercion is the return type, point at it. (#25228)
|
||||
let hir_id = self.tcx.hir().parent_id(self.tcx.hir().parent_id(then_expr.hir_id));
|
||||
let ret_reason = self.maybe_get_coercion_reason(hir_id, span);
|
||||
let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
let ret_reason = self.maybe_get_coercion_reason(hir_id, if_span);
|
||||
let cause = self.cause(if_span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
let mut error = false;
|
||||
coercion.coerce_forced_unit(
|
||||
self,
|
||||
&cause,
|
||||
|err| {
|
||||
if let Some((span, msg)) = &ret_reason {
|
||||
err.span_label(*span, msg.clone());
|
||||
} else if let ExprKind::Block(block, _) = &then_expr.kind
|
||||
&& let Some(expr) = &block.expr
|
||||
{
|
||||
err.span_label(expr.span, "found here");
|
||||
}
|
||||
err.note("`if` expressions without `else` evaluate to `()`");
|
||||
err.help("consider adding an `else` block that evaluates to the expected type");
|
||||
error = true;
|
||||
},
|
||||
|err| self.explain_if_expr(err, ret_reason, if_span, cond_expr, then_expr, &mut error),
|
||||
false,
|
||||
);
|
||||
error
|
||||
}
|
||||
|
||||
/// Explain why `if` expressions without `else` evaluate to `()` and detect likely irrefutable
|
||||
/// `if let PAT = EXPR {}` expressions that could be turned into `let PAT = EXPR;`.
|
||||
fn explain_if_expr(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
ret_reason: Option<(Span, String)>,
|
||||
if_span: Span,
|
||||
cond_expr: &'tcx hir::Expr<'tcx>,
|
||||
then_expr: &'tcx hir::Expr<'tcx>,
|
||||
error: &mut bool,
|
||||
) {
|
||||
if let Some((if_span, msg)) = ret_reason {
|
||||
err.span_label(if_span, msg.clone());
|
||||
} else if let ExprKind::Block(block, _) = then_expr.kind
|
||||
&& let Some(expr) = block.expr
|
||||
{
|
||||
err.span_label(expr.span, "found here");
|
||||
}
|
||||
err.note("`if` expressions without `else` evaluate to `()`");
|
||||
err.help("consider adding an `else` block that evaluates to the expected type");
|
||||
*error = true;
|
||||
if let ExprKind::Let(hir::Let { span, pat, init, .. }) = cond_expr.kind
|
||||
&& let ExprKind::Block(block, _) = then_expr.kind
|
||||
// Refutability checks occur on the MIR, so we approximate it here by checking
|
||||
// if we have an enum with a single variant or a struct in the pattern.
|
||||
&& let PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..) = pat.kind
|
||||
&& let hir::QPath::Resolved(_, path) = qpath
|
||||
{
|
||||
match path.res {
|
||||
Res::Def(DefKind::Ctor(CtorOf::Struct, _), _) => {
|
||||
// Structs are always irrefutable. Their fields might not be, but we
|
||||
// don't check for that here, it's only an approximation.
|
||||
}
|
||||
Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id)
|
||||
if self
|
||||
.tcx
|
||||
.adt_def(self.tcx.parent(self.tcx.parent(def_id)))
|
||||
.variants()
|
||||
.len()
|
||||
== 1 =>
|
||||
{
|
||||
// There's only a single variant in the `enum`, so we can suggest the
|
||||
// irrefutable `let` instead of `if let`.
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
let mut sugg = vec![
|
||||
// Remove the `if`
|
||||
(if_span.until(*span), String::new()),
|
||||
];
|
||||
match (block.stmts, block.expr) {
|
||||
([first, ..], Some(expr)) => {
|
||||
let padding = self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.indentation_before(first.span)
|
||||
.unwrap_or_else(|| String::new());
|
||||
sugg.extend([
|
||||
(init.span.between(first.span), format!(";\n{padding}")),
|
||||
(expr.span.shrink_to_hi().with_hi(block.span.hi()), String::new()),
|
||||
]);
|
||||
}
|
||||
([], Some(expr)) => {
|
||||
let padding = self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.indentation_before(expr.span)
|
||||
.unwrap_or_else(|| String::new());
|
||||
sugg.extend([
|
||||
(init.span.between(expr.span), format!(";\n{padding}")),
|
||||
(expr.span.shrink_to_hi().with_hi(block.span.hi()), String::new()),
|
||||
]);
|
||||
}
|
||||
// If there's no value in the body, then the `if` expression would already
|
||||
// be of type `()`, so checking for those cases is unnecessary.
|
||||
(_, None) => return,
|
||||
}
|
||||
err.multipart_suggestion(
|
||||
"consider using an irrefutable `let` binding instead",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_get_coercion_reason(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
|
|
|
|||
|
|
@ -141,33 +141,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return Some(CallStep::Builtin(adjusted_ty));
|
||||
}
|
||||
|
||||
ty::Closure(def_id, args) => {
|
||||
// Check whether this is a call to a closure where we
|
||||
// haven't yet decided on whether the closure is fn vs
|
||||
// fnmut vs fnonce. If so, we have to defer further processing.
|
||||
ty::Closure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => {
|
||||
let def_id = def_id.expect_local();
|
||||
let closure_sig = args.as_closure().sig();
|
||||
let closure_sig = self.instantiate_binder_with_fresh_vars(
|
||||
call_expr.span,
|
||||
infer::FnCall,
|
||||
closure_sig,
|
||||
);
|
||||
let adjustments = self.adjust_steps(autoderef);
|
||||
self.record_deferred_call_resolution(
|
||||
def_id,
|
||||
DeferredCallResolution {
|
||||
call_expr,
|
||||
callee_expr,
|
||||
closure_ty: adjusted_ty,
|
||||
adjustments,
|
||||
fn_sig: closure_sig,
|
||||
},
|
||||
);
|
||||
return Some(CallStep::DeferredClosure(def_id, closure_sig));
|
||||
}
|
||||
|
||||
// Check whether this is a call to a closure where we
|
||||
// haven't yet decided on whether the closure is fn vs
|
||||
// fnmut vs fnonce. If so, we have to defer further processing.
|
||||
if self.closure_kind(args).is_none() {
|
||||
let closure_sig = args.as_closure().sig();
|
||||
let closure_sig = self.instantiate_binder_with_fresh_vars(
|
||||
call_expr.span,
|
||||
infer::FnCall,
|
||||
closure_sig,
|
||||
);
|
||||
let adjustments = self.adjust_steps(autoderef);
|
||||
self.record_deferred_call_resolution(
|
||||
def_id,
|
||||
DeferredCallResolution {
|
||||
call_expr,
|
||||
callee_expr,
|
||||
adjusted_ty,
|
||||
adjustments,
|
||||
fn_sig: closure_sig,
|
||||
closure_args: args,
|
||||
},
|
||||
);
|
||||
return Some(CallStep::DeferredClosure(def_id, closure_sig));
|
||||
}
|
||||
// When calling a `CoroutineClosure` that is local to the body, we will
|
||||
// not know what its `closure_kind` is yet. Instead, just fill in the
|
||||
// signature with an infer var for the `tupled_upvars_ty` of the coroutine,
|
||||
// and record a deferred call resolution which will constrain that var
|
||||
// as part of `AsyncFn*` trait confirmation.
|
||||
ty::CoroutineClosure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => {
|
||||
let def_id = def_id.expect_local();
|
||||
let closure_args = args.as_coroutine_closure();
|
||||
let coroutine_closure_sig = self.instantiate_binder_with_fresh_vars(
|
||||
call_expr.span,
|
||||
infer::FnCall,
|
||||
closure_args.coroutine_closure_sig(),
|
||||
);
|
||||
let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
span: callee_expr.span,
|
||||
});
|
||||
let call_sig = self.tcx.mk_fn_sig(
|
||||
[coroutine_closure_sig.tupled_inputs_ty],
|
||||
coroutine_closure_sig.to_coroutine(
|
||||
self.tcx,
|
||||
closure_args.parent_args(),
|
||||
// Inherit the kind ty of the closure, since we're calling this
|
||||
// coroutine with the most relaxed `AsyncFn*` trait that we can.
|
||||
// We don't necessarily need to do this here, but it saves us
|
||||
// computing one more infer var that will get constrained later.
|
||||
closure_args.kind_ty(),
|
||||
self.tcx.coroutine_for_closure(def_id),
|
||||
tupled_upvars_ty,
|
||||
),
|
||||
coroutine_closure_sig.c_variadic,
|
||||
coroutine_closure_sig.unsafety,
|
||||
coroutine_closure_sig.abi,
|
||||
);
|
||||
let adjustments = self.adjust_steps(autoderef);
|
||||
self.record_deferred_call_resolution(
|
||||
def_id,
|
||||
DeferredCallResolution {
|
||||
call_expr,
|
||||
callee_expr,
|
||||
closure_ty: adjusted_ty,
|
||||
adjustments,
|
||||
fn_sig: call_sig,
|
||||
},
|
||||
);
|
||||
return Some(CallStep::DeferredClosure(def_id, call_sig));
|
||||
}
|
||||
|
||||
// Hack: we know that there are traits implementing Fn for &F
|
||||
|
|
@ -886,10 +930,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pub struct DeferredCallResolution<'tcx> {
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
callee_expr: &'tcx hir::Expr<'tcx>,
|
||||
adjusted_ty: Ty<'tcx>,
|
||||
closure_ty: Ty<'tcx>,
|
||||
adjustments: Vec<Adjustment<'tcx>>,
|
||||
fn_sig: ty::FnSig<'tcx>,
|
||||
closure_args: GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> DeferredCallResolution<'tcx> {
|
||||
|
|
@ -898,10 +941,10 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> {
|
|||
|
||||
// we should not be invoked until the closure kind has been
|
||||
// determined by upvar inference
|
||||
assert!(fcx.closure_kind(self.closure_args).is_some());
|
||||
assert!(fcx.closure_kind(self.closure_ty).is_some());
|
||||
|
||||
// We may now know enough to figure out fn vs fnmut etc.
|
||||
match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) {
|
||||
match fcx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) {
|
||||
Some((autoref, method_callee)) => {
|
||||
// One problem is that when we get here, we are going
|
||||
// to have a newly instantiated function signature
|
||||
|
|
@ -937,7 +980,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> {
|
|||
span_bug!(
|
||||
self.call_expr.span,
|
||||
"Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`",
|
||||
self.adjusted_ty
|
||||
self.closure_ty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::Adt(..)
|
||||
| ty::Never
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None => (None, None),
|
||||
};
|
||||
|
||||
let ClosureSignatures { bound_sig, liberated_sig } =
|
||||
let ClosureSignatures { bound_sig, mut liberated_sig } =
|
||||
self.sig_of_closure(expr_def_id, closure.fn_decl, closure.kind, expected_sig);
|
||||
|
||||
debug!(?bound_sig, ?liberated_sig);
|
||||
|
|
@ -125,7 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
|
||||
| hir::CoroutineKind::Coroutine(_) => {
|
||||
let yield_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType);
|
||||
|
|
@ -137,7 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// not a problem.
|
||||
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
|
||||
let yield_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType);
|
||||
|
|
@ -166,8 +166,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit);
|
||||
|
||||
let interior = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: body.value.span,
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
self.deferred_coroutine_interiors.borrow_mut().push((
|
||||
expr_def_id,
|
||||
|
|
@ -175,10 +175,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
interior,
|
||||
));
|
||||
|
||||
// Coroutines that come from coroutine closures have not yet determined
|
||||
// their kind ty, so make a fresh infer var which will be constrained
|
||||
// later during upvar analysis. Regular coroutines always have the kind
|
||||
// ty of `().`
|
||||
let kind_ty = match kind {
|
||||
hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) => self
|
||||
.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
}),
|
||||
_ => tcx.types.unit,
|
||||
};
|
||||
|
||||
let coroutine_args = ty::CoroutineArgs::new(
|
||||
tcx,
|
||||
ty::CoroutineArgsParts {
|
||||
parent_args,
|
||||
kind_ty,
|
||||
resume_ty,
|
||||
yield_ty,
|
||||
return_ty: liberated_sig.output(),
|
||||
|
|
@ -192,6 +206,94 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(CoroutineTypes { resume_ty, yield_ty }),
|
||||
)
|
||||
}
|
||||
hir::ClosureKind::CoroutineClosure(kind) => {
|
||||
// async closures always return the type ascribed after the `->` (if present),
|
||||
// and yield `()`.
|
||||
let (bound_return_ty, bound_yield_ty) = match kind {
|
||||
hir::CoroutineDesugaring::Async => {
|
||||
(bound_sig.skip_binder().output(), tcx.types.unit)
|
||||
}
|
||||
hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => {
|
||||
todo!("`gen` and `async gen` closures not supported yet")
|
||||
}
|
||||
};
|
||||
// Compute all of the variables that will be used to populate the coroutine.
|
||||
let resume_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
let interior = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
let closure_kind_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
// FIXME(eddyb) distinguish closure kind inference variables from the rest.
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
let coroutine_captures_by_ref_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
let closure_args = ty::CoroutineClosureArgs::new(
|
||||
tcx,
|
||||
ty::CoroutineClosureArgsParts {
|
||||
parent_args,
|
||||
closure_kind_ty,
|
||||
signature_parts_ty: Ty::new_fn_ptr(
|
||||
tcx,
|
||||
bound_sig.map_bound(|sig| {
|
||||
tcx.mk_fn_sig(
|
||||
[
|
||||
resume_ty,
|
||||
Ty::new_tup_from_iter(tcx, sig.inputs().iter().copied()),
|
||||
],
|
||||
Ty::new_tup(tcx, &[bound_yield_ty, bound_return_ty]),
|
||||
sig.c_variadic,
|
||||
sig.unsafety,
|
||||
sig.abi,
|
||||
)
|
||||
}),
|
||||
),
|
||||
tupled_upvars_ty,
|
||||
coroutine_captures_by_ref_ty,
|
||||
coroutine_witness_ty: interior,
|
||||
},
|
||||
);
|
||||
|
||||
let coroutine_upvars_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::ClosureSynthetic,
|
||||
span: expr_span,
|
||||
});
|
||||
|
||||
// We need to turn the liberated signature that we got from HIR, which
|
||||
// looks something like `|Args...| -> T`, into a signature that is suitable
|
||||
// for type checking the inner body of the closure, which always returns a
|
||||
// coroutine. To do so, we use the `CoroutineClosureSignature` to compute
|
||||
// the coroutine type, filling in the tupled_upvars_ty and kind_ty with infer
|
||||
// vars which will get constrained during upvar analysis.
|
||||
let coroutine_output_ty = tcx.liberate_late_bound_regions(
|
||||
expr_def_id.to_def_id(),
|
||||
closure_args.coroutine_closure_sig().map_bound(|sig| {
|
||||
sig.to_coroutine(
|
||||
tcx,
|
||||
parent_args,
|
||||
closure_kind_ty,
|
||||
tcx.coroutine_for_closure(expr_def_id),
|
||||
coroutine_upvars_ty,
|
||||
)
|
||||
}),
|
||||
);
|
||||
liberated_sig = tcx.mk_fn_sig(
|
||||
liberated_sig.inputs().iter().copied(),
|
||||
coroutine_output_ty,
|
||||
liberated_sig.c_variadic,
|
||||
liberated_sig.unsafety,
|
||||
liberated_sig.abi,
|
||||
);
|
||||
|
||||
(Ty::new_coroutine_closure(tcx, expr_def_id.to_def_id(), closure_args.args), None)
|
||||
}
|
||||
};
|
||||
|
||||
check_fn(
|
||||
|
|
@ -690,7 +792,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
_,
|
||||
))
|
||||
| hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_))
|
||||
| hir::ClosureKind::Closure => astconv.ty_infer(None, decl.output.span()),
|
||||
| hir::ClosureKind::Closure
|
||||
| hir::ClosureKind::CoroutineClosure(_) => {
|
||||
astconv.ty_infer(None, decl.output.span())
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1118,7 +1118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// We won't diverge unless both branches do (or the condition does).
|
||||
self.diverges.set(cond_diverges | then_diverges & else_diverges);
|
||||
} else {
|
||||
self.if_fallback_coercion(sp, then_expr, &mut coerce);
|
||||
self.if_fallback_coercion(sp, cond_expr, then_expr, &mut coerce);
|
||||
|
||||
// If the condition is false we can't diverge.
|
||||
self.diverges.set(cond_diverges);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
match ty.kind() {
|
||||
// Not all of these (e.g., unsafe fns) implement `FnOnce`,
|
||||
// so we look for these beforehand.
|
||||
// FIXME(async_closures): These don't impl `FnOnce` by default.
|
||||
ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true,
|
||||
// If it's not a simple function, look for things which implement `FnOnce`.
|
||||
_ => {
|
||||
|
|
@ -540,6 +541,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let mut bound_spans: SortedMap<Span, Vec<String>> = Default::default();
|
||||
let mut restrict_type_params = false;
|
||||
let mut suggested_derive = false;
|
||||
let mut unsatisfied_bounds = false;
|
||||
if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
|
||||
let msg = "consider using `len` instead";
|
||||
|
|
@ -554,6 +556,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
|
||||
));
|
||||
}
|
||||
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
|
||||
// We special case the situation where we are looking for `_` in
|
||||
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
|
||||
// implementations that have unsatisfied trait bounds to suggest, leading us to claim
|
||||
// things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord`
|
||||
// have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so
|
||||
// that `impl<T: FnPtr> Ord for T` can apply", which is not what we want. We have a type
|
||||
// parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict
|
||||
// `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling
|
||||
// `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates`
|
||||
// suggestions.
|
||||
} else if !unsatisfied_predicates.is_empty() {
|
||||
let mut type_params = FxIndexMap::default();
|
||||
|
||||
|
|
@ -916,20 +929,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.enumerate()
|
||||
.collect::<Vec<(usize, String)>>();
|
||||
|
||||
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
|
||||
restrict_type_params = true;
|
||||
// #74886: Sort here so that the output is always the same.
|
||||
let obligations = obligations.into_sorted_stable_ord();
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!(
|
||||
"consider restricting the type parameter{s} to satisfy the \
|
||||
trait bound{s}",
|
||||
s = pluralize!(obligations.len())
|
||||
),
|
||||
format!("{} {}", add_where_or_comma, obligations.join(", ")),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) {
|
||||
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
|
||||
restrict_type_params = true;
|
||||
// #74886: Sort here so that the output is always the same.
|
||||
let obligations = obligations.into_sorted_stable_ord();
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!(
|
||||
"consider restricting the type parameter{s} to satisfy the trait \
|
||||
bound{s}",
|
||||
s = pluralize!(obligations.len())
|
||||
),
|
||||
format!("{} {}", add_where_or_comma, obligations.join(", ")),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
|
||||
|
|
@ -977,7 +992,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
"the following trait bounds were not satisfied:\n{bound_list}"
|
||||
));
|
||||
}
|
||||
self.suggest_derive(&mut err, unsatisfied_predicates);
|
||||
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);
|
||||
|
||||
unsatisfied_bounds = true;
|
||||
}
|
||||
|
|
@ -1200,7 +1215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
|
||||
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
|
||||
} else {
|
||||
self.suggest_traits_to_import(
|
||||
&mut err,
|
||||
|
|
@ -1210,7 +1225,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
args.map(|args| args.len() + 1),
|
||||
source,
|
||||
no_match_data.out_of_scope_traits.clone(),
|
||||
unsatisfied_predicates,
|
||||
static_candidates,
|
||||
unsatisfied_bounds,
|
||||
expected.only_has_type(self),
|
||||
|
|
@ -1325,7 +1339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
|
||||
return Some(err);
|
||||
Some(err)
|
||||
}
|
||||
|
||||
fn note_candidates_on_method_error(
|
||||
|
|
@ -2470,7 +2484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Option<ty::Predicate<'tcx>>,
|
||||
Option<ObligationCause<'tcx>>,
|
||||
)],
|
||||
) {
|
||||
) -> bool {
|
||||
let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates);
|
||||
derives.sort();
|
||||
derives.dedup();
|
||||
|
|
@ -2495,6 +2509,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
!derives_grouped.is_empty()
|
||||
}
|
||||
|
||||
fn note_derefed_ty_has_method(
|
||||
|
|
@ -2697,11 +2712,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
inputs_len: Option<usize>,
|
||||
source: SelfSource<'tcx>,
|
||||
valid_out_of_scope_traits: Vec<DefId>,
|
||||
unsatisfied_predicates: &[(
|
||||
ty::Predicate<'tcx>,
|
||||
Option<ty::Predicate<'tcx>>,
|
||||
Option<ObligationCause<'tcx>>,
|
||||
)],
|
||||
static_candidates: &[CandidateSource],
|
||||
unsatisfied_bounds: bool,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
|
|
@ -2918,19 +2928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// this isn't perfect (that is, there are cases when
|
||||
// implementing a trait would be legal but is rejected
|
||||
// here).
|
||||
unsatisfied_predicates.iter().all(|(p, _, _)| {
|
||||
match p.kind().skip_binder() {
|
||||
// Hide traits if they are present in predicates as they can be fixed without
|
||||
// having to implement them.
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
|
||||
t.def_id() == info.def_id
|
||||
}
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
|
||||
p.projection_ty.def_id == info.def_id
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}) && (type_is_local || info.def_id.is_local())
|
||||
(type_is_local || info.def_id.is_local())
|
||||
&& !self.tcx.trait_is_auto(info.def_id)
|
||||
&& self
|
||||
.associated_value(info.def_id, item_name)
|
||||
|
|
|
|||
|
|
@ -170,9 +170,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
) {
|
||||
// Extract the type of the closure.
|
||||
let ty = self.node_ty(closure_hir_id);
|
||||
let (closure_def_id, args) = match *ty.kind() {
|
||||
ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args)),
|
||||
ty::Coroutine(def_id, args) => (def_id, UpvarArgs::Coroutine(args)),
|
||||
let (closure_def_id, args, infer_kind) = match *ty.kind() {
|
||||
ty::Closure(def_id, args) => {
|
||||
(def_id, UpvarArgs::Closure(args), self.closure_kind(ty).is_none())
|
||||
}
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
(def_id, UpvarArgs::CoroutineClosure(args), self.closure_kind(ty).is_none())
|
||||
}
|
||||
ty::Coroutine(def_id, args) => (def_id, UpvarArgs::Coroutine(args), false),
|
||||
ty::Error(_) => {
|
||||
// #51714: skip analysis when we have already encountered type errors
|
||||
return;
|
||||
|
|
@ -188,18 +193,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
let closure_def_id = closure_def_id.expect_local();
|
||||
|
||||
let infer_kind = if let UpvarArgs::Closure(closure_args) = args {
|
||||
self.closure_kind(closure_args).is_none().then_some(closure_args)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id);
|
||||
let mut delegate = InferBorrowKind {
|
||||
closure_def_id,
|
||||
capture_information: Default::default(),
|
||||
fake_reads: Default::default(),
|
||||
};
|
||||
|
||||
// As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
|
||||
// to `ByRef` for the `async {}` block internal to async fns/closure. This means
|
||||
// that we would *not* be moving all of the parameters into the async block by default.
|
||||
//
|
||||
// We force all of these arguments to be captured by move before we do expr use analysis.
|
||||
//
|
||||
// FIXME(async_closures): This could be cleaned up. It's a bit janky that we're just
|
||||
// moving all of the `LocalSource::AsyncFn` locals here.
|
||||
if let Some(hir::CoroutineKind::Desugared(
|
||||
_,
|
||||
hir::CoroutineSource::Fn | hir::CoroutineSource::Closure,
|
||||
)) = self.tcx.coroutine_kind(closure_def_id)
|
||||
{
|
||||
let hir::ExprKind::Block(block, _) = body.value.kind else {
|
||||
bug!();
|
||||
};
|
||||
for stmt in block.stmts {
|
||||
let hir::StmtKind::Local(hir::Local {
|
||||
init: Some(init),
|
||||
source: hir::LocalSource::AsyncFn,
|
||||
pat,
|
||||
..
|
||||
}) = stmt.kind
|
||||
else {
|
||||
bug!();
|
||||
};
|
||||
let hir::PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), _, _, _) =
|
||||
pat.kind
|
||||
else {
|
||||
// Complex pattern, skip the non-upvar local.
|
||||
continue;
|
||||
};
|
||||
let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = init.kind else {
|
||||
bug!();
|
||||
};
|
||||
let hir::def::Res::Local(local_id) = path.res else {
|
||||
bug!();
|
||||
};
|
||||
let place = self.place_for_root_variable(closure_def_id, local_id);
|
||||
delegate.capture_information.push((
|
||||
place,
|
||||
ty::CaptureInfo {
|
||||
capture_kind_expr_id: Some(init.hir_id),
|
||||
path_expr_id: Some(init.hir_id),
|
||||
capture_kind: UpvarCapture::ByValue,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
euv::ExprUseVisitor::new(
|
||||
&mut delegate,
|
||||
&self.infcx,
|
||||
|
|
@ -257,10 +307,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let before_feature_tys = self.final_upvar_tys(closure_def_id);
|
||||
|
||||
if let Some(closure_args) = infer_kind {
|
||||
if infer_kind {
|
||||
// Unify the (as yet unbound) type variable in the closure
|
||||
// args with the kind we inferred.
|
||||
let closure_kind_ty = closure_args.as_closure().kind_ty();
|
||||
let closure_kind_ty = match args {
|
||||
UpvarArgs::Closure(args) => args.as_closure().kind_ty(),
|
||||
UpvarArgs::CoroutineClosure(args) => args.as_coroutine_closure().kind_ty(),
|
||||
UpvarArgs::Coroutine(_) => unreachable!("coroutines don't have an inferred kind"),
|
||||
};
|
||||
self.demand_eqtype(
|
||||
span,
|
||||
Ty::from_closure_kind(self.tcx, closure_kind),
|
||||
|
|
@ -282,6 +336,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// For coroutine-closures, we additionally must compute the
|
||||
// `coroutine_captures_by_ref_ty` type, which is used to generate the by-ref
|
||||
// version of the coroutine-closure's output coroutine.
|
||||
if let UpvarArgs::CoroutineClosure(args) = args {
|
||||
let closure_env_region: ty::Region<'_> = ty::Region::new_bound(
|
||||
self.tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(0),
|
||||
kind: ty::BoundRegionKind::BrEnv,
|
||||
},
|
||||
);
|
||||
let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter(
|
||||
self.tcx,
|
||||
self.typeck_results
|
||||
.borrow()
|
||||
.closure_min_captures_flattened(
|
||||
self.tcx.coroutine_for_closure(closure_def_id).expect_local(),
|
||||
)
|
||||
// Skip the captures that are just moving the closure's args
|
||||
// into the coroutine. These are always by move, and we append
|
||||
// those later in the `CoroutineClosureSignature` helper functions.
|
||||
.skip(
|
||||
args.as_coroutine_closure()
|
||||
.coroutine_closure_sig()
|
||||
.skip_binder()
|
||||
.tupled_inputs_ty
|
||||
.tuple_fields()
|
||||
.len(),
|
||||
)
|
||||
.map(|captured_place| {
|
||||
let upvar_ty = captured_place.place.ty();
|
||||
let capture = captured_place.info.capture_kind;
|
||||
// Not all upvars are captured by ref, so use
|
||||
// `apply_capture_kind_on_capture_ty` to ensure that we
|
||||
// compute the right captured type.
|
||||
apply_capture_kind_on_capture_ty(
|
||||
self.tcx,
|
||||
upvar_ty,
|
||||
capture,
|
||||
Some(closure_env_region),
|
||||
)
|
||||
}),
|
||||
);
|
||||
let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
|
||||
self.tcx,
|
||||
ty::Binder::bind_with_vars(
|
||||
self.tcx.mk_fn_sig(
|
||||
[],
|
||||
tupled_upvars_ty_for_borrow,
|
||||
false,
|
||||
hir::Unsafety::Normal,
|
||||
rustc_target::spec::abi::Abi::Rust,
|
||||
),
|
||||
self.tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Region(
|
||||
ty::BoundRegionKind::BrEnv,
|
||||
)]),
|
||||
),
|
||||
);
|
||||
self.demand_eqtype(
|
||||
span,
|
||||
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
|
||||
coroutine_captures_by_ref_ty,
|
||||
);
|
||||
|
||||
// Additionally, we can now constrain the coroutine's kind type.
|
||||
let ty::Coroutine(_, coroutine_args) =
|
||||
*self.typeck_results.borrow().expr_ty(body.value).kind()
|
||||
else {
|
||||
bug!();
|
||||
};
|
||||
self.demand_eqtype(
|
||||
span,
|
||||
coroutine_args.as_coroutine().kind_ty(),
|
||||
Ty::from_closure_kind(self.tcx, closure_kind),
|
||||
);
|
||||
}
|
||||
|
||||
self.log_closure_min_capture_info(closure_def_id, span);
|
||||
|
||||
// Now that we've analyzed the closure, we know how each
|
||||
|
|
@ -551,7 +683,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
|
||||
// Go through each entry in the current list of min_captures
|
||||
// - if ancestor is found, update it's capture kind to account for current place's
|
||||
// - if ancestor is found, update its capture kind to account for current place's
|
||||
// capture information.
|
||||
//
|
||||
// - if descendant is found, remove it from the list, and update the current place's
|
||||
|
|
|
|||
|
|
@ -414,6 +414,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
|
|||
}
|
||||
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Bool
|
||||
|
|
@ -480,7 +481,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
|
|||
}
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
|
||||
match self.infcx.unwrap().probe_effect_var(vid) {
|
||||
Some(value) => return self.fold_const(value.as_const(self.tcx)),
|
||||
Some(value) => return self.fold_const(value),
|
||||
None => {
|
||||
return self.canonicalize_const_var(
|
||||
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::infer::unify_key::EffectVarValue;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::GenericArg;
|
||||
use rustc_middle::ty::{self, List, Ty, TyCtxt};
|
||||
|
|
@ -152,7 +153,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
)
|
||||
.into(),
|
||||
CanonicalVarKind::Effect => {
|
||||
let vid = self.inner.borrow_mut().effect_unification_table().new_key(None).vid;
|
||||
let vid = self
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.new_key(EffectVarValue::Unknown)
|
||||
.vid;
|
||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ use rustc_hir::lang_items::LangItem;
|
|||
use rustc_middle::dep_graph::DepContext;
|
||||
use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError};
|
||||
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::ToPredicate;
|
||||
use rustc_middle::ty::{
|
||||
self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
|
||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
|
||||
|
|
@ -519,10 +520,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
|
||||
}
|
||||
|
||||
RegionResolutionError::CannotNormalize(ty, origin) => {
|
||||
RegionResolutionError::CannotNormalize(clause, origin) => {
|
||||
let clause: ty::Clause<'tcx> =
|
||||
clause.map_bound(ty::ClauseKind::TypeOutlives).to_predicate(self.tcx);
|
||||
self.tcx
|
||||
.dcx()
|
||||
.struct_span_err(origin.span(), format!("cannot normalize `{ty}`"))
|
||||
.struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
|
@ -2839,7 +2842,11 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
|||
// say, also take a look at the error code, maybe we can
|
||||
// tailor to that.
|
||||
_ => match terr {
|
||||
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_coroutine() => Error0644,
|
||||
TypeError::CyclicTy(ty)
|
||||
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() =>
|
||||
{
|
||||
Error0644
|
||||
}
|
||||
TypeError::IntrinsicCast => Error0308,
|
||||
_ => Error0308,
|
||||
},
|
||||
|
|
@ -2886,7 +2893,9 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
|||
// say, also take a look at the error code, maybe we can
|
||||
// tailor to that.
|
||||
_ => match terr {
|
||||
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_coroutine() => {
|
||||
TypeError::CyclicTy(ty)
|
||||
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() =>
|
||||
{
|
||||
ObligationCauseFailureCode::ClosureSelfref { span }
|
||||
}
|
||||
TypeError::IntrinsicCast => {
|
||||
|
|
|
|||
|
|
@ -883,7 +883,10 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
GenericArgKind::Type(ty) => {
|
||||
if matches!(
|
||||
ty.kind(),
|
||||
ty::Alias(ty::Opaque, ..) | ty::Closure(..) | ty::Coroutine(..)
|
||||
ty::Alias(ty::Opaque, ..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
) {
|
||||
// Opaque types can't be named by the user right now.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -228,7 +228,10 @@ impl<T> Trait<T> for X {
|
|||
#traits-as-parameters",
|
||||
);
|
||||
}
|
||||
(ty::Param(p), ty::Closure(..) | ty::Coroutine(..)) => {
|
||||
(
|
||||
ty::Param(p),
|
||||
ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
|
||||
) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
if let Some(param) = generics.opt_type_param(p, tcx) {
|
||||
let p_span = tcx.def_span(param.def_id);
|
||||
|
|
@ -497,7 +500,7 @@ impl<T> Trait<T> for X {
|
|||
}
|
||||
CyclicTy(ty) => {
|
||||
// Watch out for various cases of cyclic types and try to explain.
|
||||
if ty.is_closure() || ty.is_coroutine() {
|
||||
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
|
||||
diag.note(
|
||||
"closures cannot capture themselves or take themselves as argument;\n\
|
||||
this error may be the result of a recent compiler bug-fix,\n\
|
||||
|
|
|
|||
|
|
@ -151,13 +151,8 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
|
|||
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
|
||||
let opt_ct = self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|effect| effect.as_const(self.infcx.tcx));
|
||||
let opt_ct =
|
||||
self.infcx.inner.borrow_mut().effect_unification_table().probe_value(v).known();
|
||||
self.freshen_const(
|
||||
opt_ct,
|
||||
ty::InferConst::EffectVar(v),
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ pub enum RegionResolutionError<'tcx> {
|
|||
Region<'tcx>, // the placeholder `'b`
|
||||
),
|
||||
|
||||
CannotNormalize(Ty<'tcx>, SubregionOrigin<'tcx>),
|
||||
CannotNormalize(ty::PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> RegionResolutionError<'tcx> {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub use self::ValuePairs::*;
|
|||
pub use relate::combine::ObligationEmittingRelation;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use rustc_middle::infer::unify_key::EffectVarValue;
|
||||
use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};
|
||||
|
||||
use self::opaque_types::OpaqueTypeStorage;
|
||||
|
|
@ -25,8 +26,8 @@ use rustc_data_structures::unify as ut;
|
|||
use rustc_errors::{DiagCtxt, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
|
||||
use rustc_middle::infer::unify_key::ConstVariableValue;
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableValue, EffectVarValue};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::traits::{select, DefiningAnchor};
|
||||
|
|
@ -818,7 +819,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
|
||||
(0..table.len())
|
||||
.map(|i| ty::EffectVid::from_usize(i))
|
||||
.filter(|&vid| table.probe_value(vid).is_none())
|
||||
.filter(|&vid| table.probe_value(vid).is_unknown())
|
||||
.map(|v| {
|
||||
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool)
|
||||
})
|
||||
|
|
@ -1236,7 +1237,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
|
||||
pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
|
||||
let effect_vid = self.inner.borrow_mut().effect_unification_table().new_key(None).vid;
|
||||
let effect_vid =
|
||||
self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid;
|
||||
let ty = self
|
||||
.tcx
|
||||
.type_of(param.def_id)
|
||||
|
|
@ -1416,8 +1418,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn probe_effect_var(&self, vid: EffectVid) -> Option<EffectVarValue<'tcx>> {
|
||||
self.inner.borrow_mut().effect_unification_table().probe_value(vid)
|
||||
pub fn probe_effect_var(&self, vid: EffectVid) -> Option<ty::Const<'tcx>> {
|
||||
self.inner.borrow_mut().effect_unification_table().probe_value(vid).known()
|
||||
}
|
||||
|
||||
/// Attempts to resolve all type/region/const variables in
|
||||
|
|
@ -1538,9 +1540,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// Obtains the latest type of the given closure; this may be a
|
||||
/// closure in the current function, in which case its
|
||||
/// `ClosureKind` may not yet be known.
|
||||
pub fn closure_kind(&self, closure_args: GenericArgsRef<'tcx>) -> Option<ty::ClosureKind> {
|
||||
let closure_kind_ty = closure_args.as_closure().kind_ty();
|
||||
let closure_kind_ty = self.shallow_resolve(closure_kind_ty);
|
||||
pub fn closure_kind(&self, closure_ty: Ty<'tcx>) -> Option<ty::ClosureKind> {
|
||||
let unresolved_kind_ty = match *closure_ty.kind() {
|
||||
ty::Closure(_, args) => args.as_closure().kind_ty(),
|
||||
ty::CoroutineClosure(_, args) => args.as_coroutine_closure().kind_ty(),
|
||||
_ => bug!("unexpected type {closure_ty}"),
|
||||
};
|
||||
let closure_kind_ty = self.shallow_resolve(unresolved_kind_ty);
|
||||
closure_kind_ty.to_opt_closure_kind()
|
||||
}
|
||||
|
||||
|
|
@ -1893,7 +1899,8 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ShallowResolver<'a, 'tcx> {
|
|||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.probe_value(vid)
|
||||
.map_or(ct, |val| val.as_const(self.infcx.tcx)),
|
||||
.known()
|
||||
.unwrap_or(ct),
|
||||
_ => ct,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -456,6 +456,17 @@ where
|
|||
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
for upvar in args.as_coroutine_closure().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
|
||||
// FIXME(async_closures): Is this the right signature to visit here?
|
||||
args.as_coroutine_closure().signature_parts_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
// Also skip the witness type, because that has no free regions.
|
||||
|
|
|
|||
|
|
@ -103,6 +103,11 @@ fn compute_components<'tcx>(
|
|||
compute_components(tcx, tupled_ty, out, visited);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
|
||||
compute_components(tcx, tupled_ty, out, visited);
|
||||
}
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
// Same as the closure case
|
||||
let tupled_ty = args.as_coroutine().tupled_upvars_ty();
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use super::region_constraints::RegionConstraintData;
|
|||
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
|
||||
use crate::infer::free_regions::RegionRelations;
|
||||
use crate::infer::lexical_region_resolve;
|
||||
use rustc_middle::traits::query::OutlivesBound;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::traits::query::{NoSolution, OutlivesBound};
|
||||
use rustc_middle::ty;
|
||||
|
||||
pub mod components;
|
||||
pub mod env;
|
||||
|
|
@ -49,12 +49,15 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
pub fn resolve_regions_with_normalize(
|
||||
&self,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
deeply_normalize_ty: impl Fn(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, Ty<'tcx>>,
|
||||
deeply_normalize_ty: impl Fn(
|
||||
ty::PolyTypeOutlivesPredicate<'tcx>,
|
||||
SubregionOrigin<'tcx>,
|
||||
) -> Result<ty::PolyTypeOutlivesPredicate<'tcx>, NoSolution>,
|
||||
) -> Vec<RegionResolutionError<'tcx>> {
|
||||
match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) {
|
||||
Ok(()) => {}
|
||||
Err((ty, origin)) => {
|
||||
return vec![RegionResolutionError::CannotNormalize(ty, origin)];
|
||||
Err((clause, origin)) => {
|
||||
return vec![RegionResolutionError::CannotNormalize(clause, origin)];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -68,8 +68,9 @@ use crate::infer::{
|
|||
use crate::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use smallvec::smallvec;
|
||||
|
||||
|
|
@ -125,11 +126,15 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// invoked after all type-inference variables have been bound --
|
||||
/// right before lexical region resolution.
|
||||
#[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))]
|
||||
pub fn process_registered_region_obligations<E>(
|
||||
pub fn process_registered_region_obligations(
|
||||
&self,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
mut deeply_normalize_ty: impl FnMut(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, E>,
|
||||
) -> Result<(), (E, SubregionOrigin<'tcx>)> {
|
||||
mut deeply_normalize_ty: impl FnMut(
|
||||
PolyTypeOutlivesPredicate<'tcx>,
|
||||
SubregionOrigin<'tcx>,
|
||||
)
|
||||
-> Result<PolyTypeOutlivesPredicate<'tcx>, NoSolution>,
|
||||
) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> {
|
||||
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
|
||||
|
||||
let normalized_caller_bounds: Vec<_> = outlives_env
|
||||
|
|
@ -137,38 +142,53 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
.caller_bounds()
|
||||
.iter()
|
||||
.filter_map(|clause| {
|
||||
let bound_clause = clause.kind();
|
||||
let ty::ClauseKind::TypeOutlives(outlives) = bound_clause.skip_binder() else {
|
||||
return None;
|
||||
};
|
||||
let outlives = clause.as_type_outlives_clause()?;
|
||||
Some(
|
||||
deeply_normalize_ty(
|
||||
outlives.0,
|
||||
outlives,
|
||||
SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP),
|
||||
)
|
||||
.map(|ty| bound_clause.rebind(ty::OutlivesPredicate(ty, outlives.1))),
|
||||
// FIXME(-Znext-solver): How do we accurately report an error span here :(
|
||||
.map_err(|NoSolution| {
|
||||
(outlives, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP))
|
||||
}),
|
||||
)
|
||||
})
|
||||
// FIXME(-Znext-solver): How do we accurately report an error here :(
|
||||
.try_collect()
|
||||
.map_err(|e| (e, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)))?;
|
||||
.try_collect()?;
|
||||
|
||||
let my_region_obligations = self.take_registered_region_obligations();
|
||||
// Must loop since the process of normalizing may itself register region obligations.
|
||||
for iteration in 0.. {
|
||||
let my_region_obligations = self.take_registered_region_obligations();
|
||||
if my_region_obligations.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
|
||||
let sup_type =
|
||||
deeply_normalize_ty(sup_type, origin.clone()).map_err(|e| (e, origin.clone()))?;
|
||||
debug!(?sup_type, ?sub_region, ?origin);
|
||||
if !self.tcx.recursion_limit().value_within_limit(iteration) {
|
||||
bug!(
|
||||
"FIXME(-Znext-solver): Overflowed when processing region obligations: {my_region_obligations:#?}"
|
||||
);
|
||||
}
|
||||
|
||||
let outlives = &mut TypeOutlives::new(
|
||||
self,
|
||||
self.tcx,
|
||||
outlives_env.region_bound_pairs(),
|
||||
None,
|
||||
&normalized_caller_bounds,
|
||||
);
|
||||
let category = origin.to_constraint_category();
|
||||
outlives.type_must_outlive(origin, sup_type, sub_region, category);
|
||||
for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
|
||||
let outlives = ty::Binder::dummy(ty::OutlivesPredicate(sup_type, sub_region));
|
||||
let ty::OutlivesPredicate(sup_type, sub_region) =
|
||||
deeply_normalize_ty(outlives, origin.clone())
|
||||
.map_err(|NoSolution| (outlives, origin.clone()))?
|
||||
.no_bound_vars()
|
||||
.expect("started with no bound vars, should end with no bound vars");
|
||||
|
||||
debug!(?sup_type, ?sub_region, ?origin);
|
||||
|
||||
let outlives = &mut TypeOutlives::new(
|
||||
self,
|
||||
self.tcx,
|
||||
outlives_env.region_bound_pairs(),
|
||||
None,
|
||||
&normalized_caller_bounds,
|
||||
);
|
||||
let category = origin.to_constraint_category();
|
||||
outlives.type_must_outlive(origin, sup_type, sub_region, category);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -202,11 +202,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
|
||||
ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
|
||||
) => {
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.unify_var_var(a_vid, b_vid)
|
||||
.map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
|
||||
self.inner.borrow_mut().effect_unification_table().union(a_vid, b_vid);
|
||||
return Ok(a);
|
||||
}
|
||||
|
||||
|
|
@ -233,19 +229,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
|
||||
(ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
|
||||
return self.unify_effect_variable(
|
||||
relation.a_is_expected(),
|
||||
vid,
|
||||
EffectVarValue::Const(b),
|
||||
);
|
||||
return Ok(self.unify_effect_variable(vid, b));
|
||||
}
|
||||
|
||||
(_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
|
||||
return self.unify_effect_variable(
|
||||
!relation.a_is_expected(),
|
||||
vid,
|
||||
EffectVarValue::Const(a),
|
||||
);
|
||||
return Ok(self.unify_effect_variable(vid, a));
|
||||
}
|
||||
|
||||
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
|
||||
|
|
@ -366,18 +354,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
Ok(Ty::new_float(self.tcx, val))
|
||||
}
|
||||
|
||||
fn unify_effect_variable(
|
||||
&self,
|
||||
vid_is_expected: bool,
|
||||
vid: ty::EffectVid,
|
||||
val: EffectVarValue<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
||||
fn unify_effect_variable(&self, vid: ty::EffectVid, val: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
self.inner
|
||||
.borrow_mut()
|
||||
.effect_unification_table()
|
||||
.unify_var_value(vid, Some(val))
|
||||
.map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
|
||||
Ok(val.as_const(self.tcx))
|
||||
.union_value(vid, EffectVarValue::Known(val));
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -579,11 +561,3 @@ fn float_unification_error<'tcx>(
|
|||
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
|
||||
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
|
||||
}
|
||||
|
||||
fn effect_unification_error<'tcx>(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_a_is_expected: bool,
|
||||
(_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
|
||||
) -> TypeError<'tcx> {
|
||||
bug!("unexpected effect unification error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,14 +237,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
|
|||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => {
|
||||
debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool);
|
||||
match self.infcx.probe_effect_var(vid) {
|
||||
Some(c) => c.as_const(self.infcx.tcx),
|
||||
None => ty::Const::new_infer(
|
||||
self.infcx.probe_effect_var(vid).unwrap_or_else(|| {
|
||||
ty::Const::new_infer(
|
||||
self.infcx.tcx,
|
||||
ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)),
|
||||
self.infcx.tcx.types.bool,
|
||||
),
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
if c.has_infer() {
|
||||
|
|
|
|||
|
|
@ -749,6 +749,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(debug_macros, true);
|
||||
tracked!(default_hidden_visibility, Some(true));
|
||||
tracked!(dep_info_omit_d_target, true);
|
||||
tracked!(direct_access_external_data, Some(true));
|
||||
tracked!(dual_proc_macros, true);
|
||||
tracked!(dwarf_version, Some(5));
|
||||
tracked!(emit_thin_lto, false);
|
||||
|
|
|
|||
|
|
@ -188,6 +188,23 @@ pub(super) fn builtin(
|
|||
#[allow(rustc::potential_query_instability)]
|
||||
let possibilities: Vec<Symbol> =
|
||||
sess.parse_sess.check_config.expecteds.keys().copied().collect();
|
||||
|
||||
let mut names_possibilities: Vec<_> = if value.is_none() {
|
||||
// We later sort and display all the possibilities, so the order here does not matter.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
sess.parse_sess
|
||||
.check_config
|
||||
.expecteds
|
||||
.iter()
|
||||
.filter_map(|(k, v)| match v {
|
||||
ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
||||
let mut is_feature_cfg = name == sym::feature;
|
||||
|
||||
|
|
@ -262,17 +279,30 @@ pub(super) fn builtin(
|
|||
}
|
||||
|
||||
is_feature_cfg |= best_match == sym::feature;
|
||||
} else if !possibilities.is_empty() {
|
||||
let mut possibilities =
|
||||
possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
let possibilities = possibilities.join("`, `");
|
||||
} else {
|
||||
if !names_possibilities.is_empty() && names_possibilities.len() <= 3 {
|
||||
names_possibilities.sort();
|
||||
for cfg_name in names_possibilities.iter() {
|
||||
db.span_suggestion(
|
||||
name_span,
|
||||
"found config with similar value",
|
||||
format!("{cfg_name} = \"{name}\""),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
if !possibilities.is_empty() {
|
||||
let mut possibilities =
|
||||
possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
let possibilities = possibilities.join("`, `");
|
||||
|
||||
// The list of expected names can be long (even by default) and
|
||||
// so the diagnostic produced can take a lot of space. To avoid
|
||||
// cloging the user output we only want to print that diagnostic
|
||||
// once.
|
||||
db.help_once(format!("expected names are: `{possibilities}`"));
|
||||
// The list of expected names can be long (even by default) and
|
||||
// so the diagnostic produced can take a lot of space. To avoid
|
||||
// cloging the user output we only want to print that diagnostic
|
||||
// once.
|
||||
db.help_once(format!("expected names are: `{possibilities}`"));
|
||||
}
|
||||
}
|
||||
|
||||
let inst = if let Some((value, _value_span)) = value {
|
||||
|
|
|
|||
|
|
@ -520,6 +520,11 @@ fn register_builtins(store: &mut LintStore) {
|
|||
"illegal_floating_point_literal_pattern",
|
||||
"no longer a warning, float patterns behave the same as `==`",
|
||||
);
|
||||
store.register_removed(
|
||||
"nontrivial_structural_match",
|
||||
"no longer needed, see RFC #3535 \
|
||||
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
|
||||
);
|
||||
}
|
||||
|
||||
fn register_internals(store: &mut LintStore) {
|
||||
|
|
|
|||
|
|
@ -1435,6 +1435,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
| ty::Bound(..)
|
||||
| ty::Error(_)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Placeholder(..)
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
Some(len) => is_ty_must_use(cx, ty, expr, span)
|
||||
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
|
||||
},
|
||||
ty::Closure(..) => Some(MustUsePath::Closure(span)),
|
||||
ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
|
||||
ty::Coroutine(def_id, ..) => {
|
||||
// async fn should be treated as "implementor of `Future`"
|
||||
let must_use = if cx.tcx.coroutine_is_async(def_id) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
//! These are the built-in lints that are emitted direct in the main
|
||||
//! compiler code, rather than using their own custom pass. Those
|
||||
//! lints are all available in `rustc_lint::builtin`.
|
||||
//!
|
||||
//! When removing a lint, make sure to also add a call to `register_removed` in
|
||||
//! compiler/rustc_lint/src/lib.rs.
|
||||
|
||||
use crate::{declare_lint, declare_lint_pass, FutureIncompatibilityReason};
|
||||
use rustc_span::edition::Edition;
|
||||
|
|
@ -66,7 +69,6 @@ declare_lint_pass! {
|
|||
MUST_NOT_SUSPEND,
|
||||
NAMED_ARGUMENTS_USED_POSITIONALLY,
|
||||
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
||||
NONTRIVIAL_STRUCTURAL_MATCH,
|
||||
ORDER_DEPENDENT_TRAIT_OBJECTS,
|
||||
OVERLAPPING_RANGE_ENDPOINTS,
|
||||
PATTERNS_IN_FNS_WITHOUT_BODY,
|
||||
|
|
@ -2280,8 +2282,8 @@ declare_lint! {
|
|||
Warn,
|
||||
"constant used in pattern contains value of non-structural-match type in a field or a variant",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||
reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>",
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #120362 <https://github.com/rust-lang/rust/issues/120362>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2336,47 +2338,8 @@ declare_lint! {
|
|||
Warn,
|
||||
"pointers are not structural-match",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||
reference: "issue #62411 <https://github.com/rust-lang/rust/issues/70861>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `nontrivial_structural_match` lint detects constants that are used in patterns,
|
||||
/// whose type is not structural-match and whose initializer body actually uses values
|
||||
/// that are not structural-match. So `Option<NotStructuralMatch>` is ok if the constant
|
||||
/// is just `None`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(nontrivial_structural_match)]
|
||||
///
|
||||
/// #[derive(Copy, Clone, Debug)]
|
||||
/// struct NoDerive(u32);
|
||||
/// impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
|
||||
/// impl Eq for NoDerive { }
|
||||
/// fn main() {
|
||||
/// const INDEX: Option<NoDerive> = [None, Some(NoDerive(10))][0];
|
||||
/// match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), };
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Previous versions of Rust accepted constants in patterns, even if those constants' types
|
||||
/// did not have `PartialEq` derived. Thus the compiler falls back to runtime execution of
|
||||
/// `PartialEq`, which can report that two constants are not equal even if they are
|
||||
/// bit-equivalent.
|
||||
pub NONTRIVIAL_STRUCTURAL_MATCH,
|
||||
Warn,
|
||||
"constant used in pattern of non-structural-match type and the constant's initializer \
|
||||
expression contains values of non-structural-match types",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||
reference: "issue #73448 <https://github.com/rust-lang/rust/issues/73448>",
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #120362 <https://github.com/rust-lang/rust/issues/120362>",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ enum LLVMRustAttribute {
|
|||
SanitizeMemory = 22,
|
||||
NonLazyBind = 23,
|
||||
OptimizeNone = 24,
|
||||
ReturnsTwice = 25,
|
||||
ReadNone = 26,
|
||||
SanitizeHWAddress = 28,
|
||||
WillReturn = 29,
|
||||
|
|
|
|||
|
|
@ -934,10 +934,8 @@ LLVMRustOptimize(
|
|||
} else {
|
||||
for (const auto &C : PipelineStartEPCallbacks)
|
||||
PB.registerPipelineStartEPCallback(C);
|
||||
if (OptStage != LLVMRustOptStage::PreLinkThinLTO) {
|
||||
for (const auto &C : OptimizerLastEPCallbacks)
|
||||
PB.registerOptimizerLastEPCallback(C);
|
||||
}
|
||||
for (const auto &C : OptimizerLastEPCallbacks)
|
||||
PB.registerOptimizerLastEPCallback(C);
|
||||
|
||||
switch (OptStage) {
|
||||
case LLVMRustOptStage::PreLinkNoLTO:
|
||||
|
|
@ -945,14 +943,7 @@ LLVMRustOptimize(
|
|||
break;
|
||||
case LLVMRustOptStage::PreLinkThinLTO:
|
||||
MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel);
|
||||
// The ThinLTOPreLink pipeline already includes ThinLTOBuffer passes. However, callback
|
||||
// passes may still run afterwards. This means we need to run the buffer passes again.
|
||||
// FIXME: In LLVM 13, the ThinLTOPreLink pipeline also runs OptimizerLastEPCallbacks
|
||||
// before the RequiredLTOPreLinkPasses, in which case we can remove these hacks.
|
||||
if (OptimizerLastEPCallbacks.empty())
|
||||
NeedThinLTOBufferPasses = false;
|
||||
for (const auto &C : OptimizerLastEPCallbacks)
|
||||
C(MPM, OptLevel);
|
||||
NeedThinLTOBufferPasses = false;
|
||||
break;
|
||||
case LLVMRustOptStage::PreLinkFatLTO:
|
||||
MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel);
|
||||
|
|
|
|||
|
|
@ -250,8 +250,6 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
|
|||
return Attribute::NonLazyBind;
|
||||
case OptimizeNone:
|
||||
return Attribute::OptimizeNone;
|
||||
case ReturnsTwice:
|
||||
return Attribute::ReturnsTwice;
|
||||
case ReadNone:
|
||||
return Attribute::ReadNone;
|
||||
case SanitizeHWAddress:
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
|
||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would
|
||||
// normally need extra variables to avoid errors about multiple mutable borrows.
|
||||
macro_rules! record {
|
||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||
|
|
@ -398,7 +398,7 @@ macro_rules! record {
|
|||
}};
|
||||
}
|
||||
|
||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
|
||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_array($value))`, which would
|
||||
// normally need extra variables to avoid errors about multiple mutable borrows.
|
||||
macro_rules! record_array {
|
||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||
|
|
|
|||
|
|
@ -194,33 +194,37 @@ impl<'tcx> UnifyValue for ConstVariableValue<'tcx> {
|
|||
/// values for the effect inference variable
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum EffectVarValue<'tcx> {
|
||||
/// The host effect is on, enabling access to syscalls, filesystem access, etc.
|
||||
Host,
|
||||
/// The host effect is off. Execution is restricted to const operations only.
|
||||
NoHost,
|
||||
Const(ty::Const<'tcx>),
|
||||
Unknown,
|
||||
Known(ty::Const<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> EffectVarValue<'tcx> {
|
||||
pub fn as_const(self, tcx: TyCtxt<'tcx>) -> ty::Const<'tcx> {
|
||||
pub fn known(self) -> Option<ty::Const<'tcx>> {
|
||||
match self {
|
||||
EffectVarValue::Host => tcx.consts.true_,
|
||||
EffectVarValue::NoHost => tcx.consts.false_,
|
||||
EffectVarValue::Const(c) => c,
|
||||
EffectVarValue::Unknown => None,
|
||||
EffectVarValue::Known(value) => Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unknown(self) -> bool {
|
||||
match self {
|
||||
EffectVarValue::Unknown => true,
|
||||
EffectVarValue::Known(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
|
||||
type Error = (EffectVarValue<'tcx>, EffectVarValue<'tcx>);
|
||||
type Error = NoError;
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
||||
match (value1, value2) {
|
||||
(EffectVarValue::Host, EffectVarValue::Host) => Ok(EffectVarValue::Host),
|
||||
(EffectVarValue::NoHost, EffectVarValue::NoHost) => Ok(EffectVarValue::NoHost),
|
||||
(EffectVarValue::NoHost | EffectVarValue::Host, _)
|
||||
| (_, EffectVarValue::NoHost | EffectVarValue::Host) => Err((*value1, *value2)),
|
||||
(EffectVarValue::Const(_), EffectVarValue::Const(_)) => {
|
||||
bug!("equating two const variables, both of which have known values")
|
||||
match (*value1, *value2) {
|
||||
(EffectVarValue::Unknown, EffectVarValue::Unknown) => Ok(EffectVarValue::Unknown),
|
||||
(EffectVarValue::Unknown, EffectVarValue::Known(val))
|
||||
| (EffectVarValue::Known(val), EffectVarValue::Unknown) => {
|
||||
Ok(EffectVarValue::Known(val))
|
||||
}
|
||||
(EffectVarValue::Known(_), EffectVarValue::Known(_)) => {
|
||||
bug!("equating known inference variables: {value1:?} {value2:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +233,7 @@ impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
|
|||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub struct EffectVidKey<'tcx> {
|
||||
pub vid: ty::EffectVid,
|
||||
pub phantom: PhantomData<EffectVarValue<'tcx>>,
|
||||
pub phantom: PhantomData<ty::Const<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> From<ty::EffectVid> for EffectVidKey<'tcx> {
|
||||
|
|
@ -239,7 +243,7 @@ impl<'tcx> From<ty::EffectVid> for EffectVidKey<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> UnifyKey for EffectVidKey<'tcx> {
|
||||
type Value = Option<EffectVarValue<'tcx>>;
|
||||
type Value = EffectVarValue<'tcx>;
|
||||
#[inline]
|
||||
fn index(&self) -> u32 {
|
||||
self.vid.as_u32()
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_type_name)]
|
||||
#![feature(discriminant_kind)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![feature(coroutines)]
|
||||
|
|
|
|||
|
|
@ -74,35 +74,32 @@ bitflags! {
|
|||
/// `#[used]`: indicates that LLVM can't eliminate this function (but the
|
||||
/// linker can!).
|
||||
const USED = 1 << 9;
|
||||
/// `#[ffi_returns_twice]`, indicates that an extern function can return
|
||||
/// multiple times
|
||||
const FFI_RETURNS_TWICE = 1 << 10;
|
||||
/// `#[track_caller]`: allow access to the caller location
|
||||
const TRACK_CALLER = 1 << 11;
|
||||
const TRACK_CALLER = 1 << 10;
|
||||
/// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
|
||||
/// declaration.
|
||||
const FFI_PURE = 1 << 12;
|
||||
const FFI_PURE = 1 << 11;
|
||||
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
|
||||
/// declaration.
|
||||
const FFI_CONST = 1 << 13;
|
||||
const FFI_CONST = 1 << 12;
|
||||
/// #[cmse_nonsecure_entry]: with a TrustZone-M extension, declare a
|
||||
/// function as an entry function from Non-Secure code.
|
||||
const CMSE_NONSECURE_ENTRY = 1 << 14;
|
||||
const CMSE_NONSECURE_ENTRY = 1 << 13;
|
||||
/// `#[coverage(off)]`: indicates that the function should be ignored by
|
||||
/// the MIR `InstrumentCoverage` pass and not added to the coverage map
|
||||
/// during codegen.
|
||||
const NO_COVERAGE = 1 << 15;
|
||||
const NO_COVERAGE = 1 << 14;
|
||||
/// `#[used(linker)]`:
|
||||
/// indicates that neither LLVM nor the linker will eliminate this function.
|
||||
const USED_LINKER = 1 << 16;
|
||||
const USED_LINKER = 1 << 15;
|
||||
/// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory.
|
||||
const DEALLOCATOR = 1 << 17;
|
||||
const DEALLOCATOR = 1 << 16;
|
||||
/// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory.
|
||||
const REALLOCATOR = 1 << 18;
|
||||
const REALLOCATOR = 1 << 17;
|
||||
/// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory.
|
||||
const ALLOCATOR_ZEROED = 1 << 19;
|
||||
const ALLOCATOR_ZEROED = 1 << 18;
|
||||
/// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function.
|
||||
const NO_BUILTINS = 1 << 20;
|
||||
const NO_BUILTINS = 1 << 19;
|
||||
}
|
||||
}
|
||||
rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags }
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Given a [`DefId`] of a [`Fn`], [`FnMut`] or [`FnOnce`] traits,
|
||||
/// Given a [`DefId`] of one of the [`Fn`], [`FnMut`] or [`FnOnce`] traits,
|
||||
/// returns a corresponding [`ty::ClosureKind`].
|
||||
/// For any other [`DefId`] return `None`.
|
||||
pub fn fn_trait_kind_from_def_id(self, id: DefId) -> Option<ty::ClosureKind> {
|
||||
|
|
@ -36,6 +36,19 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given a [`DefId`] of one of the `AsyncFn`, `AsyncFnMut` or `AsyncFnOnce` traits,
|
||||
/// returns a corresponding [`ty::ClosureKind`].
|
||||
/// For any other [`DefId`] return `None`.
|
||||
pub fn async_fn_trait_kind_from_def_id(self, id: DefId) -> Option<ty::ClosureKind> {
|
||||
let items = self.lang_items();
|
||||
match Some(id) {
|
||||
x if x == items.async_fn_trait() => Some(ty::ClosureKind::Fn),
|
||||
x if x == items.async_fn_mut_trait() => Some(ty::ClosureKind::FnMut),
|
||||
x if x == items.async_fn_once_trait() => Some(ty::ClosureKind::FnOnce),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family
|
||||
/// trait, if it is defined.
|
||||
pub fn fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option<DefId> {
|
||||
|
|
|
|||
|
|
@ -140,8 +140,12 @@ fn to_profiler_name(type_name: &'static str) -> &'static str {
|
|||
/// loop that goes over each available MIR and applies `run_pass`.
|
||||
pub trait MirPass<'tcx> {
|
||||
fn name(&self) -> &'static str {
|
||||
let name = std::any::type_name::<Self>();
|
||||
if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }
|
||||
// FIXME Simplify the implementation once more `str` methods get const-stable.
|
||||
// See copypaste in `MirLint`
|
||||
const {
|
||||
let name = std::any::type_name::<Self>();
|
||||
crate::util::common::c_name(name)
|
||||
}
|
||||
}
|
||||
|
||||
fn profiler_name(&self) -> &'static str {
|
||||
|
|
@ -262,6 +266,25 @@ pub struct CoroutineInfo<'tcx> {
|
|||
/// Coroutine drop glue. This field is populated after the state transform pass.
|
||||
pub coroutine_drop: Option<Body<'tcx>>,
|
||||
|
||||
/// The body of the coroutine, modified to take its upvars by move rather than by ref.
|
||||
///
|
||||
/// This is used by coroutine-closures, which must return a different flavor of coroutine
|
||||
/// when called using `AsyncFnOnce::call_once`. It is produced by the `ByMoveBody` pass which
|
||||
/// is run right after building the initial MIR, and will only be populated for coroutines
|
||||
/// which come out of the async closure desugaring.
|
||||
///
|
||||
/// This body should be processed in lockstep with the containing body -- any optimization
|
||||
/// passes, etc, should be applied to this body as well. This is done automatically if
|
||||
/// using `run_passes`.
|
||||
pub by_move_body: Option<Body<'tcx>>,
|
||||
|
||||
/// The body of the coroutine, modified to take its upvars by mutable ref rather than by
|
||||
/// immutable ref.
|
||||
///
|
||||
/// FIXME(async_closures): This is literally the same body as the parent body. Find a better
|
||||
/// way to represent the by-mut signature (or cap the closure-kind of the coroutine).
|
||||
pub by_mut_body: Option<Body<'tcx>>,
|
||||
|
||||
/// The layout of a coroutine. This field is populated after the state transform pass.
|
||||
pub coroutine_layout: Option<CoroutineLayout<'tcx>>,
|
||||
|
||||
|
|
@ -281,6 +304,8 @@ impl<'tcx> CoroutineInfo<'tcx> {
|
|||
coroutine_kind,
|
||||
yield_ty: Some(yield_ty),
|
||||
resume_ty: Some(resume_ty),
|
||||
by_move_body: None,
|
||||
by_mut_body: None,
|
||||
coroutine_drop: None,
|
||||
coroutine_layout: None,
|
||||
}
|
||||
|
|
@ -591,6 +616,14 @@ impl<'tcx> Body<'tcx> {
|
|||
self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop.as_ref())
|
||||
}
|
||||
|
||||
pub fn coroutine_by_move_body(&self) -> Option<&Body<'tcx>> {
|
||||
self.coroutine.as_ref()?.by_move_body.as_ref()
|
||||
}
|
||||
|
||||
pub fn coroutine_by_mut_body(&self) -> Option<&Body<'tcx>> {
|
||||
self.coroutine.as_ref()?.by_mut_body.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn coroutine_kind(&self) -> Option<CoroutineKind> {
|
||||
self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind)
|
||||
|
|
|
|||
|
|
@ -402,6 +402,8 @@ impl<'tcx> CodegenUnit<'tcx> {
|
|||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::Virtual(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue