Auto merge of #105746 - matthiaskrgr:rollup-sz3grbv, r=matthiaskrgr
Rollup of 11 pull requests Successful merges: - #104592 (Ensure async trait impls are async (or otherwise return an opaque type)) - #105623 (Fix `-Z print-type-sizes` for generators with discriminant field ordered first) - #105627 (Auto traits in `dyn Trait + Auto` are suggestable) - #105633 (Make `report_projection_error` more `Term` agnostic) - #105683 (Various cleanups to dest prop) - #105692 (Add regression test for #104678) - #105707 (rustdoc: remove unnecessary CSS `kbd { cursor: default }`) - #105715 (Do not mention long types in E0599 label) - #105722 (more clippy::complexity fixes) - #105724 (rustdoc: remove no-op CSS `.scrape-example .src-line-numbers { margin: 0 }`) - #105730 (rustdoc: remove no-op CSS `.item-info:before { color }`) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
984eab57f7
61 changed files with 509 additions and 325 deletions
|
@ -2466,7 +2466,7 @@ pub enum ModKind {
|
|||
Unloaded,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default)]
|
||||
pub struct ModSpans {
|
||||
/// `inner_span` covers the body of the module; for a file module, its the whole file.
|
||||
/// For an inline module, its the span inside the `{ ... }`, not including the curly braces.
|
||||
|
@ -2474,12 +2474,6 @@ pub struct ModSpans {
|
|||
pub inject_use_span: Span,
|
||||
}
|
||||
|
||||
impl Default for ModSpans {
|
||||
fn default() -> ModSpans {
|
||||
ModSpans { inner_span: Default::default(), inject_use_span: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Foreign module declaration.
|
||||
///
|
||||
/// E.g., `extern { .. }` or `extern "C" { .. }`.
|
||||
|
|
|
@ -126,13 +126,13 @@ impl<K: Ord, V> SortedMap<K, V> {
|
|||
/// Iterate over the keys, sorted
|
||||
#[inline]
|
||||
pub fn keys(&self) -> impl Iterator<Item = &K> + ExactSizeIterator + DoubleEndedIterator {
|
||||
self.data.iter().map(|&(ref k, _)| k)
|
||||
self.data.iter().map(|(k, _)| k)
|
||||
}
|
||||
|
||||
/// Iterate over values, sorted by key
|
||||
#[inline]
|
||||
pub fn values(&self) -> impl Iterator<Item = &V> + ExactSizeIterator + DoubleEndedIterator {
|
||||
self.data.iter().map(|&(_, ref v)| v)
|
||||
self.data.iter().map(|(_, v)| v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -222,7 +222,7 @@ impl<K: Ord, V> SortedMap<K, V> {
|
|||
K: Borrow<Q>,
|
||||
Q: Ord + ?Sized,
|
||||
{
|
||||
self.data.binary_search_by(|&(ref x, _)| x.borrow().cmp(key))
|
||||
self.data.binary_search_by(|(x, _)| x.borrow().cmp(key))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -300,7 +300,7 @@ impl<K: Ord, V> FromIterator<(K, V)> for SortedMap<K, V> {
|
|||
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
|
||||
let mut data: Vec<(K, V)> = iter.into_iter().collect();
|
||||
|
||||
data.sort_unstable_by(|&(ref k1, _), &(ref k2, _)| k1.cmp(k2));
|
||||
data.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
||||
data.dedup_by(|&mut (ref k1, _), &mut (ref k2, _)| k1.cmp(k2) == Ordering::Equal);
|
||||
|
||||
SortedMap { data }
|
||||
|
|
|
@ -20,6 +20,10 @@ hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
|
|||
.where_label = this `where` clause might not match the one in the trait
|
||||
.bounds_label = this bound might be missing in the impl
|
||||
|
||||
hir_analysis_async_trait_impl_should_be_async =
|
||||
method `{$method_name}` should be async because the method from the trait is async
|
||||
.trait_item_label = required because the trait method is async
|
||||
|
||||
hir_analysis_drop_impl_on_wrong_item =
|
||||
the `Drop` trait may only be implemented for local structs, enums, and unions
|
||||
.label = must be a struct, enum, or union in the current crate
|
||||
|
|
|
@ -2313,7 +2313,7 @@ impl FileWithAnnotatedLines {
|
|||
}
|
||||
|
||||
// Find overlapping multiline annotations, put them at different depths
|
||||
multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, usize::MAX - ml.line_end));
|
||||
multiline_annotations.sort_by_key(|(_, ml)| (ml.line_start, usize::MAX - ml.line_end));
|
||||
for (_, ann) in multiline_annotations.clone() {
|
||||
for (_, a) in multiline_annotations.iter_mut() {
|
||||
// Move all other multiline annotations overlapping with this one
|
||||
|
|
|
@ -324,7 +324,7 @@ impl CodeSuggestion {
|
|||
// Account for the difference between the width of the current code and the
|
||||
// snippet being suggested, so that the *later* suggestions are correctly
|
||||
// aligned on the screen.
|
||||
acc += len as isize - (cur_hi.col.0 - cur_lo.col.0) as isize;
|
||||
acc += len - (cur_hi.col.0 - cur_lo.col.0) as isize;
|
||||
}
|
||||
prev_hi = cur_hi;
|
||||
prev_line = sf.get_line(prev_hi.line - 1);
|
||||
|
|
|
@ -67,6 +67,10 @@ pub(crate) fn compare_impl_method<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
if let Err(_) = compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
|
||||
{
|
||||
return;
|
||||
|
@ -323,6 +327,34 @@ fn compare_predicate_entailment<'tcx>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compare_asyncness<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
impl_m: &ty::AssocItem,
|
||||
impl_m_span: Span,
|
||||
trait_m: &ty::AssocItem,
|
||||
trait_item_span: Option<Span>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async {
|
||||
match tcx.fn_sig(impl_m.def_id).skip_binder().output().kind() {
|
||||
ty::Alias(ty::Opaque, ..) => {
|
||||
// allow both `async fn foo()` and `fn foo() -> impl Future`
|
||||
}
|
||||
ty::Error(rustc_errors::ErrorGuaranteed { .. }) => {
|
||||
// We don't know if it's ok, but at least it's already an error.
|
||||
}
|
||||
_ => {
|
||||
return Err(tcx.sess.emit_err(crate::errors::AsyncTraitImplShouldBeAsync {
|
||||
span: impl_m_span,
|
||||
method_name: trait_m.name,
|
||||
trait_item_span,
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), level = "debug", ret)]
|
||||
pub fn collect_trait_impl_trait_tys<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
@ -51,6 +51,17 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
|
|||
pub ident: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_async_trait_impl_should_be_async)]
|
||||
pub struct AsyncTraitImplShouldBeAsync {
|
||||
#[primary_span]
|
||||
// #[label]
|
||||
pub span: Span,
|
||||
#[label(trait_item_label)]
|
||||
pub trait_item_span: Option<Span>,
|
||||
pub method_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")]
|
||||
pub struct DropImplOnWrongItem {
|
||||
|
|
|
@ -1757,7 +1757,6 @@ impl<'a> State<'a> {
|
|||
self.print_qpath(qpath, true);
|
||||
self.popen();
|
||||
if let Some(ddpos) = ddpos.as_opt_usize() {
|
||||
let ddpos = ddpos as usize;
|
||||
self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p));
|
||||
if ddpos != 0 {
|
||||
self.word_space(",");
|
||||
|
|
|
@ -893,7 +893,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
|
||||
let ty_str = if ty_str.len() > 50 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("on `{ty_str}` ")
|
||||
};
|
||||
err.span_label(span, format!(
|
||||
"{item_kind} cannot be called {ty_str}due to unsatisfied trait bounds"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ impl Mismatch {
|
|||
let crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
|
||||
|
||||
// If we're not in a "rustc_" crate, bail.
|
||||
let Some(("rustc", slug_prefix)) = crate_name.split_once("_") else { return None };
|
||||
let Some(("rustc", slug_prefix)) = crate_name.split_once('_') else { return None };
|
||||
|
||||
let slug_name = slug.segments.first()?.ident.to_string();
|
||||
if !slug_name.starts_with(slug_prefix) {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::ty::{
|
||||
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, ExistentialPredicate, InferConst,
|
||||
InferTy, Opaque, PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
|
||||
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
|
||||
PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
|
||||
};
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
@ -469,17 +469,6 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
Dynamic(dty, _, _) => {
|
||||
for pred in *dty {
|
||||
match pred.skip_binder() {
|
||||
ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => {
|
||||
// Okay
|
||||
}
|
||||
_ => return ControlFlow::Break(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Param(param) => {
|
||||
// FIXME: It would be nice to make this not use string manipulation,
|
||||
// but it's pretty hard to do this, since `ty::ParamTy` is missing
|
||||
|
|
|
@ -132,15 +132,12 @@ use std::collections::hash_map::{Entry, OccupiedEntry};
|
|||
use crate::MirPass;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{dump_mir, PassWhere};
|
||||
use rustc_middle::mir::{
|
||||
traversal, BasicBlock, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place,
|
||||
Rvalue, Statement, StatementKind, TerminatorKind,
|
||||
};
|
||||
use rustc_middle::mir::{
|
||||
visit::{MutVisitor, PlaceContext, Visitor},
|
||||
ProjectionElem,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::impls::MaybeLiveLocals;
|
||||
use rustc_mir_dataflow::{Analysis, ResultsCursor};
|
||||
|
@ -359,40 +356,45 @@ struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
|
|||
// through these methods, and not directly.
|
||||
impl<'alloc> Candidates<'alloc> {
|
||||
/// Just `Vec::retain`, but the condition is inverted and we add debugging output
|
||||
fn vec_remove_debug(
|
||||
fn vec_filter_candidates(
|
||||
src: Local,
|
||||
v: &mut Vec<Local>,
|
||||
mut f: impl FnMut(Local) -> bool,
|
||||
mut f: impl FnMut(Local) -> CandidateFilter,
|
||||
at: Location,
|
||||
) {
|
||||
v.retain(|dest| {
|
||||
let remove = f(*dest);
|
||||
if remove {
|
||||
if remove == CandidateFilter::Remove {
|
||||
trace!("eliminating {:?} => {:?} due to conflict at {:?}", src, dest, at);
|
||||
}
|
||||
!remove
|
||||
remove == CandidateFilter::Keep
|
||||
});
|
||||
}
|
||||
|
||||
/// `vec_remove_debug` but for an `Entry`
|
||||
fn entry_remove(
|
||||
/// `vec_filter_candidates` but for an `Entry`
|
||||
fn entry_filter_candidates(
|
||||
mut entry: OccupiedEntry<'_, Local, Vec<Local>>,
|
||||
p: Local,
|
||||
f: impl FnMut(Local) -> bool,
|
||||
f: impl FnMut(Local) -> CandidateFilter,
|
||||
at: Location,
|
||||
) {
|
||||
let candidates = entry.get_mut();
|
||||
Self::vec_remove_debug(p, candidates, f, at);
|
||||
Self::vec_filter_candidates(p, candidates, f, at);
|
||||
if candidates.len() == 0 {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all candidates `(p, q)` or `(q, p)` where `p` is the indicated local and `f(q)` is true.
|
||||
fn remove_candidates_if(&mut self, p: Local, mut f: impl FnMut(Local) -> bool, at: Location) {
|
||||
/// For all candidates `(p, q)` or `(q, p)` removes the candidate if `f(q)` says to do so
|
||||
fn filter_candidates_by(
|
||||
&mut self,
|
||||
p: Local,
|
||||
mut f: impl FnMut(Local) -> CandidateFilter,
|
||||
at: Location,
|
||||
) {
|
||||
// Cover the cases where `p` appears as a `src`
|
||||
if let Entry::Occupied(entry) = self.c.entry(p) {
|
||||
Self::entry_remove(entry, p, &mut f, at);
|
||||
Self::entry_filter_candidates(entry, p, &mut f, at);
|
||||
}
|
||||
// And the cases where `p` appears as a `dest`
|
||||
let Some(srcs) = self.reverse.get_mut(&p) else {
|
||||
|
@ -401,18 +403,31 @@ impl<'alloc> Candidates<'alloc> {
|
|||
// We use `retain` here to remove the elements from the reverse set if we've removed the
|
||||
// matching candidate in the forward set.
|
||||
srcs.retain(|src| {
|
||||
if !f(*src) {
|
||||
if f(*src) == CandidateFilter::Keep {
|
||||
return true;
|
||||
}
|
||||
let Entry::Occupied(entry) = self.c.entry(*src) else {
|
||||
return false;
|
||||
};
|
||||
Self::entry_remove(entry, *src, |dest| dest == p, at);
|
||||
Self::entry_filter_candidates(
|
||||
entry,
|
||||
*src,
|
||||
|dest| {
|
||||
if dest == p { CandidateFilter::Remove } else { CandidateFilter::Keep }
|
||||
},
|
||||
at,
|
||||
);
|
||||
false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
enum CandidateFilter {
|
||||
Keep,
|
||||
Remove,
|
||||
}
|
||||
|
||||
impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
|
||||
/// Filters the set of candidates to remove those that conflict.
|
||||
///
|
||||
|
@ -460,7 +475,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
|
|||
for (i, statement) in data.statements.iter().enumerate().rev() {
|
||||
self.at = Location { block, statement_index: i };
|
||||
self.live.seek_after_primary_effect(self.at);
|
||||
self.get_statement_write_info(&statement.kind);
|
||||
self.write_info.for_statement(&statement.kind, self.body);
|
||||
self.apply_conflicts();
|
||||
}
|
||||
}
|
||||
|
@ -469,80 +484,59 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
|
|||
fn apply_conflicts(&mut self) {
|
||||
let writes = &self.write_info.writes;
|
||||
for p in writes {
|
||||
self.candidates.remove_candidates_if(
|
||||
let other_skip = self.write_info.skip_pair.and_then(|(a, b)| {
|
||||
if a == *p {
|
||||
Some(b)
|
||||
} else if b == *p {
|
||||
Some(a)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
self.candidates.filter_candidates_by(
|
||||
*p,
|
||||
// It is possible that a local may be live for less than the
|
||||
// duration of a statement This happens in the case of function
|
||||
// calls or inline asm. Because of this, we also mark locals as
|
||||
// conflicting when both of them are written to in the same
|
||||
// statement.
|
||||
|q| self.live.contains(q) || writes.contains(&q),
|
||||
|q| {
|
||||
if Some(q) == other_skip {
|
||||
return CandidateFilter::Keep;
|
||||
}
|
||||
// It is possible that a local may be live for less than the
|
||||
// duration of a statement This happens in the case of function
|
||||
// calls or inline asm. Because of this, we also mark locals as
|
||||
// conflicting when both of them are written to in the same
|
||||
// statement.
|
||||
if self.live.contains(q) || writes.contains(&q) {
|
||||
CandidateFilter::Remove
|
||||
} else {
|
||||
CandidateFilter::Keep
|
||||
}
|
||||
},
|
||||
self.at,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the write info for the `statement`.
|
||||
fn get_statement_write_info(&mut self, statement: &StatementKind<'tcx>) {
|
||||
self.write_info.writes.clear();
|
||||
match statement {
|
||||
StatementKind::Assign(box (lhs, rhs)) => match rhs {
|
||||
Rvalue::Use(op) => {
|
||||
if !lhs.is_indirect() {
|
||||
self.get_assign_use_write_info(*lhs, op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.write_info.for_statement(statement);
|
||||
}
|
||||
|
||||
fn get_assign_use_write_info(&mut self, lhs: Place<'tcx>, rhs: &Operand<'tcx>) {
|
||||
// We register the writes for the operand unconditionally
|
||||
self.write_info.add_operand(rhs);
|
||||
// However, we cannot do the same thing for the `lhs` as that would always block the
|
||||
// optimization. Instead, we consider removing candidates manually.
|
||||
let Some(rhs) = rhs.place() else {
|
||||
self.write_info.add_place(lhs);
|
||||
return;
|
||||
};
|
||||
// Find out which candidate pair we should skip, if any
|
||||
let Some((src, dest)) = places_to_candidate_pair(lhs, rhs, self.body) else {
|
||||
self.write_info.add_place(lhs);
|
||||
return;
|
||||
};
|
||||
self.candidates.remove_candidates_if(
|
||||
lhs.local,
|
||||
|other| {
|
||||
// Check if this is the candidate pair that should not be removed
|
||||
if (lhs.local == src && other == dest) || (lhs.local == dest && other == src) {
|
||||
return false;
|
||||
}
|
||||
// Otherwise, do the "standard" thing
|
||||
self.live.contains(other)
|
||||
},
|
||||
self.at,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes where a statement/terminator writes to
|
||||
#[derive(Default, Debug)]
|
||||
struct WriteInfo {
|
||||
writes: Vec<Local>,
|
||||
/// If this pair of locals is a candidate pair, completely skip processing it during this
|
||||
/// statement. All other candidates are unaffected.
|
||||
skip_pair: Option<(Local, Local)>,
|
||||
}
|
||||
|
||||
impl WriteInfo {
|
||||
fn for_statement<'tcx>(&mut self, statement: &StatementKind<'tcx>) {
|
||||
fn for_statement<'tcx>(&mut self, statement: &StatementKind<'tcx>, body: &Body<'tcx>) {
|
||||
self.reset();
|
||||
match statement {
|
||||
StatementKind::Assign(box (lhs, rhs)) => {
|
||||
self.add_place(*lhs);
|
||||
match rhs {
|
||||
Rvalue::Use(op) | Rvalue::Repeat(op, _) => {
|
||||
Rvalue::Use(op) => {
|
||||
self.add_operand(op);
|
||||
self.consider_skipping_for_assign_use(*lhs, op, body);
|
||||
}
|
||||
Rvalue::Repeat(op, _) => {
|
||||
self.add_operand(op);
|
||||
}
|
||||
Rvalue::Cast(_, op, _)
|
||||
|
@ -586,8 +580,22 @@ impl WriteInfo {
|
|||
}
|
||||
}
|
||||
|
||||
fn consider_skipping_for_assign_use<'tcx>(
|
||||
&mut self,
|
||||
lhs: Place<'tcx>,
|
||||
rhs: &Operand<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) {
|
||||
let Some(rhs) = rhs.place() else {
|
||||
return
|
||||
};
|
||||
if let Some(pair) = places_to_candidate_pair(lhs, rhs, body) {
|
||||
self.skip_pair = Some(pair);
|
||||
}
|
||||
}
|
||||
|
||||
fn for_terminator<'tcx>(&mut self, terminator: &TerminatorKind<'tcx>) {
|
||||
self.writes.clear();
|
||||
self.reset();
|
||||
match terminator {
|
||||
TerminatorKind::SwitchInt { discr: op, .. }
|
||||
| TerminatorKind::Assert { cond: op, .. } => {
|
||||
|
@ -657,15 +665,16 @@ impl WriteInfo {
|
|||
Operand::Copy(_) | Operand::Constant(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.writes.clear();
|
||||
self.skip_pair = None;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// Candidate accumulation
|
||||
|
||||
fn is_constant<'tcx>(place: Place<'tcx>) -> bool {
|
||||
place.projection.iter().all(|p| !matches!(p, ProjectionElem::Deref | ProjectionElem::Index(_)))
|
||||
}
|
||||
|
||||
/// If the pair of places is being considered for merging, returns the candidate which would be
|
||||
/// merged in order to accomplish this.
|
||||
///
|
||||
|
@ -741,10 +750,6 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
|
|||
Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)),
|
||||
)) = &statement.kind
|
||||
{
|
||||
if !is_constant(*lhs) || !is_constant(*rhs) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some((src, dest)) = places_to_candidate_pair(*lhs, *rhs, self.body) else {
|
||||
return;
|
||||
};
|
||||
|
|
|
@ -875,18 +875,12 @@ pub struct PacRet {
|
|||
pub key: PAuthKey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Hash, Debug, PartialEq, Default)]
|
||||
pub struct BranchProtection {
|
||||
pub bti: bool,
|
||||
pub pac_ret: Option<PacRet>,
|
||||
}
|
||||
|
||||
impl Default for BranchProtection {
|
||||
fn default() -> Self {
|
||||
BranchProtection { bti: false, pac_ret: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn default_lib_output() -> CrateType {
|
||||
CrateType::Rlib
|
||||
}
|
||||
|
@ -1875,7 +1869,7 @@ fn parse_opt_level(
|
|||
.into_iter()
|
||||
.flat_map(|(i, s)| {
|
||||
// NB: This can match a string without `=`.
|
||||
if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None }
|
||||
if let Some("opt-level") = s.split('=').next() { Some(i) } else { None }
|
||||
})
|
||||
.max();
|
||||
if max_o > max_c {
|
||||
|
@ -1912,7 +1906,7 @@ fn select_debuginfo(
|
|||
.into_iter()
|
||||
.flat_map(|(i, s)| {
|
||||
// NB: This can match a string without `=`.
|
||||
if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None }
|
||||
if let Some("debuginfo") = s.split('=').next() { Some(i) } else { None }
|
||||
})
|
||||
.max();
|
||||
if max_g > max_c {
|
||||
|
|
|
@ -175,7 +175,7 @@ cfg_if::cfg_if! {
|
|||
// There might still be a tail left to analyze
|
||||
let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset;
|
||||
if tail_start < src.len() {
|
||||
analyze_source_file_generic(&src[tail_start as usize ..],
|
||||
analyze_source_file_generic(&src[tail_start ..],
|
||||
src.len() - tail_start,
|
||||
output_offset + BytePos::from_usize(tail_start),
|
||||
lines,
|
||||
|
@ -219,7 +219,7 @@ fn analyze_source_file_generic(
|
|||
while i < scan_len {
|
||||
let byte = unsafe {
|
||||
// We verified that i < scan_len <= src.len()
|
||||
*src_bytes.get_unchecked(i as usize)
|
||||
*src_bytes.get_unchecked(i)
|
||||
};
|
||||
|
||||
// How much to advance in order to get to the next UTF-8 char in the
|
||||
|
|
|
@ -1381,7 +1381,7 @@ impl<S: Encoder> Encodable<S> for SourceFile {
|
|||
4 => {
|
||||
raw_diffs = Vec::with_capacity(bytes_per_diff * num_diffs);
|
||||
for diff in diff_iter {
|
||||
raw_diffs.extend_from_slice(&(diff.0 as u32).to_le_bytes());
|
||||
raw_diffs.extend_from_slice(&(diff.0).to_le_bytes());
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
|
|
@ -941,7 +941,7 @@ impl SourceMap {
|
|||
/// Otherwise, the span reached to limit is returned.
|
||||
pub fn span_look_ahead(&self, span: Span, expect: Option<&str>, limit: Option<usize>) -> Span {
|
||||
let mut sp = span;
|
||||
for _ in 0..limit.unwrap_or(100 as usize) {
|
||||
for _ in 0..limit.unwrap_or(100_usize) {
|
||||
sp = self.next_point(sp);
|
||||
if let Ok(ref snippet) = self.span_to_snippet(sp) {
|
||||
if expect.map_or(false, |es| snippet == es) {
|
||||
|
|
|
@ -81,7 +81,7 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
|
|||
_ => os.into(),
|
||||
};
|
||||
|
||||
let platform_version: StaticCow<str> = match os.as_ref() {
|
||||
let platform_version: StaticCow<str> = match os {
|
||||
"ios" => ios_lld_platform_version(),
|
||||
"tvos" => tvos_lld_platform_version(),
|
||||
"watchos" => watchos_lld_platform_version(),
|
||||
|
|
|
@ -5,12 +5,7 @@ pub fn target() -> Target {
|
|||
base.max_atomic_width = Some(64);
|
||||
base.add_pre_link_args(
|
||||
LinkerFlavor::Unix(Cc::No),
|
||||
&[
|
||||
"-b64".into(),
|
||||
"-bpT:0x100000000".into(),
|
||||
"-bpD:0x110000000".into(),
|
||||
"-bcdtors:all:0:s".into(),
|
||||
],
|
||||
&["-b64", "-bpT:0x100000000", "-bpD:0x110000000", "-bcdtors:all:0:s"],
|
||||
);
|
||||
|
||||
Target {
|
||||
|
|
|
@ -1636,17 +1636,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
infer::LateBoundRegionConversionTime::HigherRankedType,
|
||||
bound_predicate.rebind(data),
|
||||
);
|
||||
let normalized_ty = ocx.normalize(
|
||||
&obligation.cause,
|
||||
obligation.param_env,
|
||||
self.tcx.mk_projection(data.projection_ty.def_id, data.projection_ty.substs),
|
||||
);
|
||||
let unnormalized_term = match data.term.unpack() {
|
||||
ty::TermKind::Ty(_) => self
|
||||
.tcx
|
||||
.mk_projection(data.projection_ty.def_id, data.projection_ty.substs)
|
||||
.into(),
|
||||
ty::TermKind::Const(ct) => self
|
||||
.tcx
|
||||
.mk_const(
|
||||
ty::UnevaluatedConst {
|
||||
def: ty::WithOptConstParam::unknown(data.projection_ty.def_id),
|
||||
substs: data.projection_ty.substs,
|
||||
},
|
||||
ct.ty(),
|
||||
)
|
||||
.into(),
|
||||
};
|
||||
let normalized_term =
|
||||
ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
|
||||
|
||||
debug!(?obligation.cause, ?obligation.param_env);
|
||||
|
||||
debug!(?normalized_ty, data.ty = ?data.term);
|
||||
debug!(?normalized_term, data.ty = ?data.term);
|
||||
|
||||
let is_normalized_ty_expected = !matches!(
|
||||
let is_normalized_term_expected = !matches!(
|
||||
obligation.cause.code().peel_derives(),
|
||||
ObligationCauseCode::ItemObligation(_)
|
||||
| ObligationCauseCode::BindingObligation(_, _)
|
||||
|
@ -1655,7 +1668,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
| ObligationCauseCode::ObjectCastObligation(..)
|
||||
| ObligationCauseCode::OpaqueType
|
||||
);
|
||||
let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error());
|
||||
|
||||
// constrain inference variables a bit more to nested obligations from normalize so
|
||||
// we can have more helpful errors.
|
||||
|
@ -1664,11 +1676,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
if let Err(new_err) = ocx.eq_exp(
|
||||
&obligation.cause,
|
||||
obligation.param_env,
|
||||
is_normalized_ty_expected,
|
||||
normalized_ty,
|
||||
expected_ty,
|
||||
is_normalized_term_expected,
|
||||
normalized_term,
|
||||
data.term,
|
||||
) {
|
||||
(Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err)
|
||||
(Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err)
|
||||
} else {
|
||||
(None, error.err)
|
||||
}
|
||||
|
@ -1677,12 +1689,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
};
|
||||
|
||||
let msg = values
|
||||
.and_then(|(predicate, _, normalized_ty, expected_ty)| {
|
||||
self.maybe_detailed_projection_msg(
|
||||
predicate,
|
||||
normalized_ty.into(),
|
||||
expected_ty.into(),
|
||||
)
|
||||
.and_then(|(predicate, _, normalized_term, expected_term)| {
|
||||
self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
|
||||
})
|
||||
.unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate));
|
||||
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
|
||||
|
|
|
@ -919,7 +919,7 @@ fn variant_info_for_generator<'tcx>(
|
|||
def_id: DefId,
|
||||
substs: ty::SubstsRef<'tcx>,
|
||||
) -> (Vec<VariantInfo>, Option<Size>) {
|
||||
let Variants::Multiple { tag, ref tag_encoding, .. } = layout.variants else {
|
||||
let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else {
|
||||
return (vec![], None);
|
||||
};
|
||||
|
||||
|
@ -975,12 +975,28 @@ fn variant_info_for_generator<'tcx>(
|
|||
if variant_size == Size::ZERO {
|
||||
variant_size = upvars_size;
|
||||
}
|
||||
// We need to add the discriminant size back into min_size, since it is subtracted
|
||||
// later during printing.
|
||||
variant_size += match tag_encoding {
|
||||
TagEncoding::Direct => tag.size(cx),
|
||||
_ => Size::ZERO,
|
||||
};
|
||||
|
||||
// This `if` deserves some explanation.
|
||||
//
|
||||
// The layout code has a choice of where to place the discriminant of this generator.
|
||||
// If the discriminant of the generator is placed early in the layout (before the
|
||||
// variant's own fields), then it'll implicitly be counted towards the size of the
|
||||
// variant, since we use the maximum offset to calculate size.
|
||||
// (side-note: I know this is a bit problematic given upvars placement, etc).
|
||||
//
|
||||
// This is important, since the layout printing code always subtracts this discriminant
|
||||
// size from the variant size if the struct is "enum"-like, so failing to account for it
|
||||
// will either lead to numerical underflow, or an underreported variant size...
|
||||
//
|
||||
// However, if the discriminant is placed past the end of the variant, then we need
|
||||
// to factor in the size of the discriminant manually. This really should be refactored
|
||||
// better, but this "works" for now.
|
||||
if layout.fields.offset(tag_field) >= variant_size {
|
||||
variant_size += match tag_encoding {
|
||||
TagEncoding::Direct => tag.size(cx),
|
||||
_ => Size::ZERO,
|
||||
};
|
||||
}
|
||||
|
||||
VariantInfo {
|
||||
name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name(variant_idx))),
|
||||
|
|
|
@ -250,9 +250,9 @@ impl<I: Interner> Clone for TyKind<I> {
|
|||
match self {
|
||||
Bool => Bool,
|
||||
Char => Char,
|
||||
Int(i) => Int(i.clone()),
|
||||
Uint(u) => Uint(u.clone()),
|
||||
Float(f) => Float(f.clone()),
|
||||
Int(i) => Int(*i),
|
||||
Uint(u) => Uint(*u),
|
||||
Float(f) => Float(*f),
|
||||
Adt(d, s) => Adt(d.clone(), s.clone()),
|
||||
Foreign(d) => Foreign(d.clone()),
|
||||
Str => Str,
|
||||
|
@ -262,7 +262,7 @@ impl<I: Interner> Clone for TyKind<I> {
|
|||
Ref(r, t, m) => Ref(r.clone(), t.clone(), m.clone()),
|
||||
FnDef(d, s) => FnDef(d.clone(), s.clone()),
|
||||
FnPtr(s) => FnPtr(s.clone()),
|
||||
Dynamic(p, r, repr) => Dynamic(p.clone(), r.clone(), repr.clone()),
|
||||
Dynamic(p, r, repr) => Dynamic(p.clone(), r.clone(), *repr),
|
||||
Closure(d, s) => Closure(d.clone(), s.clone()),
|
||||
Generator(d, s, m) => Generator(d.clone(), s.clone(), m.clone()),
|
||||
GeneratorWitness(g) => GeneratorWitness(g.clone()),
|
||||
|
@ -270,7 +270,7 @@ impl<I: Interner> Clone for TyKind<I> {
|
|||
Tuple(t) => Tuple(t.clone()),
|
||||
Alias(k, p) => Alias(*k, p.clone()),
|
||||
Param(p) => Param(p.clone()),
|
||||
Bound(d, b) => Bound(d.clone(), b.clone()),
|
||||
Bound(d, b) => Bound(*d, b.clone()),
|
||||
Placeholder(p) => Placeholder(p.clone()),
|
||||
Infer(t) => Infer(t.clone()),
|
||||
Error(e) => Error(e.clone()),
|
||||
|
@ -936,7 +936,7 @@ impl<I: Interner> Clone for RegionKind<I> {
|
|||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
ReEarlyBound(r) => ReEarlyBound(r.clone()),
|
||||
ReLateBound(d, r) => ReLateBound(d.clone(), r.clone()),
|
||||
ReLateBound(d, r) => ReLateBound(*d, r.clone()),
|
||||
ReFree(r) => ReFree(r.clone()),
|
||||
ReStatic => ReStatic,
|
||||
ReVar(r) => ReVar(r.clone()),
|
||||
|
|
|
@ -1396,7 +1396,6 @@ kbd {
|
|||
vertical-align: middle;
|
||||
border: solid 1px var(--border-color);
|
||||
border-radius: 3px;
|
||||
cursor: default;
|
||||
color: var(--kbd--color);
|
||||
background-color: var(--kbd-background);
|
||||
box-shadow: inset 0 -1px 0 var(--kbd-box-shadow-color);
|
||||
|
@ -1970,7 +1969,6 @@ in storage.js
|
|||
}
|
||||
|
||||
.scraped-example .code-wrapper .src-line-numbers {
|
||||
margin: 0;
|
||||
padding: 14px 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,8 +150,6 @@ pre, .rustdoc.source .example-wrap {
|
|||
color: #c5c5c5;
|
||||
}
|
||||
|
||||
.content .item-info::before { color: #ccc; }
|
||||
|
||||
.sidebar h2 a,
|
||||
.sidebar h3 a {
|
||||
color: white;
|
||||
|
|
|
@ -85,8 +85,6 @@
|
|||
--table-alt-row-background-color: #2A2A2A;
|
||||
}
|
||||
|
||||
.content .item-info::before { color: #ccc; }
|
||||
|
||||
body.source .example-wrap pre.rust a {
|
||||
background: #333;
|
||||
}
|
||||
|
|
|
@ -82,8 +82,6 @@
|
|||
--table-alt-row-background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.content .item-info::before { color: #ccc; }
|
||||
|
||||
body.source .example-wrap pre.rust a {
|
||||
background: #eee;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ define-function: (
|
|||
"color": |color|,
|
||||
"background-color": |background|,
|
||||
"box-shadow": |box_shadow| + " 0px -1px 0px 0px inset",
|
||||
"cursor": "default",
|
||||
}, ALL)),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
fn foo(x: &(dyn Display + Send)) {}
|
||||
|
||||
fn main() {
|
||||
foo();
|
||||
//~^ ERROR this function takes 1 argument but 0 arguments were supplied
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
error[E0061]: this function takes 1 argument but 0 arguments were supplied
|
||||
--> $DIR/display-is-suggestable.rs:6:5
|
||||
|
|
||||
LL | foo();
|
||||
| ^^^-- an argument of type `&dyn std::fmt::Display + Send` is missing
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/display-is-suggestable.rs:3:4
|
||||
|
|
||||
LL | fn foo(x: &(dyn Display + Send)) {}
|
||||
| ^^^ ------------------------
|
||||
help: provide the argument
|
||||
|
|
||||
LL | foo(/* &dyn std::fmt::Display + Send */);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0061`.
|
|
@ -0,0 +1,24 @@
|
|||
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/const-projection-err.rs:4:26
|
||||
|
|
||||
LL | #![cfg_attr(gce, feature(generic_const_exprs))]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
error[E0271]: type mismatch resolving `<T as TraitWAssocConst>::A == 1`
|
||||
--> $DIR/const-projection-err.rs:14:11
|
||||
|
|
||||
LL | foo::<T>();
|
||||
| ^ expected `0`, found `1`
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/const-projection-err.rs:11:28
|
||||
|
|
||||
LL | fn foo<T: TraitWAssocConst<A = 1>>() {}
|
||||
| ^^^^^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
18
src/test/ui/associated-type-bounds/const-projection-err.rs
Normal file
18
src/test/ui/associated-type-bounds/const-projection-err.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
// revisions: stock gce
|
||||
|
||||
#![feature(associated_const_equality)]
|
||||
#![cfg_attr(gce, feature(generic_const_exprs))]
|
||||
//[gce]~^ WARN the feature `generic_const_exprs` is incomplete
|
||||
|
||||
trait TraitWAssocConst {
|
||||
const A: usize;
|
||||
}
|
||||
|
||||
fn foo<T: TraitWAssocConst<A = 1>>() {}
|
||||
|
||||
fn bar<T: TraitWAssocConst<A = 0>>() {
|
||||
foo::<T>();
|
||||
//~^ ERROR type mismatch resolving `<T as TraitWAssocConst>::A == 1`
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,17 @@
|
|||
error[E0271]: type mismatch resolving `<T as TraitWAssocConst>::A == 1`
|
||||
--> $DIR/const-projection-err.rs:14:11
|
||||
|
|
||||
LL | foo::<T>();
|
||||
| ^ expected `1`, found `<T as TraitWAssocConst>::A`
|
||||
|
|
||||
= note: expected constant `1`
|
||||
found constant `<T as TraitWAssocConst>::A`
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/const-projection-err.rs:11:28
|
||||
|
|
||||
LL | fn foo<T: TraitWAssocConst<A = 1>>() {}
|
||||
| ^^^^^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
|
@ -1,4 +1,3 @@
|
|||
// check-pass
|
||||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
|
@ -13,11 +12,9 @@ trait MyTrait {
|
|||
}
|
||||
|
||||
impl MyTrait for i32 {
|
||||
// This will break once a PR that implements #102745 is merged
|
||||
fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
|
||||
Box::pin(async {
|
||||
*self
|
||||
})
|
||||
//~^ ERROR method `foo` should be async
|
||||
Box::pin(async { *self })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
error: method `foo` should be async because the method from the trait is async
|
||||
--> $DIR/async-example-desugared-boxed.rs:15:5
|
||||
|
|
||||
LL | async fn foo(&self) -> i32;
|
||||
| --------------------------- required because the trait method is async
|
||||
...
|
||||
LL | fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// check-pass
|
||||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![feature(return_position_impl_trait_in_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::Poll;
|
||||
|
||||
trait MyTrait {
|
||||
async fn foo(&self) -> i32;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyFuture(i32);
|
||||
|
||||
impl Future for MyFuture {
|
||||
type Output = i32;
|
||||
fn poll(
|
||||
self: Pin<&mut Self>,
|
||||
_: &mut std::task::Context<'_>,
|
||||
) -> Poll<<Self as Future>::Output> {
|
||||
Poll::Ready(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait for i32 {
|
||||
// FIXME: this should eventually require `#[refine]` to compile, because it also provides
|
||||
// `Clone`.
|
||||
fn foo(&self) -> impl Future<Output = i32> + Clone {
|
||||
MyFuture(*self)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,29 @@
|
|||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![feature(return_position_impl_trait_in_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::future::Future;
|
||||
use std::task::Poll;
|
||||
|
||||
trait MyTrait {
|
||||
async fn foo(&self) -> i32;
|
||||
}
|
||||
|
||||
struct MyFuture;
|
||||
impl Future for MyFuture {
|
||||
type Output = i32;
|
||||
fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||
Poll::Ready(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait for u32 {
|
||||
fn foo(&self) -> MyFuture {
|
||||
//~^ ERROR method `foo` should be async
|
||||
MyFuture
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
error: method `foo` should be async because the method from the trait is async
|
||||
--> $DIR/async-example-desugared-manual.rs:23:5
|
||||
|
|
||||
LL | async fn foo(&self) -> i32;
|
||||
| --------------------------- required because the trait method is async
|
||||
...
|
||||
LL | fn foo(&self) -> MyFuture {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -12,11 +12,8 @@ trait MyTrait {
|
|||
}
|
||||
|
||||
impl MyTrait for i32 {
|
||||
// This will break once a PR that implements #102745 is merged
|
||||
fn foo(&self) -> impl Future<Output = i32> + '_ {
|
||||
async {
|
||||
*self
|
||||
}
|
||||
async { *self }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ trait MyTrait {
|
|||
|
||||
impl MyTrait for i32 {
|
||||
fn foo(&self) -> i32 {
|
||||
//~^ ERROR: `i32` is not a future [E0277]
|
||||
//~^ ERROR: method `foo` should be async
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
error[E0277]: `i32` is not a future
|
||||
--> $DIR/fn-not-async-err.rs:11:22
|
||||
|
|
||||
LL | fn foo(&self) -> i32 {
|
||||
| ^^^ `i32` is not a future
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `i32`
|
||||
= note: i32 must be a future or must implement `IntoFuture` to be awaited
|
||||
note: required by a bound in `MyTrait::foo::{opaque#0}`
|
||||
--> $DIR/fn-not-async-err.rs:7:28
|
||||
error: method `foo` should be async because the method from the trait is async
|
||||
--> $DIR/fn-not-async-err.rs:11:5
|
||||
|
|
||||
LL | async fn foo(&self) -> i32;
|
||||
| ^^^ required by this bound in `MyTrait::foo::{opaque#0}`
|
||||
| --------------------------- required because the trait method is async
|
||||
...
|
||||
LL | fn foo(&self) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
|
|
@ -12,9 +12,7 @@ trait MyTrait {
|
|||
impl MyTrait for i32 {
|
||||
fn foo(&self) -> impl Future<Output = i32> {
|
||||
//~^ ERROR `impl Trait` only allowed in function and inherent method return types, not in `impl` method return [E0562]
|
||||
async {
|
||||
*self
|
||||
}
|
||||
async { *self }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
src/test/ui/async-await/in-trait/issue-104678.rs
Normal file
31
src/test/ui/async-await/in-trait/issue-104678.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
// edition:2021
|
||||
// check-pass
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::future::Future;
|
||||
pub trait Pool {
|
||||
type Conn;
|
||||
|
||||
async fn async_callback<'a, F: FnOnce(&'a Self::Conn) -> Fut, Fut: Future<Output = ()>>(
|
||||
&'a self,
|
||||
callback: F,
|
||||
) -> ();
|
||||
}
|
||||
|
||||
pub struct PoolImpl;
|
||||
pub struct ConnImpl;
|
||||
|
||||
impl Pool for PoolImpl {
|
||||
type Conn = ConnImpl;
|
||||
|
||||
async fn async_callback<'a, F: FnOnce(&'a Self::Conn) -> Fut, Fut: Future<Output = ()>>(
|
||||
&'a self,
|
||||
_callback: F,
|
||||
) -> () {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -8,7 +8,7 @@ LL | pub struct Map<S, F> {
|
|||
| doesn't satisfy `_: StreamExt`
|
||||
...
|
||||
LL | let filter = map.filterx(|x: &_| true);
|
||||
| ^^^^^^^ method cannot be called on `Map<Repeat, [closure@$DIR/issue-30786.rs:117:27: 117:34]>` due to unsatisfied trait bounds
|
||||
| ^^^^^^^ method cannot be called due to unsatisfied trait bounds
|
||||
|
|
||||
note: the following trait bounds were not satisfied:
|
||||
`&'a mut &Map<Repeat, [closure@$DIR/issue-30786.rs:117:27: 117:34]>: Stream`
|
||||
|
@ -29,7 +29,7 @@ LL | pub struct Filter<S, F> {
|
|||
| doesn't satisfy `_: StreamExt`
|
||||
...
|
||||
LL | let count = filter.countx();
|
||||
| ^^^^^^ method cannot be called on `Filter<Map<Repeat, for<'a> fn(&'a u64) -> &'a u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>` due to unsatisfied trait bounds
|
||||
| ^^^^^^ method cannot be called due to unsatisfied trait bounds
|
||||
|
|
||||
note: the following trait bounds were not satisfied:
|
||||
`&'a mut &Filter<Map<Repeat, for<'a> fn(&'a u64) -> &'a u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>: Stream`
|
||||
|
|
|
@ -55,8 +55,10 @@ error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
|
|||
--> $DIR/issue-105330.rs:12:11
|
||||
|
|
||||
LL | foo::<Demo>()();
|
||||
| ^^^^ types differ
|
||||
| ^^^^ expected `32`, found `<Demo as TraitWAssocConst>::A`
|
||||
|
|
||||
= note: expected constant `32`
|
||||
found constant `<Demo as TraitWAssocConst>::A`
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/issue-105330.rs:11:28
|
||||
|
|
||||
|
@ -89,8 +91,10 @@ error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
|
|||
--> $DIR/issue-105330.rs:19:11
|
||||
|
|
||||
LL | foo::<Demo>();
|
||||
| ^^^^ types differ
|
||||
| ^^^^ expected `32`, found `<Demo as TraitWAssocConst>::A`
|
||||
|
|
||||
= note: expected constant `32`
|
||||
found constant `<Demo as TraitWAssocConst>::A`
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/issue-105330.rs:11:28
|
||||
|
|
||||
|
|
|
@ -13,7 +13,7 @@ error[E0599]: the method `collect` exists for struct `Cloned<TakeWhile<&mut std:
|
|||
--> $DIR/issue-31173.rs:12:10
|
||||
|
|
||||
LL | .collect();
|
||||
| ^^^^^^^ method cannot be called on `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:7:21: 7:25]>>` due to unsatisfied trait bounds
|
||||
| ^^^^^^^ method cannot be called due to unsatisfied trait bounds
|
||||
--> $SRC_DIR/core/src/iter/adapters/take_while.rs:LL:COL
|
||||
|
|
||||
= note: doesn't satisfy `<_ as Iterator>::Item = &_`
|
||||
|
|
|
@ -17,7 +17,7 @@ error[E0599]: the method `count` exists for struct `Filter<Fuse<std::iter::Once<
|
|||
--> $DIR/issue-36053-2.rs:7:55
|
||||
|
|
||||
LL | once::<&str>("str").fuse().filter(|a: &str| true).count();
|
||||
| --------- ^^^^^ method cannot be called on `Filter<Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:48]>` due to unsatisfied trait bounds
|
||||
| --------- ^^^^^ method cannot be called due to unsatisfied trait bounds
|
||||
| |
|
||||
| doesn't satisfy `<_ as FnOnce<(&&str,)>>::Output = bool`
|
||||
| doesn't satisfy `_: FnMut<(&&str,)>`
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type lib
|
||||
// edition:2021
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
|
||||
#![feature(start)]
|
||||
|
||||
async fn wait() {}
|
||||
|
||||
async fn test(arg: [u8; 8192]) {
|
||||
pub async fn test(arg: [u8; 8192]) {
|
||||
wait().await;
|
||||
drop(arg);
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
let _ = test([0; 8192]);
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
print-type-size type: `[async fn body@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes
|
||||
print-type-size type: `[async fn body@$DIR/async.rs:8:36: 11:2]`: 16386 bytes, alignment: 1 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `Suspend0`: 16385 bytes
|
||||
print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
|
||||
|
@ -16,14 +16,14 @@ print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment
|
|||
print-type-size variant `MaybeUninit`: 8192 bytes
|
||||
print-type-size field `.uninit`: 0 bytes
|
||||
print-type-size field `.value`: 8192 bytes
|
||||
print-type-size type: `[async fn body@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size type: `[async fn body@$DIR/async.rs:6:17: 6:19]`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `Unresumed`: 0 bytes
|
||||
print-type-size variant `Returned`: 0 bytes
|
||||
print-type-size variant `Panicked`: 0 bytes
|
||||
print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:6:17: 6:19]>`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size field `.value`: 1 bytes
|
||||
print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:6:17: 6:19]>`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size variant `MaybeUninit`: 1 bytes
|
||||
print-type-size field `.uninit`: 0 bytes
|
||||
print-type-size field `.value`: 1 bytes
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
|
||||
#![feature(start, generators, generator_trait)]
|
||||
#![feature(generators, generator_trait)]
|
||||
|
||||
use std::ops::Generator;
|
||||
|
||||
|
@ -13,8 +13,6 @@ fn generator<const C: usize>(array: [u8; C]) -> impl Generator<Yield = (), Retur
|
|||
}
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
pub fn foo() {
|
||||
let _ = generator([0; 8192]);
|
||||
0
|
||||
}
|
||||
|
|
23
src/test/ui/print_type_sizes/generator_discr_placement.rs
Normal file
23
src/test/ui/print_type_sizes/generator_discr_placement.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
// compile-flags: -Z print-type-sizes --crate-type lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
|
||||
// Tests a generator that has its discriminant as the *final* field.
|
||||
|
||||
// Avoid emitting panic handlers, like the rest of these tests...
|
||||
#![feature(generators)]
|
||||
|
||||
pub fn foo() {
|
||||
let a = || {
|
||||
{
|
||||
let w: i32 = 4;
|
||||
yield;
|
||||
drop(w);
|
||||
}
|
||||
{
|
||||
let z: i32 = 7;
|
||||
yield;
|
||||
drop(z);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
print-type-size type: `[generator@$DIR/generator_discr_placement.rs:11:13: 11:15]`: 8 bytes, alignment: 4 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `Suspend0`: 7 bytes
|
||||
print-type-size padding: 3 bytes
|
||||
print-type-size field `.w`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size variant `Suspend1`: 7 bytes
|
||||
print-type-size padding: 3 bytes
|
||||
print-type-size field `.z`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size variant `Unresumed`: 0 bytes
|
||||
print-type-size variant `Returned`: 0 bytes
|
||||
print-type-size variant `Panicked`: 0 bytes
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
// ^-- needed because `--pass check` does not emit the output needed.
|
||||
|
@ -8,24 +8,6 @@
|
|||
// monomorphized, in the MIR of the original function in which they
|
||||
// occur, to have their size reported.
|
||||
|
||||
#![feature(start)]
|
||||
|
||||
// In an ad-hoc attempt to avoid the injection of unwinding code
|
||||
// (which clutters the output of `-Z print-type-sizes` with types from
|
||||
// `unwind::libunwind`):
|
||||
//
|
||||
// * I am not using Default to build values because that seems to
|
||||
// cause the injection of unwinding code. (Instead I just make `fn new`
|
||||
// methods.)
|
||||
//
|
||||
// * Pair derive Copy to ensure that we don't inject
|
||||
// unwinding code into generic uses of Pair when T itself is also
|
||||
// Copy.
|
||||
//
|
||||
// (I suspect this reflect some naivety within the rust compiler
|
||||
// itself; it should be checking for drop glue, i.e., a destructor
|
||||
// somewhere in the monomorphized types. It should not matter whether
|
||||
// the type is Copy.)
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Pair<T> {
|
||||
_car: T,
|
||||
|
@ -61,11 +43,9 @@ pub fn f1<T:Copy>(x: T) {
|
|||
Pair::new(FiftyBytes::new(), FiftyBytes::new());
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
pub fn start() {
|
||||
let _b: Pair<u8> = Pair::new(0, 0);
|
||||
let _s: Pair<SevenBytes> = Pair::new(SevenBytes::new(), SevenBytes::new());
|
||||
let ref _z: ZeroSized = ZeroSized;
|
||||
f1::<SevenBytes>(SevenBytes::new());
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
|
||||
// This file illustrates that when multiple structural types occur in
|
||||
// a function, every one of them is included in the output.
|
||||
|
||||
#![feature(start)]
|
||||
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
pub struct FiftyBytes([u8; 50]);
|
||||
|
||||
|
@ -13,11 +11,3 @@ pub enum Enum {
|
|||
Small(SevenBytes),
|
||||
Large(FiftyBytes),
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
let _e: Enum;
|
||||
let _f: FiftyBytes;
|
||||
let _s: SevenBytes;
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
// ^-- needed because `--pass check` does not emit the output needed.
|
||||
|
@ -14,7 +14,6 @@
|
|||
// aligned (while on most it is 8-byte aligned) and so the resulting
|
||||
// padding and overall computed sizes can be quite different.
|
||||
|
||||
#![feature(start)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
|
@ -56,7 +55,7 @@ pub struct NestedNonZero {
|
|||
|
||||
impl Default for NestedNonZero {
|
||||
fn default() -> Self {
|
||||
NestedNonZero { pre: 0, val: NonZeroU32::new(1).unwrap(), post: 0 }
|
||||
NestedNonZero { pre: 0, val: unsafe { NonZeroU32::new_unchecked(1) }, post: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,8 +75,7 @@ pub union Union2<A: Copy, B: Copy> {
|
|||
b: B,
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
pub fn test() {
|
||||
let _x: MyOption<NonZeroU32> = Default::default();
|
||||
let _y: EmbeddedDiscr = Default::default();
|
||||
let _z: MyOption<IndirectNonZero> = Default::default();
|
||||
|
@ -96,6 +94,4 @@ fn start(_: isize, _: *const *const u8) -> isize {
|
|||
// ...even when theoretically possible.
|
||||
let _j: MyOption<Union1<NonZeroU32>> = Default::default();
|
||||
let _k: MyOption<Union2<NonZeroU32, NonZeroU32>> = Default::default();
|
||||
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
// ^-- needed because `--pass check` does not emit the output needed.
|
||||
|
@ -8,16 +8,12 @@
|
|||
// (even if multiple functions), it is only printed once in the
|
||||
// print-type-sizes output.
|
||||
|
||||
#![feature(start)]
|
||||
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
|
||||
pub fn f1() {
|
||||
let _s: SevenBytes = SevenBytes([0; 7]);
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
pub fn test() {
|
||||
let _s: SevenBytes = SevenBytes([0; 7]);
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
// ^-- needed because `--pass check` does not emit the output needed.
|
||||
|
@ -13,11 +13,10 @@
|
|||
// padding and overall computed sizes can be quite different.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![feature(start)]
|
||||
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
struct Packed1 {
|
||||
pub struct Packed1 {
|
||||
a: u8,
|
||||
b: u8,
|
||||
g: i32,
|
||||
|
@ -28,7 +27,7 @@ struct Packed1 {
|
|||
|
||||
#[derive(Default)]
|
||||
#[repr(packed(2))]
|
||||
struct Packed2 {
|
||||
pub struct Packed2 {
|
||||
a: u8,
|
||||
b: u8,
|
||||
g: i32,
|
||||
|
@ -40,7 +39,7 @@ struct Packed2 {
|
|||
#[derive(Default)]
|
||||
#[repr(packed(2))]
|
||||
#[repr(C)]
|
||||
struct Packed2C {
|
||||
pub struct Packed2C {
|
||||
a: u8,
|
||||
b: u8,
|
||||
g: i32,
|
||||
|
@ -50,7 +49,7 @@ struct Packed2C {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Padded {
|
||||
pub struct Padded {
|
||||
a: u8,
|
||||
b: u8,
|
||||
g: i32,
|
||||
|
@ -58,12 +57,3 @@ struct Padded {
|
|||
h: i16,
|
||||
d: u8,
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
let _c: Packed1 = Default::default();
|
||||
let _d: Packed2 = Default::default();
|
||||
let _e: Packed2C = Default::default();
|
||||
let _f: Padded = Default::default();
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
|
||||
// This file illustrates how padding is handled: alignment
|
||||
|
@ -9,7 +9,6 @@
|
|||
// aligned (while on most it is 8-byte aligned) and so the resulting
|
||||
// padding and overall computed sizes can be quite different.
|
||||
|
||||
#![feature(start)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct S {
|
||||
|
@ -27,8 +26,3 @@ enum E2 {
|
|||
A(i8, i32),
|
||||
B(S),
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
// ^-- needed because `--pass check` does not emit the output needed.
|
||||
|
@ -11,7 +11,7 @@
|
|||
// It avoids using u64/i64 because on some targets that is only 4-byte
|
||||
// aligned (while on most it is 8-byte aligned) and so the resulting
|
||||
// padding and overall computed sizes can be quite different.
|
||||
#![feature(start)]
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[repr(align(16))]
|
||||
|
@ -24,15 +24,9 @@ enum E {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct S {
|
||||
pub struct S {
|
||||
a: i32,
|
||||
b: i32,
|
||||
c: A,
|
||||
d: i8,
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
let _s: S = Default::default();
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
|
||||
// This test makes sure that the tag is not grown for `repr(C)` or `repr(u8)`
|
||||
// variants (see https://github.com/rust-lang/rust/issues/50098 for the original bug).
|
||||
|
||||
#![feature(start)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[repr(C, u8)]
|
||||
|
@ -18,8 +17,3 @@ enum Repru8 {
|
|||
A(u16),
|
||||
B,
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
// ^-- needed because `--pass check` does not emit the output needed.
|
||||
// FIXME: consider using an attribute instead of side-effects.
|
||||
|
||||
#![feature(never_type)]
|
||||
#![feature(start)]
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
pub fn test() {
|
||||
let _x: Option<!> = None;
|
||||
let _y: Result<u32, !> = Ok(42);
|
||||
let _z: Result<!, !> = loop {};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
|
||||
// This file illustrates two things:
|
||||
|
@ -9,8 +9,6 @@
|
|||
// 2. For an enum, the print-type-sizes output will also include the
|
||||
// size of each variant.
|
||||
|
||||
#![feature(start)]
|
||||
|
||||
pub struct SevenBytes([u8; 7]);
|
||||
pub struct FiftyBytes([u8; 50]);
|
||||
|
||||
|
@ -18,9 +16,3 @@ pub enum Enum {
|
|||
Small(SevenBytes),
|
||||
Large(FiftyBytes),
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
let _e: Enum;
|
||||
0
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
// compile-flags: -Z print-type-sizes
|
||||
// compile-flags: -Z print-type-sizes --crate-type=lib
|
||||
// build-pass
|
||||
// ignore-pass
|
||||
|
||||
// At one point, zero-sized fields such as those in this file were causing
|
||||
// incorrect output from `-Z print-type-sizes`.
|
||||
|
||||
#![feature(start)]
|
||||
|
||||
struct S1 {
|
||||
x: u32,
|
||||
y: u32,
|
||||
|
@ -28,8 +26,7 @@ struct S5<TagW, TagZ> {
|
|||
tagz: TagZ,
|
||||
}
|
||||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
pub fn test() {
|
||||
let _s1: S1 = S1 { x: 0, y: 0, tag: () };
|
||||
|
||||
let _s5: S5<(), Empty> = S5 {
|
||||
|
@ -43,5 +40,4 @@ fn start(_: isize, _: *const *const u8) -> isize {
|
|||
z: 4,
|
||||
tagz: Empty {},
|
||||
};
|
||||
0
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue