mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #125349 - matthiaskrgr:rollup-p2mbdxi, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #124050 (Remove libc from MSVC targets) - #124283 (Note for E0599 if shadowed bindings has the method.) - #125123 (Fix `read_exact` and `read_buf_exact` for `&[u8]` and `io:Cursor`) - #125158 (hir pretty: fix block indent) - #125308 (track cycle participants per root) - #125332 (Update books) - #125333 (switch to the default implementation of `write_vectored`) - #125346 (Remove some `Path::to_str` from `rustc_codegen_llvm`) Failed merges: - #125310 (Move ~100 tests from tests/ui to subdirs) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
8e7517d99a
@ -200,21 +200,20 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
|
||||
_ => panic!("unsupported arch {}", sess.target.arch),
|
||||
};
|
||||
let mut dlltool_cmd = std::process::Command::new(&dlltool);
|
||||
dlltool_cmd.args([
|
||||
"-d",
|
||||
def_file_path.to_str().unwrap(),
|
||||
"-D",
|
||||
lib_name,
|
||||
"-l",
|
||||
output_path.to_str().unwrap(),
|
||||
"-m",
|
||||
dlltool_target_arch,
|
||||
"-f",
|
||||
dlltool_target_bitness,
|
||||
"--no-leading-underscore",
|
||||
"--temp-prefix",
|
||||
temp_prefix.to_str().unwrap(),
|
||||
]);
|
||||
dlltool_cmd
|
||||
.arg("-d")
|
||||
.arg(def_file_path)
|
||||
.arg("-D")
|
||||
.arg(lib_name)
|
||||
.arg("-l")
|
||||
.arg(&output_path)
|
||||
.arg("-m")
|
||||
.arg(dlltool_target_arch)
|
||||
.arg("-f")
|
||||
.arg(dlltool_target_bitness)
|
||||
.arg("--no-leading-underscore")
|
||||
.arg("--temp-prefix")
|
||||
.arg(temp_prefix);
|
||||
|
||||
match dlltool_cmd.output() {
|
||||
Err(e) => {
|
||||
|
@ -1454,7 +1454,7 @@ impl<'a> State<'a> {
|
||||
self.word_space(":");
|
||||
}
|
||||
// containing cbox, will be closed by print-block at `}`
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.cbox(0);
|
||||
// head-box, will be closed by print-block after `{`
|
||||
self.ibox(0);
|
||||
self.print_block(blk);
|
||||
|
@ -1346,6 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if segment.ident.name != kw::Empty {
|
||||
if let Some(err) = self.report_method_error(
|
||||
span,
|
||||
Some(rcvr),
|
||||
rcvr_t,
|
||||
segment.ident,
|
||||
SelfSource::MethodCall(rcvr),
|
||||
|
@ -834,6 +834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if item_name.name != kw::Empty {
|
||||
if let Some(e) = self.report_method_error(
|
||||
span,
|
||||
None,
|
||||
ty.normalized,
|
||||
item_name,
|
||||
SelfSource::QPath(qself),
|
||||
|
@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
|
||||
use crate::Expectation;
|
||||
use crate::FnCtxt;
|
||||
use core::ops::ControlFlow;
|
||||
use hir::Expr;
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_attr::parse_confusables;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
@ -19,7 +20,6 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::PatKind::Binding;
|
||||
use rustc_hir::PathSegment;
|
||||
use rustc_hir::{ExprKind, Node, QPath};
|
||||
use rustc_infer::infer::{self, RegionVariableOrigin};
|
||||
@ -46,7 +46,7 @@ use std::borrow::Cow;
|
||||
|
||||
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
|
||||
use super::{CandidateSource, MethodError, NoMatchData};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
||||
@ -188,6 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn report_method_error(
|
||||
&self,
|
||||
span: Span,
|
||||
rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
source: SelfSource<'tcx>,
|
||||
@ -212,6 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
MethodError::NoMatch(mut no_match_data) => {
|
||||
return self.report_no_match_method_error(
|
||||
span,
|
||||
rcvr_opt,
|
||||
rcvr_ty,
|
||||
item_name,
|
||||
source,
|
||||
@ -356,9 +358,197 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
err
|
||||
}
|
||||
|
||||
pub fn suggest_use_shadowed_binding_with_method(
|
||||
&self,
|
||||
rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
|
||||
method_name: Ident,
|
||||
ty_str_reported: &str,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
#[derive(Debug)]
|
||||
struct LetStmt {
|
||||
ty_hir_id_opt: Option<hir::HirId>,
|
||||
binding_id: hir::HirId,
|
||||
span: Span,
|
||||
init_hir_id: hir::HirId,
|
||||
}
|
||||
|
||||
// Used for finding suggest binding.
|
||||
// ```rust
|
||||
// earlier binding for suggesting:
|
||||
// let y = vec![1, 2];
|
||||
// now binding:
|
||||
// if let Some(y) = x {
|
||||
// y.push(y);
|
||||
// }
|
||||
// ```
|
||||
struct LetVisitor<'a, 'tcx> {
|
||||
// Error binding which don't have `method_name`.
|
||||
binding_name: Symbol,
|
||||
binding_id: hir::HirId,
|
||||
// Used for check if the suggest binding has `method_name`.
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
call_expr: &'tcx Expr<'tcx>,
|
||||
method_name: Ident,
|
||||
// Suggest the binding which is shallowed.
|
||||
sugg_let: Option<LetStmt>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LetVisitor<'a, 'tcx> {
|
||||
// Check scope of binding.
|
||||
fn is_sub_scope(&self, sub_id: hir::ItemLocalId, super_id: hir::ItemLocalId) -> bool {
|
||||
let scope_tree = self.fcx.tcx.region_scope_tree(self.fcx.body_id);
|
||||
if let Some(sub_var_scope) = scope_tree.var_scope(sub_id)
|
||||
&& let Some(super_var_scope) = scope_tree.var_scope(super_id)
|
||||
&& scope_tree.is_subscope_of(sub_var_scope, super_var_scope)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Check if an earlier shadowed binding make `the receiver` of a MethodCall has the method.
|
||||
// If it does, record the earlier binding for subsequent notes.
|
||||
fn check_and_add_sugg_binding(&mut self, binding: LetStmt) -> bool {
|
||||
if !self.is_sub_scope(self.binding_id.local_id, binding.binding_id.local_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the earlier shadowed binding'ty and use it to check the method.
|
||||
if let Some(ty_hir_id) = binding.ty_hir_id_opt
|
||||
&& let Some(tyck_ty) = self.fcx.node_ty_opt(ty_hir_id)
|
||||
{
|
||||
if self
|
||||
.fcx
|
||||
.lookup_probe_for_diagnostic(
|
||||
self.method_name,
|
||||
tyck_ty,
|
||||
self.call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
self.sugg_let = Some(binding);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the shadowed binding has an an itializer expression,
|
||||
// use the initializer expression'ty to try to find the method again.
|
||||
// For example like: `let mut x = Vec::new();`,
|
||||
// `Vec::new()` is the itializer expression.
|
||||
if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id)
|
||||
&& self
|
||||
.fcx
|
||||
.lookup_probe_for_diagnostic(
|
||||
self.method_name,
|
||||
self_ty,
|
||||
self.call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
None,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
self.sugg_let = Some(binding);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for LetVisitor<'_, '_> {
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
|
||||
if let hir::StmtKind::Let(&hir::LetStmt { pat, ty, init, .. }) = ex.kind
|
||||
&& let hir::PatKind::Binding(_, binding_id, binding_name, ..) = pat.kind
|
||||
&& let Some(init) = init
|
||||
&& binding_name.name == self.binding_name
|
||||
&& binding_id != self.binding_id
|
||||
{
|
||||
if self.check_and_add_sugg_binding(LetStmt {
|
||||
ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None },
|
||||
binding_id: binding_id,
|
||||
span: pat.span,
|
||||
init_hir_id: init.hir_id,
|
||||
}) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
hir::intravisit::walk_stmt(self, ex)
|
||||
}
|
||||
}
|
||||
|
||||
// Used for find the error binding.
|
||||
// When the visitor reaches this point, all the shadowed bindings
|
||||
// have been found, so the visitor ends.
|
||||
fn visit_pat(&mut self, p: &'v hir::Pat<'v>) -> Self::Result {
|
||||
match p.kind {
|
||||
hir::PatKind::Binding(_, binding_id, binding_name, _) => {
|
||||
if binding_name.name == self.binding_name && binding_id == self.binding_id {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
intravisit::walk_pat(self, p);
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rcvr) = rcvr_opt
|
||||
&& let hir::ExprKind::Path(QPath::Resolved(_, path)) = rcvr.kind
|
||||
&& let hir::def::Res::Local(recv_id) = path.res
|
||||
&& let Some(segment) = path.segments.first()
|
||||
{
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body_id = self.tcx.hir().body_owned_by(self.body_id);
|
||||
let body = map.body(body_id);
|
||||
|
||||
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(rcvr.hir_id) {
|
||||
let mut let_visitor = LetVisitor {
|
||||
fcx: self,
|
||||
call_expr,
|
||||
binding_name: segment.ident.name,
|
||||
binding_id: recv_id,
|
||||
method_name,
|
||||
sugg_let: None,
|
||||
};
|
||||
let_visitor.visit_body(body);
|
||||
if let Some(sugg_let) = let_visitor.sugg_let
|
||||
&& let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id)
|
||||
{
|
||||
let _sm = self.infcx.tcx.sess.source_map();
|
||||
let rcvr_name = segment.ident.name;
|
||||
let mut span = MultiSpan::from_span(sugg_let.span);
|
||||
span.push_span_label(sugg_let.span,
|
||||
format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here"));
|
||||
span.push_span_label(
|
||||
self.tcx.hir().span(recv_id),
|
||||
format!(
|
||||
"earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`"
|
||||
),
|
||||
);
|
||||
err.span_note(
|
||||
span,
|
||||
format!(
|
||||
"there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \
|
||||
that has method `{method_name}` available"
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_no_match_method_error(
|
||||
&self,
|
||||
mut span: Span,
|
||||
rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
source: SelfSource<'tcx>,
|
||||
@ -451,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source {
|
||||
self.suggest_missing_writer(rcvr_ty, rcvr_expr)
|
||||
} else {
|
||||
tcx.dcx().create_err(NoAssociatedItem {
|
||||
let mut err = tcx.dcx().create_err(NoAssociatedItem {
|
||||
span,
|
||||
item_kind,
|
||||
item_name,
|
||||
@ -461,9 +651,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
} else {
|
||||
rcvr_ty.prefix_string(self.tcx)
|
||||
},
|
||||
ty_str: ty_str_reported,
|
||||
ty_str: ty_str_reported.clone(),
|
||||
trait_missing_method,
|
||||
})
|
||||
});
|
||||
|
||||
if is_method {
|
||||
self.suggest_use_shadowed_binding_with_method(
|
||||
rcvr_opt,
|
||||
item_name,
|
||||
&ty_str_reported,
|
||||
&mut err,
|
||||
);
|
||||
}
|
||||
|
||||
err
|
||||
};
|
||||
if tcx.sess.source_map().is_multiline(sugg_span) {
|
||||
err.span_label(sugg_span.with_hi(span.lo()), "");
|
||||
@ -2240,7 +2441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
type Result = ControlFlow<Option<&'v hir::Expr<'v>>>;
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
|
||||
if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& let hir::PatKind::Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
ControlFlow::Break(init)
|
||||
|
@ -14,11 +14,11 @@ pub struct EvaluationCache<'tcx> {
|
||||
map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct CacheData<'tcx> {
|
||||
pub result: QueryResult<'tcx>,
|
||||
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<TyCtxt<'tcx>>]>,
|
||||
pub reached_depth: usize,
|
||||
pub additional_depth: usize,
|
||||
pub encountered_overflow: bool,
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: CanonicalInput<'tcx>,
|
||||
proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<TyCtxt<'tcx>>]>,
|
||||
reached_depth: usize,
|
||||
additional_depth: usize,
|
||||
encountered_overflow: bool,
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
dep_node: DepNodeIndex,
|
||||
@ -40,17 +40,17 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
let data = WithDepNode::new(dep_node, QueryData { result, proof_tree });
|
||||
entry.cycle_participants.extend(cycle_participants);
|
||||
if encountered_overflow {
|
||||
entry.with_overflow.insert(reached_depth, data);
|
||||
entry.with_overflow.insert(additional_depth, data);
|
||||
} else {
|
||||
entry.success = Some(Success { data, reached_depth });
|
||||
entry.success = Some(Success { data, additional_depth });
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
drop(map);
|
||||
if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow })
|
||||
!= self.get(tcx, key, |_| false, Limit(reached_depth))
|
||||
{
|
||||
bug!("unable to retrieve inserted element from cache: {key:?}");
|
||||
let expected = CacheData { result, proof_tree, additional_depth, encountered_overflow };
|
||||
let actual = self.get(tcx, key, [], Limit(additional_depth));
|
||||
if !actual.as_ref().is_some_and(|actual| expected == *actual) {
|
||||
bug!("failed to lookup inserted element for {key:?}: {expected:?} != {actual:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,23 +63,25 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: CanonicalInput<'tcx>,
|
||||
cycle_participant_in_stack: impl FnOnce(&FxHashSet<CanonicalInput<'tcx>>) -> bool,
|
||||
stack_entries: impl IntoIterator<Item = CanonicalInput<'tcx>>,
|
||||
available_depth: Limit,
|
||||
) -> Option<CacheData<'tcx>> {
|
||||
let map = self.map.borrow();
|
||||
let entry = map.get(&key)?;
|
||||
|
||||
if cycle_participant_in_stack(&entry.cycle_participants) {
|
||||
return None;
|
||||
for stack_entry in stack_entries {
|
||||
if entry.cycle_participants.contains(&stack_entry) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref success) = entry.success {
|
||||
if available_depth.value_within_limit(success.reached_depth) {
|
||||
if available_depth.value_within_limit(success.additional_depth) {
|
||||
let QueryData { result, proof_tree } = success.data.get(tcx);
|
||||
return Some(CacheData {
|
||||
result,
|
||||
proof_tree,
|
||||
reached_depth: success.reached_depth,
|
||||
additional_depth: success.additional_depth,
|
||||
encountered_overflow: false,
|
||||
});
|
||||
}
|
||||
@ -90,7 +92,7 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
CacheData {
|
||||
result,
|
||||
proof_tree,
|
||||
reached_depth: available_depth.0,
|
||||
additional_depth: available_depth.0,
|
||||
encountered_overflow: true,
|
||||
}
|
||||
})
|
||||
@ -99,7 +101,7 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
|
||||
struct Success<'tcx> {
|
||||
data: WithDepNode<QueryData<'tcx>>,
|
||||
reached_depth: usize,
|
||||
additional_depth: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -47,20 +47,39 @@ struct StackEntry<I: Interner> {
|
||||
/// Whether this entry is a non-root cycle participant.
|
||||
///
|
||||
/// We must not move the result of non-root cycle participants to the
|
||||
/// global cache. See [SearchGraph::cycle_participants] for more details.
|
||||
/// We store the highest stack depth of a head of a cycle this goal is involved
|
||||
/// in. This necessary to soundly cache its provisional result.
|
||||
/// global cache. We store the highest stack depth of a head of a cycle
|
||||
/// this goal is involved in. This necessary to soundly cache its
|
||||
/// provisional result.
|
||||
non_root_cycle_participant: Option<StackDepth>,
|
||||
|
||||
encountered_overflow: bool,
|
||||
|
||||
has_been_used: HasBeenUsed,
|
||||
|
||||
/// We put only the root goal of a coinductive cycle into the global cache.
|
||||
///
|
||||
/// If we were to use that result when later trying to prove another cycle
|
||||
/// participant, we can end up with unstable query results.
|
||||
///
|
||||
/// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
|
||||
/// an example of where this is needed.
|
||||
///
|
||||
/// There can be multiple roots on the same stack, so we need to track
|
||||
/// cycle participants per root:
|
||||
/// ```plain
|
||||
/// A :- B
|
||||
/// B :- A, C
|
||||
/// C :- D
|
||||
/// D :- C
|
||||
/// ```
|
||||
cycle_participants: FxHashSet<CanonicalInput<I>>,
|
||||
/// Starts out as `None` and gets set when rerunning this
|
||||
/// goal in case we encounter a cycle.
|
||||
provisional_result: Option<QueryResult<I>>,
|
||||
}
|
||||
|
||||
/// The provisional result for a goal which is not on the stack.
|
||||
#[derive(Debug)]
|
||||
struct DetachedEntry<I: Interner> {
|
||||
/// The head of the smallest non-trivial cycle involving this entry.
|
||||
///
|
||||
@ -110,40 +129,17 @@ pub(super) struct SearchGraph<I: Interner> {
|
||||
/// An element is *deeper* in the stack if its index is *lower*.
|
||||
stack: IndexVec<StackDepth, StackEntry<I>>,
|
||||
provisional_cache: FxHashMap<CanonicalInput<I>, ProvisionalCacheEntry<I>>,
|
||||
/// We put only the root goal of a coinductive cycle into the global cache.
|
||||
///
|
||||
/// If we were to use that result when later trying to prove another cycle
|
||||
/// participant, we can end up with unstable query results.
|
||||
///
|
||||
/// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
|
||||
/// an example of where this is needed.
|
||||
cycle_participants: FxHashSet<CanonicalInput<I>>,
|
||||
}
|
||||
|
||||
impl<I: Interner> SearchGraph<I> {
|
||||
pub(super) fn new(mode: SolverMode) -> SearchGraph<I> {
|
||||
Self {
|
||||
mode,
|
||||
stack: Default::default(),
|
||||
provisional_cache: Default::default(),
|
||||
cycle_participants: Default::default(),
|
||||
}
|
||||
Self { mode, stack: Default::default(), provisional_cache: Default::default() }
|
||||
}
|
||||
|
||||
pub(super) fn solver_mode(&self) -> SolverMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// Update the stack and reached depths on cache hits.
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) {
|
||||
let reached_depth = self.stack.next_index().plus(additional_depth);
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.reached_depth = last.reached_depth.max(reached_depth);
|
||||
last.encountered_overflow |= encountered_overflow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops the highest goal from the stack, lazily updating the
|
||||
/// the next goal in the stack.
|
||||
///
|
||||
@ -159,13 +155,7 @@ impl<I: Interner> SearchGraph<I> {
|
||||
}
|
||||
|
||||
pub(super) fn is_empty(&self) -> bool {
|
||||
if self.stack.is_empty() {
|
||||
debug_assert!(self.provisional_cache.is_empty());
|
||||
debug_assert!(self.cycle_participants.is_empty());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
self.stack.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the remaining depth allowed for nested goals.
|
||||
@ -215,15 +205,26 @@ impl<I: Interner> SearchGraph<I> {
|
||||
// their result does not get moved to the global cache.
|
||||
fn tag_cycle_participants(
|
||||
stack: &mut IndexVec<StackDepth, StackEntry<I>>,
|
||||
cycle_participants: &mut FxHashSet<CanonicalInput<I>>,
|
||||
usage_kind: HasBeenUsed,
|
||||
head: StackDepth,
|
||||
) {
|
||||
stack[head].has_been_used |= usage_kind;
|
||||
debug_assert!(!stack[head].has_been_used.is_empty());
|
||||
for entry in &mut stack.raw[head.index() + 1..] {
|
||||
|
||||
// The current root of these cycles. Note that this may not be the final
|
||||
// root in case a later goal depends on a goal higher up the stack.
|
||||
let mut current_root = head;
|
||||
while let Some(parent) = stack[current_root].non_root_cycle_participant {
|
||||
current_root = parent;
|
||||
debug_assert!(!stack[current_root].has_been_used.is_empty());
|
||||
}
|
||||
|
||||
let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1);
|
||||
let current_cycle_root = &mut stack[current_root.as_usize()];
|
||||
for entry in cycle_participants {
|
||||
entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head));
|
||||
cycle_participants.insert(entry.input);
|
||||
current_cycle_root.cycle_participants.insert(entry.input);
|
||||
current_cycle_root.cycle_participants.extend(mem::take(&mut entry.cycle_participants));
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,6 +267,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
&mut ProofTreeBuilder<TyCtxt<'tcx>>,
|
||||
) -> QueryResult<TyCtxt<'tcx>>,
|
||||
) -> QueryResult<TyCtxt<'tcx>> {
|
||||
self.check_invariants();
|
||||
// Check for overflow.
|
||||
let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
@ -276,37 +278,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
return Self::response_no_constraints(tcx, input, Certainty::overflow(true));
|
||||
};
|
||||
|
||||
// Try to fetch the goal from the global cache.
|
||||
'global: {
|
||||
let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) =
|
||||
self.global_cache(tcx).get(
|
||||
tcx,
|
||||
input,
|
||||
|cycle_participants| {
|
||||
self.stack.iter().any(|entry| cycle_participants.contains(&entry.input))
|
||||
},
|
||||
available_depth,
|
||||
)
|
||||
else {
|
||||
break 'global;
|
||||
};
|
||||
|
||||
// If we're building a proof tree and the current cache entry does not
|
||||
// contain a proof tree, we do not use the entry but instead recompute
|
||||
// the goal. We simply overwrite the existing entry once we're done,
|
||||
// caching the proof tree.
|
||||
if !inspect.is_noop() {
|
||||
if let Some(revisions) = proof_tree {
|
||||
inspect.goal_evaluation_kind(
|
||||
inspect::WipCanonicalGoalEvaluationKind::Interned { revisions },
|
||||
);
|
||||
} else {
|
||||
break 'global;
|
||||
}
|
||||
}
|
||||
|
||||
self.on_cache_hit(reached_depth, encountered_overflow);
|
||||
debug!("global cache hit");
|
||||
if let Some(result) = self.lookup_global_cache(tcx, input, available_depth, inspect) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -332,12 +304,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
// already set correctly while computing the cache entry.
|
||||
inspect
|
||||
.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit);
|
||||
Self::tag_cycle_participants(
|
||||
&mut self.stack,
|
||||
&mut self.cycle_participants,
|
||||
HasBeenUsed::empty(),
|
||||
entry.head,
|
||||
);
|
||||
Self::tag_cycle_participants(&mut self.stack, HasBeenUsed::empty(), entry.head);
|
||||
return entry.result;
|
||||
} else if let Some(stack_depth) = cache_entry.stack_depth {
|
||||
debug!("encountered cycle with depth {stack_depth:?}");
|
||||
@ -354,12 +321,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
} else {
|
||||
HasBeenUsed::INDUCTIVE_CYCLE
|
||||
};
|
||||
Self::tag_cycle_participants(
|
||||
&mut self.stack,
|
||||
&mut self.cycle_participants,
|
||||
usage_kind,
|
||||
stack_depth,
|
||||
);
|
||||
Self::tag_cycle_participants(&mut self.stack, usage_kind, stack_depth);
|
||||
|
||||
// Return the provisional result or, if we're in the first iteration,
|
||||
// start with no constraints.
|
||||
@ -380,6 +342,7 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
non_root_cycle_participant: None,
|
||||
encountered_overflow: false,
|
||||
has_been_used: HasBeenUsed::empty(),
|
||||
cycle_participants: Default::default(),
|
||||
provisional_result: None,
|
||||
};
|
||||
assert_eq!(self.stack.push(entry), depth);
|
||||
@ -388,63 +351,16 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
|
||||
// This is for global caching, so we properly track query dependencies.
|
||||
// Everything that affects the `result` should be performed within this
|
||||
// `with_anon_task` closure.
|
||||
// `with_anon_task` closure. If computing this goal depends on something
|
||||
// not tracked by the cache key and from outside of this anon task, it
|
||||
// must not be added to the global cache. Notably, this is the case for
|
||||
// trait solver cycles participants.
|
||||
let ((final_entry, result), dep_node) =
|
||||
tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || {
|
||||
// When we encounter a coinductive cycle, we have to fetch the
|
||||
// result of that cycle while we are still computing it. Because
|
||||
// of this we continuously recompute the cycle until the result
|
||||
// of the previous iteration is equal to the final result, at which
|
||||
// point we are done.
|
||||
for _ in 0..FIXPOINT_STEP_LIMIT {
|
||||
let result = prove_goal(self, inspect);
|
||||
let stack_entry = self.pop_stack();
|
||||
debug_assert_eq!(stack_entry.input, input);
|
||||
|
||||
// If the current goal is not the root of a cycle, we are done.
|
||||
if stack_entry.has_been_used.is_empty() {
|
||||
return (stack_entry, result);
|
||||
}
|
||||
|
||||
// If it is a cycle head, we have to keep trying to prove it until
|
||||
// we reach a fixpoint. We need to do so for all cycle heads,
|
||||
// not only for the root.
|
||||
//
|
||||
// See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
|
||||
// for an example.
|
||||
|
||||
// Start by clearing all provisional cache entries which depend on this
|
||||
// the current goal.
|
||||
Self::clear_dependent_provisional_results(
|
||||
&mut self.provisional_cache,
|
||||
self.stack.next_index(),
|
||||
);
|
||||
|
||||
// Check whether we reached a fixpoint, either because the final result
|
||||
// is equal to the provisional result of the previous iteration, or because
|
||||
// this was only the root of either coinductive or inductive cycles, and the
|
||||
// final result is equal to the initial response for that case.
|
||||
let reached_fixpoint = if let Some(r) = stack_entry.provisional_result {
|
||||
r == result
|
||||
} else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE {
|
||||
Self::response_no_constraints(tcx, input, Certainty::Yes) == result
|
||||
} else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE {
|
||||
Self::response_no_constraints(tcx, input, Certainty::overflow(false))
|
||||
== result
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// If we did not reach a fixpoint, update the provisional result and reevaluate.
|
||||
if reached_fixpoint {
|
||||
return (stack_entry, result);
|
||||
} else {
|
||||
let depth = self.stack.push(StackEntry {
|
||||
has_been_used: HasBeenUsed::empty(),
|
||||
provisional_result: Some(result),
|
||||
..stack_entry
|
||||
});
|
||||
debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth));
|
||||
match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) {
|
||||
StepResult::Done(final_entry, result) => return (final_entry, result),
|
||||
StepResult::HasChanged => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,14 +389,13 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
} else {
|
||||
self.provisional_cache.remove(&input);
|
||||
let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len();
|
||||
let cycle_participants = mem::take(&mut self.cycle_participants);
|
||||
// When encountering a cycle, both inductive and coinductive, we only
|
||||
// move the root into the global cache. We also store all other cycle
|
||||
// participants involved.
|
||||
//
|
||||
// We must not use the global cache entry of a root goal if a cycle
|
||||
// participant is on the stack. This is necessary to prevent unstable
|
||||
// results. See the comment of `SearchGraph::cycle_participants` for
|
||||
// results. See the comment of `StackEntry::cycle_participants` for
|
||||
// more details.
|
||||
self.global_cache(tcx).insert(
|
||||
tcx,
|
||||
@ -488,15 +403,129 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
proof_tree,
|
||||
reached_depth,
|
||||
final_entry.encountered_overflow,
|
||||
cycle_participants,
|
||||
final_entry.cycle_participants,
|
||||
dep_node,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
self.check_invariants();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Try to fetch a previously computed result from the global cache,
|
||||
/// making sure to only do so if it would match the result of reevaluating
|
||||
/// this goal.
|
||||
fn lookup_global_cache(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
input: CanonicalInput<TyCtxt<'tcx>>,
|
||||
available_depth: Limit,
|
||||
inspect: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
|
||||
) -> Option<QueryResult<TyCtxt<'tcx>>> {
|
||||
let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self
|
||||
.global_cache(tcx)
|
||||
.get(tcx, input, self.stack.iter().map(|e| e.input), available_depth)?;
|
||||
|
||||
// If we're building a proof tree and the current cache entry does not
|
||||
// contain a proof tree, we do not use the entry but instead recompute
|
||||
// the goal. We simply overwrite the existing entry once we're done,
|
||||
// caching the proof tree.
|
||||
if !inspect.is_noop() {
|
||||
if let Some(revisions) = proof_tree {
|
||||
let kind = inspect::WipCanonicalGoalEvaluationKind::Interned { revisions };
|
||||
inspect.goal_evaluation_kind(kind);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the reached depth of the current goal to make sure
|
||||
// its state is the same regardless of whether we've used the
|
||||
// global cache or not.
|
||||
let reached_depth = self.stack.next_index().plus(additional_depth);
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.reached_depth = last.reached_depth.max(reached_depth);
|
||||
last.encountered_overflow |= encountered_overflow;
|
||||
}
|
||||
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
enum StepResult<I: Interner> {
|
||||
Done(StackEntry<I>, QueryResult<I>),
|
||||
HasChanged,
|
||||
}
|
||||
|
||||
impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
/// When we encounter a coinductive cycle, we have to fetch the
|
||||
/// result of that cycle while we are still computing it. Because
|
||||
/// of this we continuously recompute the cycle until the result
|
||||
/// of the previous iteration is equal to the final result, at which
|
||||
/// point we are done.
|
||||
fn fixpoint_step_in_task<F>(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
input: CanonicalInput<TyCtxt<'tcx>>,
|
||||
inspect: &mut ProofTreeBuilder<TyCtxt<'tcx>>,
|
||||
prove_goal: &mut F,
|
||||
) -> StepResult<TyCtxt<'tcx>>
|
||||
where
|
||||
F: FnMut(&mut Self, &mut ProofTreeBuilder<TyCtxt<'tcx>>) -> QueryResult<TyCtxt<'tcx>>,
|
||||
{
|
||||
let result = prove_goal(self, inspect);
|
||||
let stack_entry = self.pop_stack();
|
||||
debug_assert_eq!(stack_entry.input, input);
|
||||
|
||||
// If the current goal is not the root of a cycle, we are done.
|
||||
if stack_entry.has_been_used.is_empty() {
|
||||
return StepResult::Done(stack_entry, result);
|
||||
}
|
||||
|
||||
// If it is a cycle head, we have to keep trying to prove it until
|
||||
// we reach a fixpoint. We need to do so for all cycle heads,
|
||||
// not only for the root.
|
||||
//
|
||||
// See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
|
||||
// for an example.
|
||||
|
||||
// Start by clearing all provisional cache entries which depend on this
|
||||
// the current goal.
|
||||
Self::clear_dependent_provisional_results(
|
||||
&mut self.provisional_cache,
|
||||
self.stack.next_index(),
|
||||
);
|
||||
|
||||
// Check whether we reached a fixpoint, either because the final result
|
||||
// is equal to the provisional result of the previous iteration, or because
|
||||
// this was only the root of either coinductive or inductive cycles, and the
|
||||
// final result is equal to the initial response for that case.
|
||||
let reached_fixpoint = if let Some(r) = stack_entry.provisional_result {
|
||||
r == result
|
||||
} else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE {
|
||||
Self::response_no_constraints(tcx, input, Certainty::Yes) == result
|
||||
} else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE {
|
||||
Self::response_no_constraints(tcx, input, Certainty::overflow(false)) == result
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// If we did not reach a fixpoint, update the provisional result and reevaluate.
|
||||
if reached_fixpoint {
|
||||
StepResult::Done(stack_entry, result)
|
||||
} else {
|
||||
let depth = self.stack.push(StackEntry {
|
||||
has_been_used: HasBeenUsed::empty(),
|
||||
provisional_result: Some(result),
|
||||
..stack_entry
|
||||
});
|
||||
debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth));
|
||||
StepResult::HasChanged
|
||||
}
|
||||
}
|
||||
|
||||
fn response_no_constraints(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal: CanonicalInput<TyCtxt<'tcx>>,
|
||||
@ -505,3 +534,77 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
|
||||
Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> SearchGraph<I> {
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
fn check_invariants(&self) {
|
||||
if !cfg!(debug_assertions) {
|
||||
return;
|
||||
}
|
||||
|
||||
let SearchGraph { mode: _, stack, provisional_cache } = self;
|
||||
if stack.is_empty() {
|
||||
assert!(provisional_cache.is_empty());
|
||||
}
|
||||
|
||||
for (depth, entry) in stack.iter_enumerated() {
|
||||
let StackEntry {
|
||||
input,
|
||||
available_depth: _,
|
||||
reached_depth: _,
|
||||
non_root_cycle_participant,
|
||||
encountered_overflow: _,
|
||||
has_been_used,
|
||||
ref cycle_participants,
|
||||
provisional_result,
|
||||
} = *entry;
|
||||
let cache_entry = provisional_cache.get(&entry.input).unwrap();
|
||||
assert_eq!(cache_entry.stack_depth, Some(depth));
|
||||
if let Some(head) = non_root_cycle_participant {
|
||||
assert!(head < depth);
|
||||
assert!(cycle_participants.is_empty());
|
||||
assert_ne!(stack[head].has_been_used, HasBeenUsed::empty());
|
||||
|
||||
let mut current_root = head;
|
||||
while let Some(parent) = stack[current_root].non_root_cycle_participant {
|
||||
current_root = parent;
|
||||
}
|
||||
assert!(stack[current_root].cycle_participants.contains(&input));
|
||||
}
|
||||
|
||||
if !cycle_participants.is_empty() {
|
||||
assert!(provisional_result.is_some() || !has_been_used.is_empty());
|
||||
for entry in stack.iter().take(depth.as_usize()) {
|
||||
assert_eq!(cycle_participants.get(&entry.input), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (&input, entry) in &self.provisional_cache {
|
||||
let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } =
|
||||
entry;
|
||||
assert!(
|
||||
stack_depth.is_some()
|
||||
|| with_coinductive_stack.is_some()
|
||||
|| with_inductive_stack.is_some()
|
||||
);
|
||||
|
||||
if let &Some(stack_depth) = stack_depth {
|
||||
assert_eq!(stack[stack_depth].input, input);
|
||||
}
|
||||
|
||||
let check_detached = |detached_entry: &DetachedEntry<I>| {
|
||||
let DetachedEntry { head, result: _ } = *detached_entry;
|
||||
assert_ne!(stack[head].has_been_used, HasBeenUsed::empty());
|
||||
};
|
||||
|
||||
if let Some(with_coinductive_stack) = with_coinductive_stack {
|
||||
check_detached(with_coinductive_stack);
|
||||
}
|
||||
|
||||
if let Some(with_inductive_stack) = with_inductive_stack {
|
||||
check_detached(with_inductive_stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,9 +33,6 @@ addr2line = { version = "0.21.0", optional = true, default-features = false }
|
||||
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
|
||||
libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true }
|
||||
|
||||
[target.'cfg(all(windows, target_env = "msvc"))'.dependencies]
|
||||
libc = { version = "0.2.153", default-features = false }
|
||||
|
||||
[target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
|
||||
object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }
|
||||
|
||||
|
@ -328,7 +328,7 @@ where
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let prev_written = cursor.written();
|
||||
|
||||
Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?;
|
||||
Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?;
|
||||
|
||||
self.pos += (cursor.written() - prev_written) as u64;
|
||||
|
||||
@ -352,17 +352,24 @@ where
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
let n = buf.len();
|
||||
Read::read_exact(&mut self.remaining_slice(), buf)?;
|
||||
self.pos += n as u64;
|
||||
Ok(())
|
||||
let result = Read::read_exact(&mut self.remaining_slice(), buf);
|
||||
|
||||
match result {
|
||||
Ok(_) => self.pos += buf.len() as u64,
|
||||
// The only possible error condition is EOF, so place the cursor at "EOF"
|
||||
Err(_) => self.pos = self.inner.as_ref().len() as u64,
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let n = cursor.capacity();
|
||||
Read::read_buf_exact(&mut self.remaining_slice(), cursor)?;
|
||||
self.pos += n as u64;
|
||||
Ok(())
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let prev_written = cursor.written();
|
||||
|
||||
let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow());
|
||||
self.pos += (cursor.written() - prev_written) as u64;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
|
@ -287,6 +287,9 @@ impl Read for &[u8] {
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if buf.len() > self.len() {
|
||||
// `read_exact` makes no promise about the content of `buf` if it
|
||||
// fails so don't bother about that.
|
||||
*self = &self[self.len()..];
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
let (a, b) = self.split_at(buf.len());
|
||||
@ -307,6 +310,9 @@ impl Read for &[u8] {
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if cursor.capacity() > self.len() {
|
||||
// Append everything we can to the cursor.
|
||||
cursor.append(*self);
|
||||
*self = &self[self.len()..];
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
let (a, b) = self.split_at(cursor.capacity());
|
||||
|
@ -653,6 +653,38 @@ fn test_take_wrong_length() {
|
||||
let _ = reader.read(&mut buffer[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_read_exact_eof() {
|
||||
let slice = &b"123456"[..];
|
||||
|
||||
let mut r = slice;
|
||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
||||
assert!(r.is_empty());
|
||||
|
||||
let mut r = slice;
|
||||
let buf = &mut [0; 10];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
||||
assert!(r.is_empty());
|
||||
assert_eq!(buf.filled(), b"123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_read_exact_eof() {
|
||||
let slice = Cursor::new(b"123456");
|
||||
|
||||
let mut r = slice.clone();
|
||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
||||
assert!(r.is_empty());
|
||||
|
||||
let mut r = slice;
|
||||
let buf = &mut [0; 10];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
||||
assert!(r.is_empty());
|
||||
assert_eq!(buf.filled(), b"123456");
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_take_read(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
|
@ -435,6 +435,7 @@ extern crate alloc as alloc_crate;
|
||||
// so include it here even if it's unused.
|
||||
#[doc(masked)]
|
||||
#[allow(unused_extern_crates)]
|
||||
#[cfg(not(all(windows, target_env = "msvc")))]
|
||||
extern crate libc;
|
||||
|
||||
// We always need an unwinder currently for backtraces
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![cfg(not(all(windows, target_env = "msvc")))]
|
||||
|
||||
use crate::any::TypeId;
|
||||
|
||||
macro_rules! ok {
|
||||
|
@ -175,23 +175,12 @@ impl Socket {
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut size: isize = 0;
|
||||
|
||||
for i in bufs.iter_mut() {
|
||||
let ret: isize =
|
||||
cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?;
|
||||
|
||||
if ret != 0 {
|
||||
size += ret;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(size.try_into().unwrap())
|
||||
crate::io::default_read_vectored(|b| self.read(b), bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> {
|
||||
@ -225,17 +214,11 @@ impl Socket {
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut size: isize = 0;
|
||||
|
||||
for i in bufs.iter() {
|
||||
size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?;
|
||||
}
|
||||
|
||||
Ok(size.try_into().unwrap())
|
||||
crate::io::default_write_vectored(|b| self.write(b), bufs)
|
||||
}
|
||||
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_timeout(&self, dur: Option<Duration>, kind: i32) -> io::Result<()> {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit bebcf527e67755a989a1739b7cfaa8f0e6b30040
|
||||
Subproject commit 5e9051f71638aa941cd5dda465e25c61cde9594f
|
@ -1 +1 @@
|
||||
Subproject commit 17842ebb050f62e40a4618edeb8e8ee86e758707
|
||||
Subproject commit dd962bb82865a5284f2404e5234f1e3222b9c022
|
@ -1 +1 @@
|
||||
Subproject commit 51817951d0d213a0011f82b62aae02c3b3f2472e
|
||||
Subproject commit e356977fceaa8591c762312d8d446769166d4b3e
|
@ -1 +1 @@
|
||||
Subproject commit 229ad13b64d919b12e548d560f06d88963b25cd3
|
||||
Subproject commit 20482893d1a502df72f76762c97aed88854cdf81
|
@ -1 +1 @@
|
||||
Subproject commit 2d1947ff34d50ca46dfe242ad75531a4c429bb52
|
||||
Subproject commit b6d4a4940bab85cc91eec70cc2e3096dd48da62d
|
@ -1,38 +1,21 @@
|
||||
// Test what happens we save incremental compilation state that makes
|
||||
// use of foreign items. This used to ICE (#34991).
|
||||
//@ ignore-sgx no libc
|
||||
|
||||
//@ revisions: rpass1
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::ffi::CString;
|
||||
|
||||
mod mlibc {
|
||||
use libc::{c_char, c_long, c_longlong};
|
||||
|
||||
extern "C" {
|
||||
pub fn atol(x: *const c_char) -> c_long;
|
||||
pub fn atoll(x: *const c_char) -> c_longlong;
|
||||
// strlen is provided either by an external library or compiler-builtins as a fallback
|
||||
pub fn strlen(x: *const std::ffi::c_char) -> usize;
|
||||
}
|
||||
}
|
||||
|
||||
fn atol(s: String) -> isize {
|
||||
fn strlen(s: String) -> usize {
|
||||
let c = CString::new(s).unwrap();
|
||||
unsafe { mlibc::atol(c.as_ptr()) as isize }
|
||||
}
|
||||
|
||||
fn atoll(s: String) -> i64 {
|
||||
let c = CString::new(s).unwrap();
|
||||
unsafe { mlibc::atoll(c.as_ptr()) as i64 }
|
||||
unsafe { mlibc::strlen(c.as_ptr()) }
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(atol("1024".to_string()) * 10, atol("10240".to_string()));
|
||||
assert_eq!(
|
||||
(atoll("11111111111111111".to_string()) * 10),
|
||||
atoll("111111111111111110".to_string())
|
||||
);
|
||||
assert_eq!(strlen("1024".to_string()), strlen("2048".to_string()));
|
||||
}
|
||||
|
@ -11,15 +11,15 @@ extern crate std;
|
||||
fn foo(_: [i32; (3 as usize)]) ({ } as ())
|
||||
|
||||
fn bar() ({
|
||||
const FOO: usize = ((5 as usize) - (4 as usize) as usize);
|
||||
let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]);
|
||||
const FOO: usize = ((5 as usize) - (4 as usize) as usize);
|
||||
let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]);
|
||||
|
||||
let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]);
|
||||
let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]);
|
||||
|
||||
let _ =
|
||||
(((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as
|
||||
&[i32; 3]) as *const _ as *const [i32; 3]) as
|
||||
*const [i32; (3 as usize)] as *const [i32; 3]);
|
||||
let _ =
|
||||
(((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as &[i32; 3])
|
||||
as *const _ as *const [i32; 3]) as *const [i32; (3 as usize)]
|
||||
as *const [i32; 3]);
|
||||
|
||||
|
||||
|
||||
@ -29,17 +29,17 @@ fn bar() ({
|
||||
|
||||
|
||||
|
||||
({
|
||||
let res =
|
||||
((::alloc::fmt::format as
|
||||
for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const
|
||||
as
|
||||
fn(&[&'static str]) -> Arguments<'_> {Arguments::<'_>::new_const})((&([("test"
|
||||
as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>))
|
||||
as String);
|
||||
(res as String)
|
||||
} as String);
|
||||
} as ())
|
||||
({
|
||||
let res =
|
||||
((::alloc::fmt::format as
|
||||
for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const
|
||||
as
|
||||
fn(&[&'static str]) -> Arguments<'_> {Arguments::<'_>::new_const})((&([("test"
|
||||
as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>))
|
||||
as String);
|
||||
(res as String)
|
||||
} as String);
|
||||
} as ())
|
||||
type Foo = [i32; (3 as usize)];
|
||||
struct Bar {
|
||||
x: [i32; (3 as usize)],
|
||||
@ -48,9 +48,9 @@ struct TupleBar([i32; (4 as usize)]);
|
||||
enum Baz { BazVariant([i32; (5 as usize)]), }
|
||||
fn id<T>(x: T) -> T ({ (x as T) } as T)
|
||||
fn use_id() ({
|
||||
let _ =
|
||||
((id::<[i32; (3 as usize)]> as
|
||||
fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
|
||||
(2 as i32), (3 as i32)] as [i32; 3])) as [i32; 3]);
|
||||
} as ())
|
||||
let _ =
|
||||
((id::<[i32; (3 as usize)]> as
|
||||
fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
|
||||
(2 as i32), (3 as i32)] as [i32; 3])) as [i32; 3]);
|
||||
} as ())
|
||||
fn main() ({ } as ())
|
||||
|
@ -1,10 +1,7 @@
|
||||
#![crate_type = "staticlib"]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use libc::{c_char, c_double, c_int, c_long, c_longlong};
|
||||
use std::ffi::{c_char, c_double, c_int, c_long, c_longlong};
|
||||
use std::ffi::VaList;
|
||||
use std::ffi::{CString, CStr};
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
use std::ffi::c_int;
|
||||
|
||||
#[link(name = "foo", kind = "static")]
|
||||
extern "C" {
|
||||
fn should_return_one() -> libc::c_int;
|
||||
fn should_return_one() -> c_int;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -26,6 +26,14 @@ error[E0599]: no method named `push` found for struct `VecDeque` in the current
|
||||
LL | x.push(1);
|
||||
| ^^^^ method not found in `VecDeque<_>`
|
||||
|
|
||||
note: there's an earlier shadowed binding `x` of type `Vec<_>` that has method `push` available
|
||||
--> $DIR/rustc_confusables_std_cases.rs:8:9
|
||||
|
|
||||
LL | let mut x = Vec::new();
|
||||
| ^^^^^ `x` of type `Vec<_>` that has method `push` defined earlier here
|
||||
...
|
||||
LL | let mut x = VecDeque::new();
|
||||
| ----- earlier `x` shadowed here with type `VecDeque`
|
||||
help: you might have meant to use `push_back`
|
||||
|
|
||||
LL | x.push_back(1);
|
||||
|
@ -10,6 +10,14 @@ LL | struct NonCopy;
|
||||
LL | _ = x.clone();
|
||||
| ^^^^^ method cannot be called on `Foo<NonCopy>` due to unsatisfied trait bounds
|
||||
|
|
||||
note: there's an earlier shadowed binding `x` of type `Foo<u32>` that has method `clone` available
|
||||
--> $DIR/deriving-with-repr-packed-2.rs:13:9
|
||||
|
|
||||
LL | let x: Foo<u32> = Foo(1, 2, 3);
|
||||
| ^ `x` of type `Foo<u32>` that has method `clone` defined earlier here
|
||||
...
|
||||
LL | let x: Foo<NonCopy> = Foo(NonCopy, NonCopy, NonCopy);
|
||||
| - earlier `x` shadowed here with type `Foo<NonCopy>`
|
||||
note: the following trait bounds were not satisfied:
|
||||
`NonCopy: Clone`
|
||||
`NonCopy: Copy`
|
||||
|
@ -1,21 +1,15 @@
|
||||
// Ensure that env::vars() does not panic if environ is null.
|
||||
// Regression test for rust-lang/rust#53200
|
||||
//@ run-pass
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
//@ ignore-windows
|
||||
|
||||
// issue-53200
|
||||
|
||||
#![feature(rustc_private)]
|
||||
extern crate libc;
|
||||
|
||||
use std::env;
|
||||
|
||||
// FIXME: more platforms?
|
||||
#[cfg(target_os = "linux")]
|
||||
fn main() {
|
||||
extern crate libc;
|
||||
unsafe { libc::clearenv(); }
|
||||
assert_eq!(env::vars().count(), 0);
|
||||
assert_eq!(std::env::vars().count(), 0);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![feature(rustc_private)]
|
||||
#![feature(test)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
extern crate libc as alloc;
|
||||
extern crate test as alloc;
|
||||
//~^ ERROR E0259
|
||||
|
||||
fn main() {}
|
||||
|
@ -4,13 +4,13 @@ error[E0259]: the name `alloc` is defined multiple times
|
||||
LL | extern crate alloc;
|
||||
| ------------------- previous import of the extern crate `alloc` here
|
||||
LL |
|
||||
LL | extern crate libc as alloc;
|
||||
LL | extern crate test as alloc;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `alloc` reimported here
|
||||
|
|
||||
= note: `alloc` must be defined only once in the type namespace of this module
|
||||
help: you can use `as` to change the binding name of the import
|
||||
|
|
||||
LL | extern crate libc as other_alloc;
|
||||
LL | extern crate test as other_alloc;
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -1,5 +1,5 @@
|
||||
// gate-test-rustc_private
|
||||
|
||||
extern crate libc; //~ ERROR use of unstable library feature 'rustc_private'
|
||||
extern crate cfg_if; //~ ERROR use of unstable library feature 'rustc_private'
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead?
|
||||
--> $DIR/rustc-private.rs:3:1
|
||||
|
|
||||
LL | extern crate libc;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
LL | extern crate cfg_if;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #27812 <https://github.com/rust-lang/rust/issues/27812> for more information
|
||||
= help: add `#![feature(rustc_private)]` to the crate attributes to enable
|
||||
|
@ -1,20 +1,14 @@
|
||||
//@ run-pass
|
||||
//@ ignore-sgx no libc
|
||||
|
||||
// Ensure no false positive on "unused extern crate" lint
|
||||
#![deny(unused_extern_crates)]
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
use std::ffi::CString;
|
||||
|
||||
mod mlibc {
|
||||
use libc::{c_char, size_t};
|
||||
use std::ffi::c_char;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "strlen"]
|
||||
pub fn my_strlen(str: *const c_char) -> size_t;
|
||||
pub fn my_strlen(str: *const c_char) -> usize;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
//@ run-pass
|
||||
#![allow(dead_code)]
|
||||
//@ pretty-expanded FIXME #23616
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
#![allow(dead_code)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
mod bar {
|
||||
extern "C" {}
|
||||
@ -13,14 +12,37 @@ mod zed {
|
||||
extern "C" {}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod mlibc {
|
||||
use libc::{c_int, c_void, size_t, ssize_t};
|
||||
extern crate libc;
|
||||
use self::libc::{c_int, c_void, size_t, ssize_t};
|
||||
|
||||
extern "C" {
|
||||
pub fn write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod mlibc {
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
pub type BOOL = i32;
|
||||
pub type HANDLE = *mut c_void;
|
||||
|
||||
#[link(name = "ntdll")]
|
||||
extern "system" {
|
||||
pub fn WriteFile(
|
||||
hfile: HANDLE,
|
||||
lpbuffer: *const u8,
|
||||
nnumberofbytestowrite: u32,
|
||||
lpnumberofbyteswritten: *mut u32,
|
||||
lpoverlapped: *mut c_void,
|
||||
) -> BOOL;
|
||||
}
|
||||
}
|
||||
|
||||
mod baz {
|
||||
extern "C" {}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
fn main() {
|
||||
extern crate libc; //~ ERROR use of unstable
|
||||
use libc::*; //~ ERROR unresolved import
|
||||
extern crate test; //~ ERROR use of unstable
|
||||
use test::*; //~ ERROR unresolved import
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
error[E0432]: unresolved import `libc`
|
||||
error[E0432]: unresolved import `test`
|
||||
--> $DIR/issue-37887.rs:3:9
|
||||
|
|
||||
LL | use libc::*;
|
||||
| ^^^^ maybe a missing crate `libc`?
|
||||
LL | use test::*;
|
||||
| ^^^^ maybe a missing crate `test`?
|
||||
|
|
||||
= help: consider adding `extern crate libc` to use the `libc` crate
|
||||
= help: consider adding `extern crate test` to use the `test` crate
|
||||
|
||||
error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead?
|
||||
error[E0658]: use of unstable library feature 'test'
|
||||
--> $DIR/issue-37887.rs:2:5
|
||||
|
|
||||
LL | extern crate libc;
|
||||
LL | extern crate test;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #27812 <https://github.com/rust-lang/rust/issues/27812> for more information
|
||||
= help: add `#![feature(rustc_private)]` to the crate attributes to enable
|
||||
= note: see issue #50297 <https://github.com/rust-lang/rust/issues/50297> for more information
|
||||
= help: add `#![feature(test)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -1,12 +1,12 @@
|
||||
//@ edition:2018
|
||||
|
||||
#![deny(unused_extern_crates)]
|
||||
#![feature(test, rustc_private)]
|
||||
#![feature(test)]
|
||||
|
||||
extern crate libc;
|
||||
extern crate core;
|
||||
//~^ ERROR unused extern crate
|
||||
//~| HELP remove
|
||||
extern crate libc as x;
|
||||
extern crate core as x;
|
||||
//~^ ERROR unused extern crate
|
||||
//~| HELP remove
|
||||
|
||||
@ -28,11 +28,11 @@ mod foo {
|
||||
|
||||
pub(super) extern crate alloc as d;
|
||||
|
||||
extern crate libc;
|
||||
extern crate core;
|
||||
//~^ ERROR unused extern crate
|
||||
//~| HELP remove
|
||||
|
||||
extern crate libc as x;
|
||||
extern crate core as x;
|
||||
//~^ ERROR unused extern crate
|
||||
//~| HELP remove
|
||||
|
||||
@ -41,11 +41,11 @@ mod foo {
|
||||
pub extern crate test as y;
|
||||
|
||||
mod bar {
|
||||
extern crate libc;
|
||||
extern crate core;
|
||||
//~^ ERROR unused extern crate
|
||||
//~| HELP remove
|
||||
|
||||
extern crate libc as x;
|
||||
extern crate core as x;
|
||||
//~^ ERROR unused extern crate
|
||||
//~| HELP remove
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
error: unused extern crate
|
||||
--> $DIR/unnecessary-extern-crate.rs:6:1
|
||||
|
|
||||
LL | extern crate libc;
|
||||
LL | extern crate core;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: remove it
|
||||
|
|
||||
note: the lint level is defined here
|
||||
@ -13,31 +13,31 @@ LL | #![deny(unused_extern_crates)]
|
||||
error: unused extern crate
|
||||
--> $DIR/unnecessary-extern-crate.rs:9:1
|
||||
|
|
||||
LL | extern crate libc as x;
|
||||
LL | extern crate core as x;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
|
||||
|
||||
error: unused extern crate
|
||||
--> $DIR/unnecessary-extern-crate.rs:31:5
|
||||
|
|
||||
LL | extern crate libc;
|
||||
LL | extern crate core;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: remove it
|
||||
|
||||
error: unused extern crate
|
||||
--> $DIR/unnecessary-extern-crate.rs:35:5
|
||||
|
|
||||
LL | extern crate libc as x;
|
||||
LL | extern crate core as x;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
|
||||
|
||||
error: unused extern crate
|
||||
--> $DIR/unnecessary-extern-crate.rs:44:9
|
||||
|
|
||||
LL | extern crate libc;
|
||||
LL | extern crate core;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: remove it
|
||||
|
||||
error: unused extern crate
|
||||
--> $DIR/unnecessary-extern-crate.rs:48:9
|
||||
|
|
||||
LL | extern crate libc as x;
|
||||
LL | extern crate core as x;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
@ -7,10 +7,10 @@ extern crate std;
|
||||
//@ check-pass
|
||||
|
||||
fn main() ({
|
||||
(if (true as bool)
|
||||
({ } as
|
||||
()) else if (let Some(a) =
|
||||
((Some as
|
||||
fn(i32) -> Option<i32> {Option::<i32>::Some})((3 as i32)) as
|
||||
Option<i32>) as bool) ({ } as ()) as ())
|
||||
} as ())
|
||||
(if (true as bool)
|
||||
({ } as
|
||||
()) else if (let Some(a) =
|
||||
((Some as
|
||||
fn(i32) -> Option<i32> {Option::<i32>::Some})((3 as i32)) as
|
||||
Option<i32>) as bool) ({ } as ()) as ())
|
||||
} as ())
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Test that `download-rustc` doesn't put duplicate copies of libc in the sysroot.
|
||||
//@ check-pass
|
||||
//@ ignore-windows doesn't necessarily have the libc crate
|
||||
#![crate_type = "lib"]
|
||||
#![no_std]
|
||||
#![feature(rustc_private)]
|
||||
|
@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
let x = Some(3);
|
||||
let y = vec![1, 2];
|
||||
if let Some(y) = x {
|
||||
y.push(y); //~ ERROR E0599
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
error[E0599]: no method named `push` found for type `{integer}` in the current scope
|
||||
--> $DIR/account-for-shadowed-bindings-issue-123558.rs:5:11
|
||||
|
|
||||
LL | y.push(y);
|
||||
| ^^^^ method not found in `{integer}`
|
||||
|
|
||||
note: there's an earlier shadowed binding `y` of type `Vec<{integer}>` that has method `push` available
|
||||
--> $DIR/account-for-shadowed-bindings-issue-123558.rs:3:9
|
||||
|
|
||||
LL | let y = vec![1, 2];
|
||||
| ^ `y` of type `Vec<{integer}>` that has method `push` defined earlier here
|
||||
LL | if let Some(y) = x {
|
||||
| - earlier `y` shadowed here with type `{integer}`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
@ -5,11 +5,13 @@
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
use std::env;
|
||||
use std::ffi::c_int;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
#[cfg(unix)]
|
||||
unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
|
||||
@ -36,14 +38,14 @@ unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn assert_fd_is_valid(fd: libc::c_int) {
|
||||
fn assert_fd_is_valid(fd: c_int) {
|
||||
if unsafe { libc::fcntl(fd, libc::F_GETFD) == -1 } {
|
||||
panic!("file descriptor {} is not valid: {}", fd, io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn assert_fd_is_valid(_fd: libc::c_int) {}
|
||||
fn assert_fd_is_valid(_fd: c_int) {}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@ run-pass
|
||||
//@ aux-build:sigpipe-utils.rs
|
||||
//@ compile-flags: -Zon-broken-pipe=error
|
||||
//@ only-unix because SIGPIPE is a unix thing
|
||||
|
||||
fn main() {
|
||||
extern crate sigpipe_utils;
|
||||
|
@ -4,6 +4,7 @@
|
||||
//@ aux-bin: assert-inherit-sig_ign.rs
|
||||
//@ run-pass
|
||||
//@ compile-flags: -Zon-broken-pipe=kill
|
||||
//@ only-unix because SIGPIPE is a unix thing
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@ run-pass
|
||||
//@ aux-build:sigpipe-utils.rs
|
||||
//@ compile-flags: -Zon-broken-pipe=kill
|
||||
//@ only-unix because SIGPIPE is a unix thing
|
||||
|
||||
fn main() {
|
||||
extern crate sigpipe_utils;
|
||||
|
@ -1,5 +1,6 @@
|
||||
//@ run-pass
|
||||
//@ aux-build:sigpipe-utils.rs
|
||||
//@ only-unix because SIGPIPE is a unix thing
|
||||
|
||||
fn main() {
|
||||
extern crate sigpipe_utils;
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@ run-pass
|
||||
//@ aux-build:sigpipe-utils.rs
|
||||
//@ compile-flags: -Zon-broken-pipe=kill
|
||||
//@ only-unix because SIGPIPE is a unix thing
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
|
@ -8,21 +8,16 @@
|
||||
//@ ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590)
|
||||
//@ ignore-nto no stack overflow handler used (no alternate stack available)
|
||||
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
use std::env;
|
||||
use std::hint::black_box;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
|
||||
// Inlining to avoid llvm turning the recursive functions into tail calls,
|
||||
// which doesn't consume stack.
|
||||
#[inline(always)]
|
||||
pub fn black_box<T>(dummy: T) { std::intrinsics::black_box(dummy); }
|
||||
|
||||
fn silent_recurse() {
|
||||
let buf = [0u8; 1000];
|
||||
black_box(buf);
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ ignore-emscripten
|
||||
//@ only-unix
|
||||
|
||||
// Emscripten doesn't flush its own stdout buffers on exit, which would fail
|
||||
// this test. So this test is disabled on this platform.
|
20
tests/ui/runtime/stdout-during-shutdown-windows.rs
Normal file
20
tests/ui/runtime/stdout-during-shutdown-windows.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//@ run-pass
|
||||
//@ check-run-results
|
||||
//@ only-windows
|
||||
|
||||
struct Bye;
|
||||
|
||||
impl Drop for Bye {
|
||||
fn drop(&mut self) {
|
||||
print!(", world!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
thread_local!{
|
||||
static BYE: Bye = Bye;
|
||||
}
|
||||
BYE.with(|_| {
|
||||
print!("hello");
|
||||
});
|
||||
}
|
@ -0,0 +1 @@
|
||||
hello, world!
|
@ -25,8 +25,8 @@ LL + let str::as_bytes;
|
||||
|
|
||||
|
||||
error[E0533]: expected unit struct, unit variant or constant, found associated function `str<{
|
||||
fn str() { let (/*ERROR*/); }
|
||||
}, T>::as_bytes`
|
||||
fn str() { let (/*ERROR*/); }
|
||||
}, T>::as_bytes`
|
||||
--> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:9
|
||||
|
|
||||
LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
|
@ -1,3 +1,4 @@
|
||||
//@ revisions: with without
|
||||
//@ compile-flags: -Znext-solver
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
@ -56,6 +57,7 @@ where
|
||||
X: IncompleteGuidance<u32, i8>,
|
||||
X: IncompleteGuidance<u32, i16>,
|
||||
{
|
||||
#[cfg(with)]
|
||||
impls_trait::<B<X>, _, _, _>(); // entering the cycle from `B` works
|
||||
|
||||
// entering the cycle from `A` fails, but would work if we were to use the cache
|
||||
|
@ -1,12 +1,12 @@
|
||||
error[E0277]: the trait bound `A<X>: Trait<_, _, _>` is not satisfied
|
||||
--> $DIR/incompleteness-unstable-result.rs:63:19
|
||||
--> $DIR/incompleteness-unstable-result.rs:65:19
|
||||
|
|
||||
LL | impls_trait::<A<X>, _, _, _>();
|
||||
| ^^^^ the trait `Trait<_, _, _>` is not implemented for `A<X>`, which is required by `A<X>: Trait<_, _, _>`
|
||||
|
|
||||
= help: the trait `Trait<U, V, D>` is implemented for `A<T>`
|
||||
note: required for `A<X>` to implement `Trait<_, _, _>`
|
||||
--> $DIR/incompleteness-unstable-result.rs:32:50
|
||||
--> $DIR/incompleteness-unstable-result.rs:33:50
|
||||
|
|
||||
LL | impl<T: ?Sized, U: ?Sized, V: ?Sized, D: ?Sized> Trait<U, V, D> for A<T>
|
||||
| ^^^^^^^^^^^^^^ ^^^^
|
||||
@ -16,7 +16,7 @@ LL | A<T>: Trait<U, D, V>,
|
||||
= note: 8 redundant requirements hidden
|
||||
= note: required for `A<X>` to implement `Trait<_, _, _>`
|
||||
note: required by a bound in `impls_trait`
|
||||
--> $DIR/incompleteness-unstable-result.rs:51:28
|
||||
--> $DIR/incompleteness-unstable-result.rs:52:28
|
||||
|
|
||||
LL | fn impls_trait<T: ?Sized + Trait<U, V, D>, U: ?Sized, V: ?Sized, D: ?Sized>() {}
|
||||
| ^^^^^^^^^^^^^^ required by this bound in `impls_trait`
|
@ -0,0 +1,26 @@
|
||||
error[E0277]: the trait bound `A<X>: Trait<_, _, _>` is not satisfied
|
||||
--> $DIR/incompleteness-unstable-result.rs:65:19
|
||||
|
|
||||
LL | impls_trait::<A<X>, _, _, _>();
|
||||
| ^^^^ the trait `Trait<_, _, _>` is not implemented for `A<X>`, which is required by `A<X>: Trait<_, _, _>`
|
||||
|
|
||||
= help: the trait `Trait<U, V, D>` is implemented for `A<T>`
|
||||
note: required for `A<X>` to implement `Trait<_, _, _>`
|
||||
--> $DIR/incompleteness-unstable-result.rs:33:50
|
||||
|
|
||||
LL | impl<T: ?Sized, U: ?Sized, V: ?Sized, D: ?Sized> Trait<U, V, D> for A<T>
|
||||
| ^^^^^^^^^^^^^^ ^^^^
|
||||
...
|
||||
LL | A<T>: Trait<U, D, V>,
|
||||
| -------------- unsatisfied trait bound introduced here
|
||||
= note: 8 redundant requirements hidden
|
||||
= note: required for `A<X>` to implement `Trait<_, _, _>`
|
||||
note: required by a bound in `impls_trait`
|
||||
--> $DIR/incompleteness-unstable-result.rs:52:28
|
||||
|
|
||||
LL | fn impls_trait<T: ?Sized + Trait<U, V, D>, U: ?Sized, V: ?Sized, D: ?Sized>() {}
|
||||
| ^^^^^^^^^^^^^^ required by this bound in `impls_trait`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -10,5 +10,5 @@ extern crate std;
|
||||
trait Animal { }
|
||||
|
||||
fn main() {
|
||||
type ServeFut = /*impl Trait*/;
|
||||
}
|
||||
type ServeFut = /*impl Trait*/;
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ extern crate std;
|
||||
|
||||
// In #100948 this caused an ICE with -Zunpretty=hir.
|
||||
fn main() {
|
||||
<bad-literal>;
|
||||
}
|
||||
<bad-literal>;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use ::std::prelude::rust_2015::*;
|
||||
extern crate std;
|
||||
|
||||
fn main() {
|
||||
let _ =
|
||||
#[rustc_box]
|
||||
Box::new(1);
|
||||
}
|
||||
let _ =
|
||||
#[rustc_box]
|
||||
Box::new(1);
|
||||
}
|
||||
|
@ -6,10 +6,10 @@ extern crate std;
|
||||
//@ check-pass
|
||||
|
||||
fn main() {
|
||||
let x = 1;
|
||||
// Should flatten to println!("a 123 b {x} xyz\n"):
|
||||
{
|
||||
::std::io::_print(format_arguments::new_v1(&["a 123 b ",
|
||||
" xyz\n"], &[format_argument::new_display(&x)]));
|
||||
};
|
||||
}
|
||||
let x = 1;
|
||||
// Should flatten to println!("a 123 b {x} xyz\n"):
|
||||
{
|
||||
::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"],
|
||||
&[format_argument::new_display(&x)]));
|
||||
};
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ extern crate std;
|
||||
|
||||
fn foo(x:
|
||||
Option<u32>) {
|
||||
let Some(_) = x else
|
||||
{
|
||||
let Some(_) = x else
|
||||
{
|
||||
|
||||
{ ::std::rt::begin_panic("explicit panic") }
|
||||
};
|
||||
}
|
||||
{ ::std::rt::begin_panic("explicit panic") }
|
||||
};
|
||||
}
|
||||
fn main() { }
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
// The output from "ps -A -o pid,ppid,args" should look like this:
|
||||
@ -28,6 +26,7 @@ use std::process::Command;
|
||||
|
||||
#[cfg(unix)]
|
||||
fn find_zombies() {
|
||||
extern crate libc;
|
||||
let my_pid = unsafe { libc::getpid() };
|
||||
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
|
||||
|
Loading…
Reference in New Issue
Block a user