Auto merge of #132035 - matthiaskrgr:rollup-ty1e4q0, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #125205 (Fixup Windows verbatim paths when used with the `include!` macro)
 - #131049 (Validate args are correct for `UnevaluatedConst`, `ExistentialTraitRef`/`ExistentialProjection`)
 - #131549 (Add a note for `?` on a `impl Future<Output = Result<..>>` in sync function)
 - #131731 (add `TestFloatParse` to `tools.rs` for bootstrap)
 - #131732 (Add doc(plugins), doc(passes), etc. to INVALID_DOC_ATTRIBUTES)
 - #132006 (don't stage-off to previous compiler when CI rustc is available)
 - #132022 (Move `cmp_in_dominator_order` out of graph dominator computation)
 - #132033 (compiletest: Make `line_directive` return a `DirectiveLine`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-10-22 14:16:37 +00:00
commit 86d69c705a
35 changed files with 475 additions and 255 deletions

View File

@ -9,8 +9,6 @@
//! Thomas Lengauer and Robert Endre Tarjan.
//! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
use std::cmp::Ordering;
use rustc_index::{Idx, IndexSlice, IndexVec};
use super::ControlFlowGraph;
@ -64,9 +62,6 @@ fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool {
}
fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
// compute the post order index (rank) for each node
let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
// We allocate capacity for the full set of nodes, because most of the time
// most of the nodes *are* reachable.
let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
@ -83,12 +78,10 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
pre_order_to_real.push(graph.start_node());
parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now.
real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO);
let mut post_order_idx = 0;
// Traverse the graph, collecting a number of things:
//
// * Preorder mapping (to it, and back to the actual ordering)
// * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product)
// * Parents for each vertex in the preorder tree
//
// These are all done here rather than through one of the 'standard'
@ -104,8 +97,6 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
continue 'recurse;
}
}
post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx;
post_order_idx += 1;
stack.pop();
}
@ -282,7 +273,7 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
let time = compute_access_time(start_node, &immediate_dominators);
Inner { post_order_rank, immediate_dominators, time }
Inner { immediate_dominators, time }
}
/// Evaluate the link-eval virtual forest, providing the currently minimum semi
@ -348,7 +339,6 @@ fn compress(
/// Tracks the list of dominators for each node.
#[derive(Clone, Debug)]
struct Inner<N: Idx> {
post_order_rank: IndexVec<N, usize>,
// Even though we track only the immediate dominator of each node, it's
// possible to get its full list of dominators by looking up the dominator
// of each dominator.
@ -379,17 +369,6 @@ impl<Node: Idx> Dominators<Node> {
}
}
/// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
/// relationship, the dominator will always precede the dominated. (The relative ordering
/// of two unrelated nodes will also be consistent, but otherwise the order has no
/// meaning.) This method cannot be used to determine if either Node dominates the other.
pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering {
match &self.kind {
Kind::Path => lhs.index().cmp(&rhs.index()),
Kind::General(g) => g.post_order_rank[rhs].cmp(&g.post_order_rank[lhs]),
}
}
/// Returns true if `a` dominates `b`.
///
/// # Panics

View File

@ -1,5 +1,6 @@
use std::default::Default;
use std::iter;
use std::path::Component::Prefix;
use std::path::{Path, PathBuf};
use std::rc::Rc;
@ -1293,7 +1294,12 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
base_path.push(path);
Ok(base_path)
} else {
Ok(path)
// This ensures that Windows verbatim paths are fixed if mixed path separators are used,
// which can happen when `concat!` is used to join paths.
match path.components().next() {
Some(Prefix(prefix)) if prefix.kind().is_verbatim() => Ok(path.components().collect()),
_ => Ok(path),
}
}
}

View File

@ -259,7 +259,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
})
.collect();
let args = tcx.mk_args(&args);
let span = i.bottom().1;
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
@ -292,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.emit();
}
ty::ExistentialTraitRef { def_id: trait_ref.def_id, args }
ty::ExistentialTraitRef::new(tcx, trait_ref.def_id, args)
})
});

View File

@ -10,7 +10,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::unord::UnordMap;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, ErrorGuaranteed, StashKey, Subdiagnostic, pluralize, struct_span_code_err,
Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, pluralize,
struct_span_code_err,
};
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
@ -2763,12 +2764,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
field_ident.span,
"field not available in `impl Future`, but it is available in its `Output`",
);
err.span_suggestion_verbose(
base.span.shrink_to_hi(),
"consider `await`ing on the `Future` and access the field of its `Output`",
".await",
Applicability::MaybeIncorrect,
);
match self.tcx.coroutine_kind(self.body_id) {
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
err.span_suggestion_verbose(
base.span.shrink_to_hi(),
"consider `await`ing on the `Future` to access the field",
".await",
Applicability::MaybeIncorrect,
);
}
_ => {
let mut span: MultiSpan = base.span.into();
span.push_span_label(self.tcx.def_span(self.body_id), "this is not `async`");
err.span_note(
span,
"this implements `Future` and its output type has the field, \
but the future cannot be awaited in a synchronous function",
);
}
}
}
fn ban_nonexisting_field(

View File

@ -109,6 +109,7 @@ impl<'tcx> Const<'tcx> {
#[inline]
pub fn new_unevaluated(tcx: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Const<'tcx> {
tcx.debug_assert_args_compatible(uv.def, uv.args);
Const::new(tcx, ty::ConstKind::Unevaluated(uv))
}

View File

@ -279,6 +279,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.debug_assert_args_compatible(def_id, args);
}
/// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection`
/// are compatible with the `DefId`. Since we're missing a `Self` type, stick on
/// a dummy self type and forward to `debug_assert_args_compatible`.
fn debug_assert_existential_args_compatible(
self,
def_id: Self::DefId,
args: Self::GenericArgs,
) {
// FIXME: We could perhaps add a `skip: usize` to `debug_assert_args_compatible`
// to avoid needing to reintern the set of args...
if cfg!(debug_assertions) {
self.debug_assert_args_compatible(
def_id,
self.mk_args_from_iter(
[self.types.trait_object_dummy_self.into()].into_iter().chain(args.iter()),
),
);
}
}
fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
where
I: Iterator<Item = T>,

View File

@ -21,6 +21,10 @@ pub(crate) struct CoverageGraph {
pub(crate) successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
pub(crate) predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
dominators: Option<Dominators<BasicCoverageBlock>>,
/// Allows nodes to be compared in some total order such that _if_
/// `a` dominates `b`, then `a < b`. If neither node dominates the other,
/// their relative order is consistent but arbitrary.
dominator_order_rank: IndexVec<BasicCoverageBlock, u32>,
}
impl CoverageGraph {
@ -54,10 +58,27 @@ impl CoverageGraph {
}
}
let mut this = Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None };
let num_nodes = bcbs.len();
let mut this = Self {
bcbs,
bb_to_bcb,
successors,
predecessors,
dominators: None,
dominator_order_rank: IndexVec::from_elem_n(0, num_nodes),
};
assert_eq!(num_nodes, this.num_nodes());
this.dominators = Some(dominators::dominators(&this));
// The dominator rank of each node is just its index in a reverse-postorder traversal.
let reverse_post_order = graph::iterate::reverse_post_order(&this, this.start_node());
// The coverage graph is created by traversal, so all nodes are reachable.
assert_eq!(reverse_post_order.len(), this.num_nodes());
for (rank, bcb) in (0u32..).zip(reverse_post_order) {
this.dominator_order_rank[bcb] = rank;
}
// The coverage graph's entry-point node (bcb0) always starts with bb0,
// which never has predecessors. Any other blocks merged into bcb0 can't
// have multiple (coverage-relevant) predecessors, so bcb0 always has
@ -162,7 +183,7 @@ impl CoverageGraph {
a: BasicCoverageBlock,
b: BasicCoverageBlock,
) -> Ordering {
self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
self.dominator_order_rank[a].cmp(&self.dominator_order_rank[b])
}
/// Returns the source of this node's sole in-edge, if it has exactly one.

View File

@ -245,6 +245,19 @@ passes_doc_test_unknown_include =
unknown `doc` attribute `{$path}`
.suggestion = use `doc = include_str!` instead
passes_doc_test_unknown_passes =
unknown `doc` attribute `{$path}`
.note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
.label = no longer functions
.help = you may want to use `doc(document_private_items)`
.no_op_note = `doc({$path})` is now a no-op
passes_doc_test_unknown_plugins =
unknown `doc` attribute `{$path}`
.note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
.label = no longer functions
.no_op_note = `doc({$path})` is now a no-op
passes_doc_test_unknown_spotlight =
unknown `doc` attribute `{$path}`
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`

View File

@ -1183,15 +1183,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
sym::masked => self.check_doc_masked(attr, meta, hir_id, target),
// no_default_passes: deprecated
// passes: deprecated
// plugins: removed, but rustdoc warns about it itself
sym::cfg
| sym::hidden
| sym::no_default_passes
| sym::notable_trait
| sym::passes
| sym::plugins => {}
sym::cfg | sym::hidden | sym::notable_trait => {}
sym::rust_logo => {
if self.check_attr_crate_level(attr, meta, hir_id)
@ -1240,6 +1232,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
sugg: (attr.meta().unwrap().span, applicability),
},
);
} else if i_meta.has_name(sym::passes)
|| i_meta.has_name(sym::no_default_passes)
{
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
i_meta.span,
errors::DocTestUnknownPasses { path, span: i_meta.span },
);
} else if i_meta.has_name(sym::plugins) {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
i_meta.span,
errors::DocTestUnknownPlugins { path, span: i_meta.span },
);
} else {
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,

View File

@ -323,6 +323,27 @@ pub(crate) struct DocTestUnknownSpotlight {
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_test_unknown_passes)]
#[note]
#[help]
#[note(passes_no_op_note)]
pub(crate) struct DocTestUnknownPasses {
pub path: String,
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_test_unknown_plugins)]
#[note]
#[note(passes_no_op_note)]
pub(crate) struct DocTestUnknownPlugins {
pub path: String,
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_doc_test_unknown_include)]
pub(crate) struct DocTestUnknownInclude {

View File

@ -322,7 +322,7 @@ impl<'tcx> ReachableContext<'tcx> {
self.visit(ty);
// Manually visit to actually see the trait's `DefId`. Type visitors won't see it
if let Some(trait_ref) = dyn_ty.principal() {
let ExistentialTraitRef { def_id, args } = trait_ref.skip_binder();
let ExistentialTraitRef { def_id, args, .. } = trait_ref.skip_binder();
self.visit_def_id(def_id, "", &"");
self.visit(args);
}

View File

@ -32,8 +32,8 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility,
use rustc_middle::query::Providers;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{
self, Const, GenericArgs, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitor,
self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitor,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
@ -246,10 +246,10 @@ where
ty::ExistentialPredicate::Trait(trait_ref) => trait_ref,
ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx),
ty::ExistentialPredicate::AutoTrait(def_id) => {
ty::ExistentialTraitRef { def_id, args: GenericArgs::empty() }
ty::ExistentialTraitRef::new(tcx, def_id, ty::GenericArgs::empty())
}
};
let ty::ExistentialTraitRef { def_id, args: _ } = trait_ref;
let ty::ExistentialTraitRef { def_id, .. } = trait_ref;
try_visit!(self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref));
}
}

View File

@ -245,11 +245,15 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
alias_ty.to_ty(tcx),
);
debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx));
ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
def_id: assoc_ty.def_id,
args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args,
term: resolved.into(),
})
ty::ExistentialPredicate::Projection(
ty::ExistentialProjection::erase_self_ty(
tcx,
ty::ProjectionPredicate {
projection_term: alias_ty.into(),
term: resolved.into(),
},
),
)
})
})
})
@ -318,10 +322,11 @@ pub(crate) fn transform_instance<'tcx>(
.lang_items()
.drop_trait()
.unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef {
let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(
tcx,
def_id,
args: List::empty(),
});
ty::List::empty(),
));
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
instance.args = tcx.mk_args_trait(self_ty, List::empty());

View File

@ -380,11 +380,12 @@ impl RustcInternal for ExistentialProjection {
type T<'tcx> = rustc_ty::ExistentialProjection<'tcx>;
fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
rustc_ty::ExistentialProjection {
def_id: self.def_id.0.internal(tables, tcx),
args: self.generic_args.internal(tables, tcx),
term: self.term.internal(tables, tcx),
}
rustc_ty::ExistentialProjection::new_from_args(
tcx,
self.def_id.0.internal(tables, tcx),
self.generic_args.internal(tables, tcx),
self.term.internal(tables, tcx),
)
}
}
@ -403,10 +404,11 @@ impl RustcInternal for ExistentialTraitRef {
type T<'tcx> = rustc_ty::ExistentialTraitRef<'tcx>;
fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
rustc_ty::ExistentialTraitRef {
def_id: self.def_id.0.internal(tables, tcx),
args: self.generic_args.internal(tables, tcx),
}
rustc_ty::ExistentialTraitRef::new_from_args(
tcx,
self.def_id.0.internal(tables, tcx),
self.generic_args.internal(tables, tcx),
)
}
}

View File

@ -68,7 +68,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> {
type T = stable_mir::ty::ExistentialTraitRef;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
let ty::ExistentialTraitRef { def_id, args } = self;
let ty::ExistentialTraitRef { def_id, args, .. } = self;
stable_mir::ty::ExistentialTraitRef {
def_id: tables.trait_def(*def_id),
generic_args: args.stable(tables),
@ -95,7 +95,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> {
type T = stable_mir::ty::ExistentialProjection;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
let ty::ExistentialProjection { def_id, args, term } = self;
let ty::ExistentialProjection { def_id, args, term, .. } = self;
stable_mir::ty::ExistentialProjection {
def_id: tables.trait_def(*def_id),
generic_args: args.stable(tables),

View File

@ -3594,52 +3594,64 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
span: Span,
) {
if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
self.tcx.coroutine_kind(obligation.cause.body_id)
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
let impls_future = self.type_implements_trait(
future_trait,
[self.tcx.instantiate_bound_regions_with_erased(self_ty)],
obligation.param_env,
);
if !impls_future.must_apply_modulo_regions() {
return;
}
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
// `<T as Future>::Output`
let projection_ty = trait_pred.map_bound(|trait_pred| {
Ty::new_projection(
self.tcx,
item_def_id,
// Future::Output has no args
[trait_pred.self_ty()],
)
});
let InferOk { value: projection_ty, .. } =
self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
debug!(
normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
);
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
);
debug!(try_trait_obligation = ?try_obligation);
if self.predicate_may_hold(&try_obligation)
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
&& snippet.ends_with('?')
{
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
let impls_future = self.type_implements_trait(
future_trait,
[self.tcx.instantiate_bound_regions_with_erased(self_ty)],
obligation.param_env,
);
if !impls_future.must_apply_modulo_regions() {
return;
}
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
// `<T as Future>::Output`
let projection_ty = trait_pred.map_bound(|trait_pred| {
Ty::new_projection(
self.tcx,
item_def_id,
// Future::Output has no args
[trait_pred.self_ty()],
)
});
let InferOk { value: projection_ty, .. } =
self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
debug!(
normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
);
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
);
debug!(try_trait_obligation = ?try_obligation);
if self.predicate_may_hold(&try_obligation)
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
&& snippet.ends_with('?')
{
err.span_suggestion_verbose(
span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
"consider `await`ing on the `Future`",
".await",
Applicability::MaybeIncorrect,
);
match self.tcx.coroutine_kind(obligation.cause.body_id) {
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
err.span_suggestion_verbose(
span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
"consider `await`ing on the `Future`",
".await",
Applicability::MaybeIncorrect,
);
}
_ => {
let mut span: MultiSpan = span.with_lo(span.hi() - BytePos(1)).into();
span.push_span_label(
self.tcx.def_span(obligation.cause.body_id),
"this is not `async`",
);
err.span_note(
span,
"this implements `Future` and its output type supports \
`?`, but the future cannot be awaited in a synchronous function",
);
}
}
}
}

View File

@ -15,9 +15,7 @@ use crate::solve::{
CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode,
};
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
use crate::{
search_graph, {self as ty},
};
use crate::{self as ty, search_graph};
pub trait Interner:
Sized
@ -173,6 +171,10 @@ pub trait Interner:
fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs);
/// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection`
/// are compatible with the `DefId`.
fn debug_assert_existential_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs);
fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
where
I: Iterator<Item = T>,

View File

@ -289,9 +289,26 @@ impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> {
pub struct ExistentialTraitRef<I: Interner> {
pub def_id: I::DefId,
pub args: I::GenericArgs,
/// This field exists to prevent the creation of `ExistentialTraitRef` without
/// calling [`ExistentialTraitRef::new_from_args`].
_use_existential_trait_ref_new_instead: (),
}
impl<I: Interner> ExistentialTraitRef<I> {
pub fn new_from_args(interner: I, trait_def_id: I::DefId, args: I::GenericArgs) -> Self {
interner.debug_assert_existential_args_compatible(trait_def_id, args);
Self { def_id: trait_def_id, args, _use_existential_trait_ref_new_instead: () }
}
pub fn new(
interner: I,
trait_def_id: I::DefId,
args: impl IntoIterator<Item: Into<I::GenericArg>>,
) -> Self {
let args = interner.mk_args_from_iter(args.into_iter().map(Into::into));
Self::new_from_args(interner, trait_def_id, args)
}
pub fn erase_self_ty(interner: I, trait_ref: TraitRef<I>) -> ExistentialTraitRef<I> {
// Assert there is a Self.
trait_ref.args.type_at(0);
@ -299,6 +316,7 @@ impl<I: Interner> ExistentialTraitRef<I> {
ExistentialTraitRef {
def_id: trait_ref.def_id,
args: interner.mk_args(&trait_ref.args.as_slice()[1..]),
_use_existential_trait_ref_new_instead: (),
}
}
@ -336,9 +354,33 @@ pub struct ExistentialProjection<I: Interner> {
pub def_id: I::DefId,
pub args: I::GenericArgs,
pub term: I::Term,
/// This field exists to prevent the creation of `ExistentialProjection`
/// without using [`ExistentialProjection::new_from_args`].
use_existential_projection_new_instead: (),
}
impl<I: Interner> ExistentialProjection<I> {
pub fn new_from_args(
interner: I,
def_id: I::DefId,
args: I::GenericArgs,
term: I::Term,
) -> ExistentialProjection<I> {
interner.debug_assert_existential_args_compatible(def_id, args);
Self { def_id, args, term, use_existential_projection_new_instead: () }
}
pub fn new(
interner: I,
def_id: I::DefId,
args: impl IntoIterator<Item: Into<I::GenericArg>>,
term: I::Term,
) -> ExistentialProjection<I> {
let args = interner.mk_args_from_iter(args.into_iter().map(Into::into));
Self::new_from_args(interner, def_id, args, term)
}
/// Extracts the underlying existential trait reference from this projection.
/// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`,
/// then this function would return an `exists T. T: Iterator` existential trait
@ -347,7 +389,7 @@ impl<I: Interner> ExistentialProjection<I> {
let def_id = interner.parent(self.def_id);
let args_count = interner.generics_of(def_id).count() - 1;
let args = interner.mk_args(&self.args.as_slice()[..args_count]);
ExistentialTraitRef { def_id, args }
ExistentialTraitRef { def_id, args, _use_existential_trait_ref_new_instead: () }
}
pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate<I> {
@ -372,6 +414,7 @@ impl<I: Interner> ExistentialProjection<I> {
def_id: projection_predicate.projection_term.def_id,
args: interner.mk_args(&projection_predicate.projection_term.args.as_slice()[1..]),
term: projection_predicate.term,
use_existential_projection_new_instead: (),
}
}
}

View File

@ -333,7 +333,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
a.args,
b.args,
)?;
Ok(ty::ExistentialProjection { def_id: a.def_id, args, term })
Ok(ty::ExistentialProjection::new_from_args(relation.cx(), a.def_id, args, term))
}
}
}
@ -373,7 +373,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialTraitRef<I> {
}))
} else {
let args = relate_args_invariantly(relation, a.args, b.args)?;
Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
Ok(ty::ExistentialTraitRef::new_from_args(relation.cx(), a.def_id, args))
}
}
}

View File

@ -1734,13 +1734,15 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
} else {
// We need to properly build cargo using the suitable stage compiler.
// HACK: currently tool stages are off-by-one compared to compiler stages, i.e. if
// you give `tool::Cargo` a stage 1 rustc, it will cause stage 2 rustc to be built
// and produce a cargo built with stage 2 rustc. To fix this, we need to chop off
// the compiler stage by 1 to align with expected `./x test run-make --stage N`
// behavior, i.e. we need to pass `N - 1` compiler stage to cargo. See also Miri
// which does a similar hack.
let compiler = builder.compiler(builder.top_stage - 1, compiler.host);
let compiler = builder.download_rustc().then_some(compiler).unwrap_or_else(||
// HACK: currently tool stages are off-by-one compared to compiler stages, i.e. if
// you give `tool::Cargo` a stage 1 rustc, it will cause stage 2 rustc to be built
// and produce a cargo built with stage 2 rustc. To fix this, we need to chop off
// the compiler stage by 1 to align with expected `./x test run-make --stage N`
// behavior, i.e. we need to pass `N - 1` compiler stage to cargo. See also Miri
// which does a similar hack.
builder.compiler(builder.top_stage - 1, compiler.host));
builder.ensure(tool::Cargo { compiler, target: compiler.host })
};
@ -3549,9 +3551,7 @@ impl Step for TestFloatParse {
let path = self.path.to_str().unwrap();
let crate_name = self.path.components().last().unwrap().as_os_str().to_str().unwrap();
if !builder.download_rustc() {
builder.ensure(compile::Std::new(compiler, self.host));
}
builder.ensure(tool::TestFloatParse { host: self.host });
// Run any unit tests in the crate
let cargo_test = tool::prepare_tool_cargo(

View File

@ -1097,6 +1097,38 @@ tool_extended!((self, builder),
Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"];
);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TestFloatParse {
pub host: TargetSelection,
}
impl Step for TestFloatParse {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/etc/test-float-parse")
}
fn run(self, builder: &Builder<'_>) {
let bootstrap_host = builder.config.build;
let compiler = builder.compiler(builder.top_stage, bootstrap_host);
builder.ensure(ToolBuild {
compiler,
target: bootstrap_host,
tool: "test-float-parse",
mode: Mode::ToolStd,
path: "src/etc/test-float-parse",
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: "",
cargo_args: Vec::new(),
});
}
}
impl Builder<'_> {
/// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
/// `host`.

View File

@ -8,7 +8,7 @@ use rustc_data_structures::unord::UnordSet;
use rustc_errors::codes::*;
use rustc_errors::emitter::{DynEmitter, HumanEmitter, stderr_destination};
use rustc_errors::json::JsonEmitter;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, TerminalUrl};
use rustc_errors::{ErrorGuaranteed, TerminalUrl};
use rustc_feature::UnstableFeatures;
use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
@ -21,8 +21,8 @@ use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_session::config::{self, CrateType, ErrorOutputType, Input, ResolveDocLinks};
pub(crate) use rustc_session::config::{Options, UnstableOptions};
use rustc_session::{Session, lint};
use rustc_span::source_map;
use rustc_span::symbol::sym;
use rustc_span::{Span, source_map};
use tracing::{debug, info};
use crate::clean::inline::build_external_trait;
@ -381,45 +381,10 @@ pub(crate) fn run_global_ctxt(
);
}
fn report_deprecated_attr(name: &str, dcx: DiagCtxtHandle<'_>, sp: Span) {
let mut msg =
dcx.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated"));
msg.note(
"see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
for more information",
);
if name == "no_default_passes" {
msg.help("`#![doc(no_default_passes)]` no longer functions; you may want to use `#![doc(document_private_items)]`");
} else if name.starts_with("passes") {
msg.help("`#![doc(passes = \"...\")]` no longer functions; you may want to use `#![doc(document_private_items)]`");
} else if name.starts_with("plugins") {
msg.warn("`#![doc(plugins = \"...\")]` no longer functions; see CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>");
}
msg.emit();
}
// Process all of the crate attributes, extracting plugin metadata along
// with the passes which we are supposed to run.
for attr in krate.module.attrs.lists(sym::doc) {
let dcx = ctxt.sess().dcx();
let name = attr.name_or_empty();
// `plugins = "..."`, `no_default_passes`, and `passes = "..."` have no effect
if attr.is_word() && name == sym::no_default_passes {
report_deprecated_attr("no_default_passes", dcx, attr.span());
} else if attr.value_str().is_some() {
match name {
sym::passes => {
report_deprecated_attr("passes = \"...\"", dcx, attr.span());
}
sym::plugins => {
report_deprecated_attr("plugins = \"...\"", dcx, attr.span());
}
_ => (),
}
}
if attr.is_word() && name == sym::document_private_items {
ctxt.render_options.document_private = true;

View File

@ -57,7 +57,7 @@ impl EarlyProps {
&mut poisoned,
testfile,
rdr,
&mut |DirectiveLine { directive: ln, .. }| {
&mut |DirectiveLine { raw_directive: ln, .. }| {
parse_and_update_aux(config, ln, &mut props.aux);
config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
},
@ -344,8 +344,8 @@ impl TestProps {
&mut poisoned,
testfile,
file,
&mut |DirectiveLine { header_revision, directive: ln, .. }| {
if header_revision.is_some() && header_revision != test_revision {
&mut |directive @ DirectiveLine { raw_directive: ln, .. }| {
if !directive.applies_to_test_revision(test_revision) {
return;
}
@ -678,28 +678,35 @@ impl TestProps {
}
}
/// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present.
///
/// See [`DirectiveLine`] for a diagram.
pub fn line_directive<'line>(
/// If the given line begins with the appropriate comment prefix for a directive,
/// returns a struct containing various parts of the directive.
fn line_directive<'line>(
line_number: usize,
comment: &str,
original_line: &'line str,
) -> Option<(Option<&'line str>, &'line str)> {
) -> Option<DirectiveLine<'line>> {
// Ignore lines that don't start with the comment prefix.
let after_comment = original_line.trim_start().strip_prefix(comment)?.trim_start();
let revision;
let raw_directive;
if let Some(after_open_bracket) = after_comment.strip_prefix('[') {
// A comment like `//@[foo]` only applies to revision `foo`.
let Some((line_revision, directive)) = after_open_bracket.split_once(']') else {
let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else {
panic!(
"malformed condition directive: expected `{comment}[foo]`, found `{original_line}`"
)
};
Some((Some(line_revision), directive.trim_start()))
revision = Some(line_revision);
raw_directive = after_close_bracket.trim_start();
} else {
Some((None, after_comment))
}
revision = None;
raw_directive = after_comment;
};
Some(DirectiveLine { line_number, revision, raw_directive })
}
// To prevent duplicating the list of commmands between `compiletest`,`htmldocck` and `jsondocck`,
@ -730,28 +737,37 @@ const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[
const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] =
&["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"];
/// The broken-down contents of a line containing a test header directive,
/// The (partly) broken-down contents of a line containing a test directive,
/// which [`iter_header`] passes to its callback function.
///
/// For example:
///
/// ```text
/// //@ compile-flags: -O
/// ^^^^^^^^^^^^^^^^^ directive
/// ^^^^^^^^^^^^^^^^^ raw_directive
///
/// //@ [foo] compile-flags: -O
/// ^^^ header_revision
/// ^^^^^^^^^^^^^^^^^ directive
/// ^^^ revision
/// ^^^^^^^^^^^^^^^^^ raw_directive
/// ```
struct DirectiveLine<'ln> {
line_number: usize,
/// Some header directives start with a revision name in square brackets
/// Some test directives start with a revision name in square brackets
/// (e.g. `[foo]`), and only apply to that revision of the test.
/// If present, this field contains the revision name (e.g. `foo`).
header_revision: Option<&'ln str>,
/// The main part of the header directive, after removing the comment prefix
revision: Option<&'ln str>,
/// The main part of the directive, after removing the comment prefix
/// and the optional revision specifier.
directive: &'ln str,
///
/// This is "raw" because the directive's name and colon-separated value
/// (if present) have not yet been extracted or checked.
raw_directive: &'ln str,
}
impl<'ln> DirectiveLine<'ln> {
fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool {
self.revision.is_none() || self.revision == test_revision
}
}
pub(crate) struct CheckDirectiveResult<'ln> {
@ -819,8 +835,8 @@ fn iter_header(
"ignore-cross-compile",
];
// Process the extra implied directives, with a dummy line number of 0.
for directive in extra_directives {
it(DirectiveLine { line_number: 0, header_revision: None, directive });
for raw_directive in extra_directives {
it(DirectiveLine { line_number: 0, revision: None, raw_directive });
}
}
@ -847,24 +863,21 @@ fn iter_header(
return;
}
let Some((header_revision, non_revisioned_directive_line)) = line_directive(comment, ln)
else {
let Some(directive_line) = line_directive(line_number, comment, ln) else {
continue;
};
// Perform unknown directive check on Rust files.
if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
let directive_ln = non_revisioned_directive_line.trim();
let CheckDirectiveResult { is_known_directive, trailing_directive } =
check_directive(directive_ln, mode, ln);
check_directive(directive_line.raw_directive, mode, ln);
if !is_known_directive {
*poisoned = true;
eprintln!(
"error: detected unknown compiletest test directive `{}` in {}:{}",
directive_ln,
directive_line.raw_directive,
testfile.display(),
line_number,
);
@ -888,11 +901,7 @@ fn iter_header(
}
}
it(DirectiveLine {
line_number,
header_revision,
directive: non_revisioned_directive_line,
});
it(directive_line);
}
}
@ -1292,8 +1301,8 @@ pub fn make_test_description<R: Read>(
&mut local_poisoned,
path,
src,
&mut |DirectiveLine { header_revision, directive: ln, line_number }| {
if header_revision.is_some() && header_revision != test_revision {
&mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
if !directive.applies_to_test_revision(test_revision) {
return;
}

View File

@ -4,7 +4,6 @@ use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use crate::common::Config;
use crate::header::line_directive;
use crate::runtest::ProcRes;
/// Representation of information to invoke a debugger and check its output
@ -24,7 +23,6 @@ impl DebuggerCommands {
file: &Path,
config: &Config,
debugger_prefixes: &[&str],
rev: Option<&str>,
) -> Result<Self, String> {
let directives = debugger_prefixes
.iter()
@ -39,17 +37,16 @@ impl DebuggerCommands {
for (line_no, line) in reader.lines().enumerate() {
counter += 1;
let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?;
let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line));
// Skip any revision specific directive that doesn't match the current
// revision being tested
if lnrev.is_some() && lnrev != rev {
// Breakpoints appear on lines with actual code, typically at the end of the line.
if line.contains("#break") {
breakpoint_lines.push(counter);
continue;
}
if line.contains("#break") {
breakpoint_lines.push(counter);
}
let Some(line) = line.trim_start().strip_prefix("//").map(str::trim_start) else {
continue;
};
for &(ref command_directive, ref check_directive) in &directives {
config

View File

@ -66,13 +66,8 @@ impl TestCx<'_> {
};
// Parse debugger commands etc from test files
let dbg_cmds = DebuggerCommands::parse_from(
&self.testpaths.file,
self.config,
prefixes,
self.revision,
)
.unwrap_or_else(|e| self.fatal(&e));
let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, prefixes)
.unwrap_or_else(|e| self.fatal(&e));
// https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
let mut script_str = String::with_capacity(2048);
@ -142,13 +137,8 @@ impl TestCx<'_> {
}
fn run_debuginfo_gdb_test_no_opt(&self) {
let dbg_cmds = DebuggerCommands::parse_from(
&self.testpaths.file,
self.config,
&["gdb"],
self.revision,
)
.unwrap_or_else(|e| self.fatal(&e));
let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, &["gdb"])
.unwrap_or_else(|e| self.fatal(&e));
let mut cmds = dbg_cmds.commands.join("\n");
// compile test file (it should have 'compile-flags:-g' in the header)
@ -413,13 +403,8 @@ impl TestCx<'_> {
}
// Parse debugger commands etc from test files
let dbg_cmds = DebuggerCommands::parse_from(
&self.testpaths.file,
self.config,
&["lldb"],
self.revision,
)
.unwrap_or_else(|e| self.fatal(&e));
let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, &["lldb"])
.unwrap_or_else(|e| self.fatal(&e));
// Write debugger script:
// We don't want to hang when calling `quit` while the process is still running

View File

@ -0,0 +1 @@
static TEST: &str = "Hello World!";

View File

@ -0,0 +1,8 @@
//@ only-windows other platforms do not have Windows verbatim paths
use run_make_support::rustc;
fn main() {
// Canonicalizing the path ensures that it's verbatim (i.e. starts with `\\?\`)
let mut path = std::fs::canonicalize(file!()).unwrap();
path.pop();
rustc().input("verbatim.rs").env("VERBATIM_DIR", path).run();
}

View File

@ -0,0 +1,12 @@
//! Include a file by concating the verbatim path using `/` instead of `\`
include!(concat!(env!("VERBATIM_DIR"), "/include/include.txt"));
fn main() {
assert_eq!(TEST, "Hello World!");
let s = include_str!(concat!(env!("VERBATIM_DIR"), "/include/include.txt"));
assert_eq!(s, "static TEST: &str = \"Hello World!\";\n");
let b = include_bytes!(concat!(env!("VERBATIM_DIR"), "/include/include.txt"));
assert_eq!(b, b"static TEST: &str = \"Hello World!\";\n");
}

View File

@ -1,16 +1,21 @@
//@ check-pass
//@ compile-flags: --passes unknown-pass
//@ error-pattern: the `passes` flag no longer functions
#![doc(no_default_passes)]
//~^ WARNING attribute is deprecated
//~^ ERROR unknown `doc` attribute `no_default_passes`
//~| NOTE no longer functions
//~| NOTE see issue #44136
//~| HELP no longer functions; you may want to use `#![doc(document_private_items)]`
//~| HELP you may want to use `doc(document_private_items)`
//~| NOTE `doc(no_default_passes)` is now a no-op
//~| NOTE `#[deny(invalid_doc_attributes)]` on by default
#![doc(passes = "collapse-docs unindent-comments")]
//~^ WARNING attribute is deprecated
//~^ ERROR unknown `doc` attribute `passes`
//~| NOTE no longer functions
//~| NOTE see issue #44136
//~| HELP no longer functions; you may want to use `#![doc(document_private_items)]`
//~| HELP you may want to use `doc(document_private_items)`
//~| NOTE `doc(passes)` is now a no-op
#![doc(plugins = "xxx")]
//~^ WARNING attribute is deprecated
//~^ ERROR unknown `doc` attribute `plugins`
//~| NOTE see issue #44136
//~| WARNING no longer functions; see CVE
//~| NOTE no longer functions
//~| NOTE `doc(plugins)` is now a no-op

View File

@ -3,32 +3,35 @@ warning: the `passes` flag no longer functions
= note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
= help: you may want to use --document-private-items
warning: the `#![doc(no_default_passes)]` attribute is deprecated
--> $DIR/deprecated-attrs.rs:5:8
error: unknown `doc` attribute `no_default_passes`
--> $DIR/deprecated-attrs.rs:4:8
|
LL | #![doc(no_default_passes)]
| ^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^ no longer functions
|
= note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
= help: `#![doc(no_default_passes)]` no longer functions; you may want to use `#![doc(document_private_items)]`
= note: `doc` attribute `no_default_passes` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
= help: you may want to use `doc(document_private_items)`
= note: `doc(no_default_passes)` is now a no-op
= note: `#[deny(invalid_doc_attributes)]` on by default
warning: the `#![doc(passes = "...")]` attribute is deprecated
--> $DIR/deprecated-attrs.rs:9:8
error: unknown `doc` attribute `passes`
--> $DIR/deprecated-attrs.rs:11:8
|
LL | #![doc(passes = "collapse-docs unindent-comments")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no longer functions
|
= note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
= help: `#![doc(passes = "...")]` no longer functions; you may want to use `#![doc(document_private_items)]`
= note: `doc` attribute `passes` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
= help: you may want to use `doc(document_private_items)`
= note: `doc(passes)` is now a no-op
warning: the `#![doc(plugins = "...")]` attribute is deprecated
--> $DIR/deprecated-attrs.rs:13:8
error: unknown `doc` attribute `plugins`
--> $DIR/deprecated-attrs.rs:17:8
|
LL | #![doc(plugins = "xxx")]
| ^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^ no longer functions
|
= note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
= warning: `#![doc(plugins = "...")]` no longer functions; see CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
= note: `doc` attribute `plugins` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
= note: `doc(plugins)` is now a no-op
warning: 3 warnings emitted
error: aborting due to 3 previous errors

View File

@ -0,0 +1,13 @@
//@ edition: 2021
struct S {
field: (),
}
async fn foo() -> S { todo!() }
fn main() -> Result<(), ()> {
foo().field;
//~^ ERROR no field `field` on type `impl Future<Output = S>`
Ok(())
}

View File

@ -0,0 +1,17 @@
error[E0609]: no field `field` on type `impl Future<Output = S>`
--> $DIR/field-in-sync.rs:10:11
|
LL | foo().field;
| ^^^^^ field not available in `impl Future`, but it is available in its `Output`
|
note: this implements `Future` and its output type has the field, but the future cannot be awaited in a synchronous function
--> $DIR/field-in-sync.rs:10:5
|
LL | fn main() -> Result<(), ()> {
| --------------------------- this is not `async`
LL | foo().field;
| ^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0609`.

View File

@ -28,7 +28,7 @@ error[E0609]: no field `0` on type `impl Future<Output = Tuple>`
LL | let _: i32 = tuple().0;
| ^ field not available in `impl Future`, but it is available in its `Output`
|
help: consider `await`ing on the `Future` and access the field of its `Output`
help: consider `await`ing on the `Future` to access the field
|
LL | let _: i32 = tuple().await.0;
| ++++++
@ -39,7 +39,7 @@ error[E0609]: no field `a` on type `impl Future<Output = Struct>`
LL | let _: i32 = struct_().a;
| ^ field not available in `impl Future`, but it is available in its `Output`
|
help: consider `await`ing on the `Future` and access the field of its `Output`
help: consider `await`ing on the `Future` to access the field
|
LL | let _: i32 = struct_().await.a;
| ++++++

View File

@ -0,0 +1,9 @@
//@ edition: 2021
async fn foo() -> Result<(), ()> { todo!() }
fn main() -> Result<(), ()> {
foo()?;
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
Ok(())
}

View File

@ -0,0 +1,18 @@
error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> $DIR/try-in-sync.rs:6:5
|
LL | foo()?;
| ^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), ()>>`
|
= help: the trait `Try` is not implemented for `impl Future<Output = Result<(), ()>>`
note: this implements `Future` and its output type supports `?`, but the future cannot be awaited in a synchronous function
--> $DIR/try-in-sync.rs:6:10
|
LL | fn main() -> Result<(), ()> {
| --------------------------- this is not `async`
LL | foo()?;
| ^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.