Merge from rustc

This commit is contained in:
Ralf Jung 2023-06-18 20:05:58 +02:00
commit 7cef28657e
97 changed files with 1663 additions and 1297 deletions

139
.github/workflows/dependencies.yml vendored Normal file
View file

@ -0,0 +1,139 @@
# Automatically run `cargo update` periodically
---
name: Bump dependencies in Cargo.lock
on:
schedule:
# Run weekly
- cron: '0 0 * * Sun'
workflow_dispatch:
# Needed so we can run it manually
permissions:
contents: read
defaults:
run:
shell: bash
env:
# So cargo doesn't complain about unstable features
RUSTC_BOOTSTRAP: 1
PR_TITLE: Weekly `cargo update`
PR_MESSAGE: |
Automation to keep dependencies in `Cargo.lock` current.
The following is the output from `cargo update`:
COMMIT_MESSAGE: "cargo update \n\n"
jobs:
not-waiting-on-bors:
name: skip if S-waiting-on-bors
runs-on: ubuntu-latest
steps:
- env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Fetch state and labels of PR
# Or exit successfully if PR does not exist
JSON=$(gh pr view cargo_update --repo $GITHUB_REPOSITORY --json labels,state || exit 0)
STATE=$(echo "$JSON" | jq -r '.state')
WAITING_ON_BORS=$(echo "$JSON" | jq '.labels[] | any(.name == "S-waiting-on-bors"; .)')
# Exit with error if open and S-waiting-on-bors
if [[ "$STATE" == "OPEN" && "$WAITING_ON_BORS" == "true" ]]; then
exit 1
fi
update:
name: update dependencies
needs: not-waiting-on-bors
runs-on: ubuntu-latest
steps:
- name: checkout the source code
uses: actions/checkout@v3
with:
submodules: recursive
- name: install the bootstrap toolchain
run: |
# Extract the stage0 version
TOOLCHAIN=$(jq -r '.compiler | {version,date} | join("-")' -- src/stage0.json)
# Install and set as default
rustup toolchain install --no-self-update --profile minimal $TOOLCHAIN
rustup default $TOOLCHAIN
- name: cargo update
# Remove first line that always just says "Updating crates.io index"
run: cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: upload Cargo.lock artifact for use in PR
uses: actions/upload-artifact@v3
with:
name: Cargo-lock
path: Cargo.lock
retention-days: 1
- name: upload cargo-update log artifact for use in PR
uses: actions/upload-artifact@v3
with:
name: cargo-updates
path: cargo_update.log
retention-days: 1
pr:
name: amend PR
needs: update
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: checkout the source code
uses: actions/checkout@v3
- name: download Cargo.lock from update job
uses: actions/download-artifact@v3
with:
name: Cargo-lock
- name: download cargo-update log from update job
uses: actions/download-artifact@v3
with:
name: cargo-updates
- name: craft PR body and commit message
run: |
echo "${COMMIT_MESSAGE}" > commit.txt
cat cargo_update.log >> commit.txt
echo "${PR_MESSAGE}" > body.md
echo '```txt' >> body.md
cat cargo_update.log >> body.md
echo '```' >> body.md
- name: commit
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git switch --force-create cargo_update
git add ./Cargo.lock
git commit --no-verify --file=commit.txt
- name: push
run: git push --no-verify --force --set-upstream origin cargo_update
- name: edit existing open pull request
id: edit
# Don't fail job if we need to open new PR
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Exit with error if PR is closed
STATE=$(gh pr view cargo_update --repo $GITHUB_REPOSITORY --json state --jq '.state')
if [[ "$STATE" != "OPEN" ]]; then
exit 1
fi
gh pr edit cargo_update --title "${PR_TITLE}" --body-file body.md --repo $GITHUB_REPOSITORY
- name: open new pull request
# Only run if there wasn't an existing PR
if: steps.edit.outcome != 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr create --title "${PR_TITLE}" --body-file body.md --repo $GITHUB_REPOSITORY

View file

@ -330,7 +330,8 @@ fn check_opaque_type_well_formed<'tcx>(
// Require the hidden type to be well-formed with only the generics of the opaque type.
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
// hidden type is well formed even without those bounds.
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into()));
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(definition_ty.into())));
ocx.register_obligation(Obligation::misc(tcx, definition_span, def_id, param_env, predicate));
// Check that all obligations are satisfied by the implementation's

View file

@ -1419,9 +1419,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
//
// See #91068 for an example.
self.prove_predicates(
sig.inputs_and_output
.iter()
.map(|ty| ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))),
sig.inputs_and_output.iter().map(|ty| {
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(
ty.into(),
)))
}),
term_location.to_locations(),
ConstraintCategory::Boring,
);
@ -1850,7 +1852,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let array_ty = rvalue.ty(body.local_decls(), tcx);
self.prove_predicate(
ty::PredicateKind::WellFormed(array_ty.into()),
ty::PredicateKind::Clause(ty::Clause::WellFormed(array_ty.into())),
Locations::Single(location),
ConstraintCategory::Boring,
);

View file

@ -379,16 +379,12 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
}
let mut new_abis = Vec::new();
loop {
while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
match p.parse_str_lit() {
Ok(str_lit) => {
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
}
Err(opt_lit) => {
// If the non-string literal is a closing paren then it's the end of the list and is fine
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
break;
}
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
let mut err =
p.sess.span_diagnostic.struct_span_err(span, "expected string literal");

View file

@ -546,7 +546,8 @@ impl<T> Box<T> {
impl<T: ?Sized, A> Drop for Box<T, A> {
fn drop(&mut self) {
// drop is currently performed by compiler.
// inner value is dropped by compiler
libc::free(self.0.pointer.0 as *mut u8);
}
}
@ -563,11 +564,6 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
libc::malloc(size)
}
#[lang = "box_free"]
unsafe fn box_free<T: ?Sized>(ptr: Unique<T>, _alloc: ()) {
libc::free(ptr.pointer.0 as *mut u8);
}
#[lang = "drop"]
pub trait Drop {
fn drop(&mut self);

View file

@ -490,7 +490,8 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> fo
impl<T: ?Sized, A: Allocator> Drop for Box<T, A> {
fn drop(&mut self) {
// drop is currently performed by compiler.
// inner value is dropped by compiler
libc::free(self.pointer.0 as *mut u8);
}
}
@ -507,11 +508,6 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
libc::malloc(size)
}
#[lang = "box_free"]
unsafe fn box_free<T: ?Sized>(ptr: Unique<T>, _alloc: ()) {
libc::free(ptr.pointer.0 as *mut u8);
}
#[lang = "drop"]
pub trait Drop {
fn drop(&mut self);

View file

@ -14,7 +14,6 @@
use rustc_hir as hir;
use rustc_middle::mir;
use rustc_middle::mir::traversal::ReversePostorderIter;
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::InternalSubsts;
@ -53,9 +52,8 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
return;
}
let mut rpo = traversal::reverse_postorder(body);
let ccx = ConstCx::new(tcx, body);
let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo);
let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx);
let promotable_candidates = validate_candidates(&ccx, &mut temps, &all_candidates);
@ -166,14 +164,13 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
pub fn collect_temps_and_candidates<'tcx>(
ccx: &ConstCx<'_, 'tcx>,
rpo: &mut ReversePostorderIter<'_, 'tcx>,
) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
let mut collector = Collector {
temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls),
candidates: vec![],
ccx,
};
for (bb, data) in rpo {
for (bb, data) in traversal::reverse_postorder(ccx.body) {
collector.visit_basic_block_data(bb, data);
}
(collector.temps, collector.candidates)

View file

@ -250,7 +250,6 @@ language_item_table! {
FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;

View file

@ -0,0 +1,583 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_lint_defs::Applicability;
use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt};
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
use crate::astconv::{AstConv, ConvertedBinding, ConvertedBindingKind};
use crate::bounds::Bounds;
use crate::errors::{MultipleRelaxedDefaultBounds, ValueOfAssociatedStructAlreadySpecified};
use super::OnlySelfBounds;
impl<'tcx> dyn AstConv<'tcx> + '_ {
/// Sets `implicitly_sized` to true on `Bounds` if necessary
pub(crate) fn add_implicitly_sized(
&self,
bounds: &mut Bounds<'tcx>,
self_ty: Ty<'tcx>,
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) {
let tcx = self.tcx();
// Try to find an unbound in bounds.
let mut unbound = None;
let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
for ab in ast_bounds {
if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
if unbound.is_none() {
unbound = Some(&ptr.trait_ref);
} else {
tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
}
}
}
};
search_bounds(ast_bounds);
if let Some((self_ty, where_clause)) = self_ty_where_predicates {
for clause in where_clause {
if let hir::WherePredicate::BoundPredicate(pred) = clause {
if pred.is_param_bound(self_ty.to_def_id()) {
search_bounds(pred.bounds);
}
}
}
}
let sized_def_id = tcx.lang_items().sized_trait();
match (&sized_def_id, unbound) {
(Some(sized_def_id), Some(tpb))
if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) =>
{
// There was in fact a `?Sized` bound, return without doing anything
return;
}
(_, Some(_)) => {
// There was a `?Trait` bound, but it was not `?Sized`; warn.
tcx.sess.span_warn(
span,
"default bound relaxed for a type parameter, but \
this does nothing because the given bound is not \
a default; only `?Sized` is supported",
);
// Otherwise, add implicitly sized if `Sized` is available.
}
_ => {
// There was no `?Sized` bound; add implicitly sized if `Sized` is available.
}
}
if sized_def_id.is_none() {
// No lang item for `Sized`, so we can't add it as a bound.
return;
}
bounds.push_sized(tcx, self_ty, span);
}
/// This helper takes a *converted* parameter type (`param_ty`)
/// and an *unconverted* list of bounds:
///
/// ```text
/// fn foo<T: Debug>
/// ^ ^^^^^ `ast_bounds` parameter, in HIR form
/// |
/// `param_ty`, in ty form
/// ```
///
/// It adds these `ast_bounds` into the `bounds` structure.
///
/// **A note on binders:** there is an implied binder around
/// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
/// for more details.
#[instrument(level = "debug", skip(self, ast_bounds, bounds))]
pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
&self,
param_ty: Ty<'tcx>,
ast_bounds: I,
bounds: &mut Bounds<'tcx>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
only_self_bounds: OnlySelfBounds,
) {
for ast_bound in ast_bounds {
match ast_bound {
hir::GenericBound::Trait(poly_trait_ref, modifier) => {
let (constness, polarity) = match modifier {
hir::TraitBoundModifier::MaybeConst => {
(ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive)
}
hir::TraitBoundModifier::None => {
(ty::BoundConstness::NotConst, ty::ImplPolarity::Positive)
}
hir::TraitBoundModifier::Negative => {
(ty::BoundConstness::NotConst, ty::ImplPolarity::Negative)
}
hir::TraitBoundModifier::Maybe => continue,
};
let _ = self.instantiate_poly_trait_ref(
&poly_trait_ref.trait_ref,
poly_trait_ref.span,
constness,
polarity,
param_ty,
bounds,
false,
only_self_bounds,
);
}
&hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
self.instantiate_lang_item_trait_ref(
lang_item,
span,
hir_id,
args,
param_ty,
bounds,
only_self_bounds,
);
}
hir::GenericBound::Outlives(lifetime) => {
let region = self.ast_region_to_region(lifetime, None);
bounds.push_region_bound(
self.tcx(),
ty::Binder::bind_with_vars(
ty::OutlivesPredicate(param_ty, region),
bound_vars,
),
lifetime.ident.span,
);
}
}
}
}
/// Translates a list of bounds from the HIR into the `Bounds` data structure.
/// The self-type for the bounds is given by `param_ty`.
///
/// Example:
///
/// ```ignore (illustrative)
/// fn foo<T: Bar + Baz>() { }
/// // ^ ^^^^^^^^^ ast_bounds
/// // param_ty
/// ```
///
/// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
/// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
/// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
///
/// `span` should be the declaration size of the parameter.
pub(crate) fn compute_bounds(
&self,
param_ty: Ty<'tcx>,
ast_bounds: &[hir::GenericBound<'_>],
only_self_bounds: OnlySelfBounds,
) -> Bounds<'tcx> {
let mut bounds = Bounds::default();
self.add_bounds(
param_ty,
ast_bounds.iter(),
&mut bounds,
ty::List::empty(),
only_self_bounds,
);
debug!(?bounds);
bounds
}
/// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
/// named `assoc_name` into ty::Bounds. Ignore the rest.
pub(crate) fn compute_bounds_that_match_assoc_item(
&self,
param_ty: Ty<'tcx>,
ast_bounds: &[hir::GenericBound<'_>],
assoc_name: Ident,
) -> Bounds<'tcx> {
let mut result = Vec::new();
for ast_bound in ast_bounds {
if let Some(trait_ref) = ast_bound.trait_ref()
&& let Some(trait_did) = trait_ref.trait_def_id()
&& self.tcx().trait_may_define_assoc_item(trait_did, assoc_name)
{
result.push(ast_bound.clone());
}
}
let mut bounds = Bounds::default();
self.add_bounds(
param_ty,
result.iter(),
&mut bounds,
ty::List::empty(),
OnlySelfBounds(true),
);
debug!(?bounds);
bounds
}
/// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates
/// onto `bounds`.
///
/// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
/// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
/// the binder (e.g., `&'a u32`) and hence may reference bound regions.
#[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
pub(super) fn add_predicates_for_ast_type_binding(
&self,
hir_ref_id: hir::HirId,
trait_ref: ty::PolyTraitRef<'tcx>,
binding: &ConvertedBinding<'_, 'tcx>,
bounds: &mut Bounds<'tcx>,
speculative: bool,
dup_bindings: &mut FxHashMap<DefId, Span>,
path_span: Span,
constness: ty::BoundConstness,
only_self_bounds: OnlySelfBounds,
polarity: ty::ImplPolarity,
) -> Result<(), ErrorGuaranteed> {
// Given something like `U: SomeTrait<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
// subtle in the event that `T` is defined in a supertrait of
// `SomeTrait`, because in that case we need to upcast.
//
// That is, consider this case:
//
// ```
// trait SubTrait: SuperTrait<i32> { }
// trait SuperTrait<A> { type T; }
//
// ... B: SubTrait<T = foo> ...
// ```
//
// We want to produce `<B as SuperTrait<i32>>::T == foo`.
let tcx = self.tcx();
let return_type_notation =
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
let candidate = if return_type_notation {
if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Fn,
binding.item_name,
) {
trait_ref
} else {
self.one_bound_for_assoc_method(
traits::supertraits(tcx, trait_ref),
trait_ref.print_only_trait_path(),
binding.item_name,
path_span,
)?
}
} else if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Type,
binding.item_name,
) {
// Simple case: X is defined in the current trait.
trait_ref
} else {
// Otherwise, we have to walk through the supertraits to find
// those that do.
self.one_bound_for_assoc_type(
|| traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(),
binding.item_name,
path_span,
match binding.kind {
ConvertedBindingKind::Equality(term) => Some(term),
_ => None,
},
)?
};
let (assoc_ident, def_scope) =
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
let find_item_of_kind = |kind| {
tcx.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
};
let assoc_item = if return_type_notation {
find_item_of_kind(ty::AssocKind::Fn)
} else {
find_item_of_kind(ty::AssocKind::Type)
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
}
.expect("missing associated type");
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
tcx.sess
.struct_span_err(
binding.span,
format!("{} `{}` is private", assoc_item.kind, binding.item_name),
)
.span_label(binding.span, format!("private {}", assoc_item.kind))
.emit();
}
tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
if !speculative {
dup_bindings
.entry(assoc_item.def_id)
.and_modify(|prev_span| {
tcx.sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
span: binding.span,
prev_span: *prev_span,
item_name: binding.item_name,
def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
});
})
.or_insert(binding.span);
}
let projection_ty = if return_type_notation {
let mut emitted_bad_param_err = false;
// If we have an method return type bound, then we need to substitute
// the method's early bound params with suitable late-bound params.
let mut num_bound_vars = candidate.bound_vars().len();
let substs =
candidate.skip_binder().substs.extend_to(tcx, assoc_item.def_id, |param, _| {
let subst = match param.kind {
ty::GenericParamDefKind::Lifetime => ty::Region::new_late_bound(
tcx,
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(num_bound_vars),
kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name),
},
)
.into(),
ty::GenericParamDefKind::Type { .. } => {
if !emitted_bad_param_err {
tcx.sess.emit_err(
crate::errors::ReturnTypeNotationIllegalParam::Type {
span: path_span,
param_span: tcx.def_span(param.def_id),
},
);
emitted_bad_param_err = true;
}
tcx.mk_bound(
ty::INNERMOST,
ty::BoundTy {
var: ty::BoundVar::from_usize(num_bound_vars),
kind: ty::BoundTyKind::Param(param.def_id, param.name),
},
)
.into()
}
ty::GenericParamDefKind::Const { .. } => {
if !emitted_bad_param_err {
tcx.sess.emit_err(
crate::errors::ReturnTypeNotationIllegalParam::Const {
span: path_span,
param_span: tcx.def_span(param.def_id),
},
);
emitted_bad_param_err = true;
}
let ty = tcx
.type_of(param.def_id)
.no_bound_vars()
.expect("ct params cannot have early bound vars");
tcx.mk_const(
ty::ConstKind::Bound(
ty::INNERMOST,
ty::BoundVar::from_usize(num_bound_vars),
),
ty,
)
.into()
}
};
num_bound_vars += 1;
subst
});
// Next, we need to check that the return-type notation is being used on
// an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output();
let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind()
&& tcx.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder
{
alias_ty
} else {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationOnNonRpitit {
span: binding.span,
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
note: (),
},
));
};
// Finally, move the fn return type's bound vars over to account for the early bound
// params (and trait ref's late bound params). This logic is very similar to
// `Predicate::subst_supertrait`, and it's no coincidence why.
let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output);
let subst_output = ty::EarlyBinder::bind(shifted_output).subst(tcx, substs);
let bound_vars = tcx.late_bound_vars(binding.hir_id);
ty::Binder::bind_with_vars(subst_output, bound_vars)
} else {
// Include substitutions for generic parameters of associated types
candidate.map_bound(|trait_ref| {
let ident = Ident::new(assoc_item.name, binding.item_name.span);
let item_segment = hir::PathSegment {
ident,
hir_id: binding.hir_id,
res: Res::Err,
args: Some(binding.gen_args),
infer_args: false,
};
let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
path_span,
assoc_item.def_id,
&item_segment,
trait_ref.substs,
);
debug!(?substs_trait_ref_and_assoc_item);
tcx.mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item)
})
};
if !speculative {
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
if let ConvertedBindingKind::Equality(ty) = binding.kind {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
self.validate_late_bound_regions(
late_bound_in_trait_ref,
late_bound_in_ty,
|br_name| {
struct_span_err!(
tcx.sess,
binding.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
binding.item_name,
br_name
)
},
);
}
}
match binding.kind {
ConvertedBindingKind::Equality(..) if return_type_notation => {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
));
}
ConvertedBindingKind::Equality(mut term) => {
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
// the "projection predicate" for:
//
// `<T as Iterator>::Item = u32`
let assoc_item_def_id = projection_ty.skip_binder().def_id;
let def_kind = tcx.def_kind(assoc_item_def_id);
match (def_kind, term.unpack()) {
(hir::def::DefKind::AssocTy, ty::TermKind::Ty(_))
| (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (),
(_, _) => {
let got = if let Some(_) = term.ty() { "type" } else { "constant" };
let expected = tcx.def_descr(assoc_item_def_id);
let mut err = tcx.sess.struct_span_err(
binding.span,
format!("expected {expected} bound, found {got}"),
);
err.span_note(
tcx.def_span(assoc_item_def_id),
format!("{expected} defined here"),
);
if let hir::def::DefKind::AssocConst = def_kind
&& let Some(t) = term.ty() && (t.is_enum() || t.references_error())
&& tcx.features().associated_const_equality {
err.span_suggestion(
binding.span,
"if equating a const, try wrapping with braces",
format!("{} = {{ const }}", binding.item_name),
Applicability::HasPlaceholders,
);
}
let reported = err.emit();
term = match def_kind {
hir::def::DefKind::AssocTy => tcx.ty_error(reported).into(),
hir::def::DefKind::AssocConst => tcx
.const_error(
tcx.type_of(assoc_item_def_id)
.subst(tcx, projection_ty.skip_binder().substs),
reported,
)
.into(),
_ => unreachable!(),
};
}
}
bounds.push_projection_bound(
tcx,
projection_ty
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
binding.span,
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
//
// `<T as Iterator>::Item: Debug`
//
// Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
// parameter to have a skipped binder.
//
// NOTE: If `only_self_bounds` is true, do NOT expand this associated
// type bound into a trait predicate, since we only want to add predicates
// for the `Self` type.
if !only_self_bounds.0 {
let param_ty = tcx.mk_alias(ty::Projection, projection_ty.skip_binder());
self.add_bounds(
param_ty,
ast_bounds.iter(),
bounds,
projection_ty.bound_vars(),
only_self_bounds,
);
}
}
}
Ok(())
}
}

View file

@ -2,6 +2,7 @@
//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an
//! instance of `AstConv`.
mod bounds;
mod errors;
pub mod generics;
mod lint;
@ -11,8 +12,7 @@ use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generi
use crate::bounds::Bounds;
use crate::collect::HirPlaceholderCollector;
use crate::errors::{
AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
AmbiguousLifetimeBound, TraitObjectDeclaredWithNoTraits, TypeofReservedKeywordUsed,
};
use crate::middle::resolve_bound_vars as rbv;
use crate::require_c_abi_if_c_variadic;
@ -31,9 +31,10 @@ use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::DynKind;
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{DynKind, ToPredicate};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
@ -885,571 +886,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.is_some()
}
/// Sets `implicitly_sized` to true on `Bounds` if necessary
pub(crate) fn add_implicitly_sized(
&self,
bounds: &mut Bounds<'tcx>,
self_ty: Ty<'tcx>,
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) {
let tcx = self.tcx();
// Try to find an unbound in bounds.
let mut unbound = None;
let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
for ab in ast_bounds {
if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
if unbound.is_none() {
unbound = Some(&ptr.trait_ref);
} else {
tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
}
}
}
};
search_bounds(ast_bounds);
if let Some((self_ty, where_clause)) = self_ty_where_predicates {
for clause in where_clause {
if let hir::WherePredicate::BoundPredicate(pred) = clause {
if pred.is_param_bound(self_ty.to_def_id()) {
search_bounds(pred.bounds);
}
}
}
}
let sized_def_id = tcx.lang_items().sized_trait();
match (&sized_def_id, unbound) {
(Some(sized_def_id), Some(tpb))
if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) =>
{
// There was in fact a `?Sized` bound, return without doing anything
return;
}
(_, Some(_)) => {
// There was a `?Trait` bound, but it was not `?Sized`; warn.
tcx.sess.span_warn(
span,
"default bound relaxed for a type parameter, but \
this does nothing because the given bound is not \
a default; only `?Sized` is supported",
);
// Otherwise, add implicitly sized if `Sized` is available.
}
_ => {
// There was no `?Sized` bound; add implicitly sized if `Sized` is available.
}
}
if sized_def_id.is_none() {
// No lang item for `Sized`, so we can't add it as a bound.
return;
}
bounds.push_sized(tcx, self_ty, span);
}
/// This helper takes a *converted* parameter type (`param_ty`)
/// and an *unconverted* list of bounds:
///
/// ```text
/// fn foo<T: Debug>
/// ^ ^^^^^ `ast_bounds` parameter, in HIR form
/// |
/// `param_ty`, in ty form
/// ```
///
/// It adds these `ast_bounds` into the `bounds` structure.
///
/// **A note on binders:** there is an implied binder around
/// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
/// for more details.
#[instrument(level = "debug", skip(self, ast_bounds, bounds))]
pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
&self,
param_ty: Ty<'tcx>,
ast_bounds: I,
bounds: &mut Bounds<'tcx>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
only_self_bounds: OnlySelfBounds,
) {
for ast_bound in ast_bounds {
match ast_bound {
hir::GenericBound::Trait(poly_trait_ref, modifier) => {
let (constness, polarity) = match modifier {
hir::TraitBoundModifier::MaybeConst => {
(ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive)
}
hir::TraitBoundModifier::None => {
(ty::BoundConstness::NotConst, ty::ImplPolarity::Positive)
}
hir::TraitBoundModifier::Negative => {
(ty::BoundConstness::NotConst, ty::ImplPolarity::Negative)
}
hir::TraitBoundModifier::Maybe => continue,
};
let _ = self.instantiate_poly_trait_ref(
&poly_trait_ref.trait_ref,
poly_trait_ref.span,
constness,
polarity,
param_ty,
bounds,
false,
only_self_bounds,
);
}
&hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
self.instantiate_lang_item_trait_ref(
lang_item,
span,
hir_id,
args,
param_ty,
bounds,
only_self_bounds,
);
}
hir::GenericBound::Outlives(lifetime) => {
let region = self.ast_region_to_region(lifetime, None);
bounds.push_region_bound(
self.tcx(),
ty::Binder::bind_with_vars(
ty::OutlivesPredicate(param_ty, region),
bound_vars,
),
lifetime.ident.span,
);
}
}
}
}
/// Translates a list of bounds from the HIR into the `Bounds` data structure.
/// The self-type for the bounds is given by `param_ty`.
///
/// Example:
///
/// ```ignore (illustrative)
/// fn foo<T: Bar + Baz>() { }
/// // ^ ^^^^^^^^^ ast_bounds
/// // param_ty
/// ```
///
/// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
/// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
/// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
///
/// `span` should be the declaration size of the parameter.
pub(crate) fn compute_bounds(
&self,
param_ty: Ty<'tcx>,
ast_bounds: &[hir::GenericBound<'_>],
only_self_bounds: OnlySelfBounds,
) -> Bounds<'tcx> {
let mut bounds = Bounds::default();
self.add_bounds(
param_ty,
ast_bounds.iter(),
&mut bounds,
ty::List::empty(),
only_self_bounds,
);
debug!(?bounds);
bounds
}
/// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
/// named `assoc_name` into ty::Bounds. Ignore the rest.
pub(crate) fn compute_bounds_that_match_assoc_item(
&self,
param_ty: Ty<'tcx>,
ast_bounds: &[hir::GenericBound<'_>],
assoc_name: Ident,
) -> Bounds<'tcx> {
let mut result = Vec::new();
for ast_bound in ast_bounds {
if let Some(trait_ref) = ast_bound.trait_ref()
&& let Some(trait_did) = trait_ref.trait_def_id()
&& self.tcx().trait_may_define_assoc_item(trait_did, assoc_name)
{
result.push(ast_bound.clone());
}
}
let mut bounds = Bounds::default();
self.add_bounds(
param_ty,
result.iter(),
&mut bounds,
ty::List::empty(),
OnlySelfBounds(true),
);
debug!(?bounds);
bounds
}
/// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates
/// onto `bounds`.
///
/// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
/// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
/// the binder (e.g., `&'a u32`) and hence may reference bound regions.
#[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
fn add_predicates_for_ast_type_binding(
&self,
hir_ref_id: hir::HirId,
trait_ref: ty::PolyTraitRef<'tcx>,
binding: &ConvertedBinding<'_, 'tcx>,
bounds: &mut Bounds<'tcx>,
speculative: bool,
dup_bindings: &mut FxHashMap<DefId, Span>,
path_span: Span,
constness: ty::BoundConstness,
only_self_bounds: OnlySelfBounds,
polarity: ty::ImplPolarity,
) -> Result<(), ErrorGuaranteed> {
// Given something like `U: SomeTrait<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
// subtle in the event that `T` is defined in a supertrait of
// `SomeTrait`, because in that case we need to upcast.
//
// That is, consider this case:
//
// ```
// trait SubTrait: SuperTrait<i32> { }
// trait SuperTrait<A> { type T; }
//
// ... B: SubTrait<T = foo> ...
// ```
//
// We want to produce `<B as SuperTrait<i32>>::T == foo`.
let tcx = self.tcx();
let return_type_notation =
binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
let candidate = if return_type_notation {
if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Fn,
binding.item_name,
) {
trait_ref
} else {
self.one_bound_for_assoc_method(
traits::supertraits(tcx, trait_ref),
trait_ref.print_only_trait_path(),
binding.item_name,
path_span,
)?
}
} else if self.trait_defines_associated_item_named(
trait_ref.def_id(),
ty::AssocKind::Type,
binding.item_name,
) {
// Simple case: X is defined in the current trait.
trait_ref
} else {
// Otherwise, we have to walk through the supertraits to find
// those that do.
self.one_bound_for_assoc_type(
|| traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(),
binding.item_name,
path_span,
match binding.kind {
ConvertedBindingKind::Equality(term) => Some(term),
_ => None,
},
)?
};
let (assoc_ident, def_scope) =
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
let find_item_of_kind = |kind| {
tcx.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
};
let assoc_item = if return_type_notation {
find_item_of_kind(ty::AssocKind::Fn)
} else {
find_item_of_kind(ty::AssocKind::Type)
.or_else(|| find_item_of_kind(ty::AssocKind::Const))
}
.expect("missing associated type");
if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
tcx.sess
.struct_span_err(
binding.span,
format!("{} `{}` is private", assoc_item.kind, binding.item_name),
)
.span_label(binding.span, format!("private {}", assoc_item.kind))
.emit();
}
tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
if !speculative {
dup_bindings
.entry(assoc_item.def_id)
.and_modify(|prev_span| {
tcx.sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
span: binding.span,
prev_span: *prev_span,
item_name: binding.item_name,
def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
});
})
.or_insert(binding.span);
}
let projection_ty = if return_type_notation {
let mut emitted_bad_param_err = false;
// If we have an method return type bound, then we need to substitute
// the method's early bound params with suitable late-bound params.
let mut num_bound_vars = candidate.bound_vars().len();
let substs =
candidate.skip_binder().substs.extend_to(tcx, assoc_item.def_id, |param, _| {
let subst = match param.kind {
GenericParamDefKind::Lifetime => ty::Region::new_late_bound(
tcx,
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(num_bound_vars),
kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name),
},
)
.into(),
GenericParamDefKind::Type { .. } => {
if !emitted_bad_param_err {
tcx.sess.emit_err(
crate::errors::ReturnTypeNotationIllegalParam::Type {
span: path_span,
param_span: tcx.def_span(param.def_id),
},
);
emitted_bad_param_err = true;
}
tcx.mk_bound(
ty::INNERMOST,
ty::BoundTy {
var: ty::BoundVar::from_usize(num_bound_vars),
kind: ty::BoundTyKind::Param(param.def_id, param.name),
},
)
.into()
}
GenericParamDefKind::Const { .. } => {
if !emitted_bad_param_err {
tcx.sess.emit_err(
crate::errors::ReturnTypeNotationIllegalParam::Const {
span: path_span,
param_span: tcx.def_span(param.def_id),
},
);
emitted_bad_param_err = true;
}
let ty = tcx
.type_of(param.def_id)
.no_bound_vars()
.expect("ct params cannot have early bound vars");
tcx.mk_const(
ty::ConstKind::Bound(
ty::INNERMOST,
ty::BoundVar::from_usize(num_bound_vars),
),
ty,
)
.into()
}
};
num_bound_vars += 1;
subst
});
// Next, we need to check that the return-type notation is being used on
// an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output();
let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind()
&& tcx.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder
{
alias_ty
} else {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationOnNonRpitit {
span: binding.span,
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
note: (),
},
));
};
// Finally, move the fn return type's bound vars over to account for the early bound
// params (and trait ref's late bound params). This logic is very similar to
// `Predicate::subst_supertrait`, and it's no coincidence why.
let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output);
let subst_output = ty::EarlyBinder::bind(shifted_output).subst(tcx, substs);
let bound_vars = tcx.late_bound_vars(binding.hir_id);
ty::Binder::bind_with_vars(subst_output, bound_vars)
} else {
// Include substitutions for generic parameters of associated types
candidate.map_bound(|trait_ref| {
let ident = Ident::new(assoc_item.name, binding.item_name.span);
let item_segment = hir::PathSegment {
ident,
hir_id: binding.hir_id,
res: Res::Err,
args: Some(binding.gen_args),
infer_args: false,
};
let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
path_span,
assoc_item.def_id,
&item_segment,
trait_ref.substs,
);
debug!(?substs_trait_ref_and_assoc_item);
tcx.mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item)
})
};
if !speculative {
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
if let ConvertedBindingKind::Equality(ty) = binding.kind {
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&projection_ty);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
self.validate_late_bound_regions(
late_bound_in_trait_ref,
late_bound_in_ty,
|br_name| {
struct_span_err!(
tcx.sess,
binding.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
binding.item_name,
br_name
)
},
);
}
}
match binding.kind {
ConvertedBindingKind::Equality(..) if return_type_notation => {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
));
}
ConvertedBindingKind::Equality(mut term) => {
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
// the "projection predicate" for:
//
// `<T as Iterator>::Item = u32`
let assoc_item_def_id = projection_ty.skip_binder().def_id;
let def_kind = tcx.def_kind(assoc_item_def_id);
match (def_kind, term.unpack()) {
(hir::def::DefKind::AssocTy, ty::TermKind::Ty(_))
| (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (),
(_, _) => {
let got = if let Some(_) = term.ty() { "type" } else { "constant" };
let expected = tcx.def_descr(assoc_item_def_id);
let mut err = tcx.sess.struct_span_err(
binding.span,
format!("expected {expected} bound, found {got}"),
);
err.span_note(
tcx.def_span(assoc_item_def_id),
format!("{expected} defined here"),
);
if let hir::def::DefKind::AssocConst = def_kind
&& let Some(t) = term.ty() && (t.is_enum() || t.references_error())
&& tcx.features().associated_const_equality {
err.span_suggestion(
binding.span,
"if equating a const, try wrapping with braces",
format!("{} = {{ const }}", binding.item_name),
Applicability::HasPlaceholders,
);
}
let reported = err.emit();
term = match def_kind {
hir::def::DefKind::AssocTy => tcx.ty_error(reported).into(),
hir::def::DefKind::AssocConst => tcx
.const_error(
tcx.type_of(assoc_item_def_id)
.subst(tcx, projection_ty.skip_binder().substs),
reported,
)
.into(),
_ => unreachable!(),
};
}
}
bounds.push_projection_bound(
tcx,
projection_ty
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
binding.span,
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
//
// `<T as Iterator>::Item: Debug`
//
// Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
// parameter to have a skipped binder.
//
// NOTE: If `only_self_bounds` is true, do NOT expand this associated
// type bound into a trait predicate, since we only want to add predicates
// for the `Self` type.
if !only_self_bounds.0 {
let param_ty = tcx.mk_alias(ty::Projection, projection_ty.skip_binder());
self.add_bounds(
param_ty,
ast_bounds.iter(),
bounds,
projection_ty.bound_vars(),
only_self_bounds,
);
}
}
}
Ok(())
}
fn ast_path_to_ty(
&self,
span: Span,
@ -1509,7 +945,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut trait_bounds = vec![];
let mut projection_bounds = vec![];
for (pred, span) in bounds.predicates() {
for (clause, span) in bounds.predicates() {
let pred: ty::Predicate<'tcx> = clause.to_predicate(tcx);
let bound_pred = pred.kind();
match bound_pred.skip_binder() {
ty::PredicateKind::Clause(clause) => match clause {
@ -1527,15 +964,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
ty::Clause::TypeOutlives(_) => {
// Do nothing, we deal with regions separately
}
ty::Clause::RegionOutlives(_) | ty::Clause::ConstArgHasType(..) => bug!(),
ty::Clause::RegionOutlives(_)
| ty::Clause::ConstArgHasType(..)
| ty::Clause::WellFormed(_)
| ty::Clause::ConstEvaluatable(_) => bug!(),
},
ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::AliasRelate(..)
ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => bug!(),

View file

@ -2,6 +2,7 @@
//! `ty` form from the HIR.
use rustc_hir::LangItem;
use rustc_middle::ty::Binder;
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_span::Span;
@ -23,52 +24,58 @@ use rustc_span::Span;
/// include the self type (e.g., `trait_bounds`) but in others we do not
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct Bounds<'tcx> {
pub predicates: Vec<(ty::Predicate<'tcx>, Span)>,
pub predicates: Vec<(Binder<'tcx, ty::Clause<'tcx>>, Span)>,
}
impl<'tcx> Bounds<'tcx> {
pub fn push_region_bound(
&mut self,
tcx: TyCtxt<'tcx>,
_tcx: TyCtxt<'tcx>,
region: ty::PolyTypeOutlivesPredicate<'tcx>,
span: Span,
) {
self.predicates.push((region.to_predicate(tcx), span));
self.predicates.push((region.map_bound(|p| ty::Clause::TypeOutlives(p)), span));
}
pub fn push_trait_bound(
&mut self,
tcx: TyCtxt<'tcx>,
_tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
constness: ty::BoundConstness,
polarity: ty::ImplPolarity,
) {
self.predicates.push((
trait_ref
.map_bound(|trait_ref| ty::TraitPredicate { trait_ref, constness, polarity })
.to_predicate(tcx),
trait_ref.map_bound(|trait_ref| {
ty::Clause::Trait(ty::TraitPredicate { trait_ref, constness, polarity })
}),
span,
));
}
pub fn push_projection_bound(
&mut self,
tcx: TyCtxt<'tcx>,
_tcx: TyCtxt<'tcx>,
projection: ty::PolyProjectionPredicate<'tcx>,
span: Span,
) {
self.predicates.push((projection.to_predicate(tcx), span));
self.predicates.push((projection.map_bound(|proj| ty::Clause::Projection(proj)), span));
}
pub fn push_sized(&mut self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) {
let sized_def_id = tcx.require_lang_item(LangItem::Sized, Some(span));
let trait_ref = ty::TraitRef::new(tcx, sized_def_id, [ty]);
// Preferable to put this obligation first, since we report better errors for sized ambiguity.
self.predicates.insert(0, (trait_ref.without_const().to_predicate(tcx), span));
self.predicates.insert(
0,
(
ty::Binder::dummy(ty::Clause::Trait(trait_ref.without_const().to_predicate(tcx))),
span,
),
);
}
pub fn predicates(&self) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + '_ {
pub fn predicates(&self) -> impl Iterator<Item = (Binder<'tcx, ty::Clause<'tcx>>, Span)> + '_ {
self.predicates.iter().cloned()
}
}

View file

@ -439,7 +439,8 @@ fn check_opaque_meets_bounds<'tcx>(
// Additionally require the hidden type to be well-formed with only the generics of the opaque type.
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
// hidden type is well formed even without those bounds.
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into()));
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(hidden_ty.into())));
ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate));
// Check that all obligations are satisfied by the implementation's

View file

@ -321,7 +321,9 @@ fn compare_method_predicate_entailment<'tcx>(
infcx.tcx,
ObligationCause::dummy(),
param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(
unnormalized_impl_fty.into(),
))),
));
}

View file

@ -81,7 +81,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
self.tcx(),
cause,
param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(arg))),
));
}
}
@ -1032,9 +1032,9 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
tcx,
cause,
wfcx.param_env,
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(
ty::Const::from_anon_const(tcx, discr_def_id.expect_local()),
)),
))),
));
}
}
@ -1876,7 +1876,8 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
// We lower empty bounds like `Vec<dyn Copy>:` as
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
// regular WF checking
if let ty::PredicateKind::WellFormed(..) = pred.kind().skip_binder() {
if let ty::PredicateKind::Clause(ty::Clause::WellFormed(..)) = pred.kind().skip_binder()
{
continue;
}
// Match the existing behavior.

View file

@ -3,6 +3,7 @@ use crate::astconv::{AstConv, OnlySelfBounds};
use rustc_hir as hir;
use rustc_infer::traits::util;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::Span;
@ -44,7 +45,12 @@ fn associated_type_bounds<'tcx>(
}
});
let all_bounds = tcx.arena.alloc_from_iter(bounds.predicates().chain(bounds_from_parent));
let all_bounds = tcx.arena.alloc_from_iter(
bounds
.predicates()
.map(|(clause, span)| (clause.to_predicate(tcx), span))
.chain(bounds_from_parent),
);
debug!(
"associated_type_bounds({}) = {:?}",
tcx.def_path_str(assoc_item_def_id.to_def_id()),
@ -72,7 +78,9 @@ fn opaque_type_bounds<'tcx>(
icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span);
debug!(?bounds);
tcx.arena.alloc_from_iter(bounds.predicates())
tcx.arena.alloc_from_iter(
bounds.predicates().map(|(clause, span)| (clause.to_predicate(tcx), span)),
)
})
}

View file

@ -126,7 +126,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
predicates.extend(
icx.astconv()
.compute_bounds(tcx.types.self_param, self_bounds, OnlySelfBounds(false))
.predicates(),
.predicates()
.map(|(clause, span)| (clause.to_predicate(tcx), span)),
);
}
@ -175,7 +176,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
param.span,
);
trace!(?bounds);
predicates.extend(bounds.predicates());
predicates.extend(
bounds.predicates().map(|(clause, span)| (clause.to_predicate(tcx), span)),
);
trace!(?predicates);
}
GenericParamKind::Const { .. } => {
@ -219,7 +222,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
} else {
let span = bound_pred.bounded_ty.span;
let predicate = ty::Binder::bind_with_vars(
ty::PredicateKind::WellFormed(ty.into()),
ty::PredicateKind::Clause(ty::Clause::WellFormed(ty.into())),
bound_vars,
);
predicates.insert((predicate.to_predicate(tcx), span));
@ -234,7 +237,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
bound_vars,
OnlySelfBounds(false),
);
predicates.extend(bounds.predicates());
predicates.extend(
bounds.predicates().map(|(clause, span)| (clause.to_predicate(tcx), span)),
);
}
hir::WherePredicate::RegionPredicate(region_pred) => {
@ -353,7 +358,7 @@ fn const_evaluatable_predicates_of(
if let ty::ConstKind::Unevaluated(_) = ct.kind() {
let span = self.tcx.def_span(c.def_id);
self.preds.insert((
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct))
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(ct)))
.to_predicate(self.tcx),
span,
));
@ -658,8 +663,12 @@ pub(super) fn implied_predicates_with_filter(
};
// Combine the two lists to form the complete set of superbounds:
let implied_bounds =
&*tcx.arena.alloc_from_iter(superbounds.predicates().chain(where_bounds_that_match));
let implied_bounds = &*tcx.arena.alloc_from_iter(
superbounds
.predicates()
.map(|(clause, span)| (clause.to_predicate(tcx), span))
.chain(where_bounds_that_match),
);
debug!(?implied_bounds);
// Now require that immediate supertraits are converted, which will, in
@ -816,7 +825,7 @@ impl<'tcx> ItemCtxt<'tcx> {
);
}
bounds.predicates().collect()
bounds.predicates().map(|(clause, span)| (clause.to_predicate(self.tcx), span)).collect()
}
#[instrument(level = "trace", skip(self))]

View file

@ -79,7 +79,7 @@ fn diagnostic_hir_wf_check<'tcx>(
self.tcx,
cause,
self.param_env,
ty::PredicateKind::WellFormed(tcx_ty.into()),
ty::PredicateKind::Clause(ty::Clause::WellFormed(tcx_ty.into())),
));
for error in ocx.select_all_or_error() {

View file

@ -542,12 +542,12 @@ fn trait_predicate_kind<'tcx>(
| ty::PredicateKind::Clause(ty::Clause::Projection(_))
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(_))
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,

View file

@ -55,13 +55,13 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
ty::PredicateKind::Clause(ty::Clause::Trait(..))
| ty::PredicateKind::Clause(ty::Clause::Projection(..))
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => (),

View file

@ -483,7 +483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
cause,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(arg))),
));
}
@ -668,10 +668,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where

View file

@ -32,7 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(arg, ty)) => {
vec![ty.into(), arg.into()]
}
ty::PredicateKind::ConstEvaluatable(e) => vec![e.into()],
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(e)) => vec![e.into()],
_ => return false,
};

View file

@ -452,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
tcx,
obligation.cause,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(method_ty.into()))),
));
let callee = MethodCallee { def_id, substs, sig: fn_sig };

View file

@ -838,11 +838,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::Clause::Projection(..))
| ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasRelate(..)

View file

@ -696,7 +696,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
// Don't point out the span of `WellFormed` predicates.
if !matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) {
if !matches!(
p.kind().skip_binder(),
ty::PredicateKind::Clause(ty::Clause::Projection(..) | ty::Clause::Trait(..))
) {
continue;
};

View file

@ -417,7 +417,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
self.tcx(),
self.trace.cause.clone(),
self.param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(b_ty.into())),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(b_ty.into()))),
));
}

View file

@ -29,11 +29,11 @@ pub fn explicit_outlives_bounds<'tcx>(
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,

View file

@ -227,7 +227,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
debug!(?data, ?obligations, "super_predicates");
self.extend_deduped(obligations);
}
ty::PredicateKind::WellFormed(..) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(..)) => {
// Currently, we do not elaborate WF predicates,
// although we easily could.
}
@ -249,7 +249,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
ty::PredicateKind::ClosureKind(..) => {
// Nothing to elaborate when waiting for a closure's kind to be inferred.
}
ty::PredicateKind::ConstEvaluatable(..) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..)) => {
// Currently, we do not elaborate const-evaluatable
// predicates.
}

View file

@ -1610,13 +1610,13 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
Clause(Clause::Projection(..)) |
AliasRelate(..) |
// Ignore bounds that a user can't type
WellFormed(..) |
Clause(Clause::WellFormed(..)) |
// FIXME(generic_const_exprs): `ConstEvaluatable` can be written
Clause(Clause::ConstEvaluatable(..)) |
ObjectSafe(..) |
ClosureKind(..) |
Subtype(..) |
Coerce(..) |
// FIXME(generic_const_exprs): `ConstEvaluatable` can be written
ConstEvaluatable(..) |
ConstEquate(..) |
Ambiguous |
TypeWellFormedFromEnv(..) => continue,

View file

@ -26,7 +26,7 @@ struct Cache {
predecessors: OnceCell<Predecessors>,
switch_sources: OnceCell<SwitchSources>,
is_cyclic: OnceCell<bool>,
postorder: OnceCell<Vec<BasicBlock>>,
reverse_postorder: OnceCell<Vec<BasicBlock>>,
dominators: OnceCell<Dominators<BasicBlock>>,
}
@ -62,11 +62,14 @@ impl<'tcx> BasicBlocks<'tcx> {
})
}
/// Returns basic blocks in a postorder.
/// Returns basic blocks in a reverse postorder.
#[inline]
pub fn postorder(&self) -> &[BasicBlock] {
self.cache.postorder.get_or_init(|| {
Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect()
pub fn reverse_postorder(&self) -> &[BasicBlock] {
self.cache.reverse_postorder.get_or_init(|| {
let mut rpo: Vec<_> =
Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect();
rpo.reverse();
rpo
})
}

View file

@ -145,7 +145,7 @@ impl MirPhase {
}
"analysis" => Self::Analysis(AnalysisPhase::parse(phase)),
"runtime" => Self::Runtime(RuntimePhase::parse(phase)),
_ => panic!("Unknown MIR dialect {}", dialect),
_ => bug!("Unknown MIR dialect: '{}'", dialect),
}
}
}
@ -159,7 +159,7 @@ impl AnalysisPhase {
match &*phase.to_ascii_lowercase() {
"initial" => Self::Initial,
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
_ => panic!("Unknown analysis phase {}", phase),
_ => bug!("Unknown analysis phase: '{}'", phase),
}
}
}
@ -174,7 +174,7 @@ impl RuntimePhase {
"initial" => Self::Initial,
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
"optimized" => Self::Optimized,
_ => panic!("Unknown runtime phase {}", phase),
_ => bug!("Unknown runtime phase: '{}'", phase),
}
}
}

View file

@ -15,8 +15,9 @@ const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT B
const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
const HEADER: &str = r#"<!DOCTYPE html>
<html>
<head>"#;
<html lang="en">
<head>
<meta charset="utf-8">"#;
const START_BODY: &str = r#"</head>
<body>"#;
const FOOTER: &str = r#"</body>

View file

@ -188,10 +188,6 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
}
}
pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> {
Postorder::new(&body.basic_blocks, START_BLOCK)
}
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
@ -219,6 +215,17 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
}
}
/// Creates an iterator over the `Body`'s basic blocks, that:
/// - returns basic blocks in a postorder,
/// - traverses the `BasicBlocks` CFG cache's reverse postorder backwards, and does not cache the
/// postorder itself.
pub fn postorder<'a, 'tcx>(
body: &'a Body<'tcx>,
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
{
reverse_postorder(body).rev()
}
/// Reverse postorder traversal of a graph
///
/// Reverse postorder is the reverse order of a postorder traversal.
@ -295,34 +302,12 @@ pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
iter.visited
}
#[derive(Clone)]
pub struct ReversePostorderIter<'a, 'tcx> {
/// Creates an iterator over the `Body`'s basic blocks, that:
/// - returns basic blocks in a reverse postorder,
/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
pub fn reverse_postorder<'a, 'tcx>(
body: &'a Body<'tcx>,
blocks: &'a [BasicBlock],
idx: usize,
}
impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
if self.idx == 0 {
return None;
}
self.idx -= 1;
self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.idx, Some(self.idx))
}
}
impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
let blocks = body.basic_blocks.postorder();
let len = blocks.len();
ReversePostorderIter { body, blocks, idx: len }
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
{
body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
}

View file

@ -270,14 +270,14 @@ impl FlagComputation {
self.add_alias_ty(projection_ty);
self.add_term(term);
}
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
self.add_substs(slice::from_ref(&arg));
}
ty::PredicateKind::ObjectSafe(_def_id) => {}
ty::PredicateKind::ClosureKind(_def_id, substs, _kind) => {
self.add_substs(substs);
}
ty::PredicateKind::ConstEvaluatable(uv) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(uv)) => {
self.add_const(uv);
}
ty::PredicateKind::ConstEquate(expected, found) => {

View file

@ -523,7 +523,7 @@ impl<'tcx> Predicate<'tcx> {
ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
tcx.trait_is_coinductive(data.def_id())
}
ty::PredicateKind::WellFormed(_) => true,
ty::PredicateKind::Clause(ty::Clause::WellFormed(_)) => true,
_ => false,
}
}
@ -536,7 +536,7 @@ impl<'tcx> Predicate<'tcx> {
#[inline]
pub fn allow_normalization(self) -> bool {
match self.kind().skip_binder() {
PredicateKind::WellFormed(_) => false,
PredicateKind::Clause(Clause::WellFormed(_)) => false,
PredicateKind::Clause(Clause::Trait(_))
| PredicateKind::Clause(Clause::RegionOutlives(_))
| PredicateKind::Clause(Clause::TypeOutlives(_))
@ -547,7 +547,7 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::ClosureKind(_, _, _)
| PredicateKind::Subtype(_)
| PredicateKind::Coerce(_)
| PredicateKind::ConstEvaluatable(_)
| PredicateKind::Clause(Clause::ConstEvaluatable(_))
| PredicateKind::ConstEquate(_, _)
| PredicateKind::Ambiguous
| PredicateKind::TypeWellFormedFromEnv(_) => true,
@ -584,6 +584,12 @@ pub enum Clause<'tcx> {
/// Ensures that a const generic argument to a parameter `const N: u8`
/// is of type `u8`.
ConstArgHasType(Const<'tcx>, Ty<'tcx>),
/// No syntax: `T` well-formed.
WellFormed(GenericArg<'tcx>),
/// Constant initializer must evaluate successfully.
ConstEvaluatable(ty::Const<'tcx>),
}
impl<'tcx> Binder<'tcx, Clause<'tcx>> {
@ -610,9 +616,6 @@ pub enum PredicateKind<'tcx> {
/// Prove a clause
Clause(Clause<'tcx>),
/// No syntax: `T` well-formed.
WellFormed(GenericArg<'tcx>),
/// Trait must be object-safe.
ObjectSafe(DefId),
@ -638,9 +641,6 @@ pub enum PredicateKind<'tcx> {
/// logic.
Coerce(CoercePredicate<'tcx>),
/// Constant initializer must evaluate successfully.
ConstEvaluatable(ty::Const<'tcx>),
/// Constants must be equal. The first component is the const that is expected.
ConstEquate(Const<'tcx>, Const<'tcx>),
@ -1214,6 +1214,13 @@ impl<'tcx> ToPredicate<'tcx> for Clause<'tcx> {
}
}
impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, Clause<'tcx>> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
tcx.mk_predicate(self.map_bound(|clause| ty::PredicateKind::Clause(clause)))
}
}
impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
@ -1324,11 +1331,11 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
| PredicateKind::Clause(Clause::RegionOutlives(..))
| PredicateKind::WellFormed(..)
| PredicateKind::Clause(Clause::WellFormed(..))
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
| PredicateKind::Clause(Clause::TypeOutlives(..))
| PredicateKind::ConstEvaluatable(..)
| PredicateKind::Clause(Clause::ConstEvaluatable(..))
| PredicateKind::ConstEquate(..)
| PredicateKind::Ambiguous
| PredicateKind::TypeWellFormedFromEnv(..) => None,
@ -1345,11 +1352,11 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
| PredicateKind::Clause(Clause::RegionOutlives(..))
| PredicateKind::WellFormed(..)
| PredicateKind::Clause(Clause::WellFormed(..))
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
| PredicateKind::Clause(Clause::TypeOutlives(..))
| PredicateKind::ConstEvaluatable(..)
| PredicateKind::Clause(Clause::ConstEvaluatable(..))
| PredicateKind::ConstEquate(..)
| PredicateKind::Ambiguous
| PredicateKind::TypeWellFormedFromEnv(..) => None,
@ -1367,10 +1374,10 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
| PredicateKind::Clause(Clause::RegionOutlives(..))
| PredicateKind::WellFormed(..)
| PredicateKind::Clause(Clause::WellFormed(..))
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
| PredicateKind::ConstEvaluatable(..)
| PredicateKind::Clause(Clause::ConstEvaluatable(..))
| PredicateKind::ConstEquate(..)
| PredicateKind::Ambiguous
| PredicateKind::TypeWellFormedFromEnv(..) => None,
@ -1384,10 +1391,8 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
| PredicateKind::WellFormed(..)
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
| PredicateKind::ConstEvaluatable(..)
| PredicateKind::ConstEquate(..)
| PredicateKind::Ambiguous
| PredicateKind::TypeWellFormedFromEnv(..) => None,

View file

@ -2877,7 +2877,7 @@ define_print_and_forward_display! {
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
p!("the constant `", print(ct), "` has type `", print(ty), "`")
},
ty::PredicateKind::WellFormed(arg) => p!(print(arg), " well-formed"),
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => p!(print(arg), " well-formed"),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe")
}
@ -2886,7 +2886,7 @@ define_print_and_forward_display! {
print_value_path(closure_def_id, &[]),
write("` implements the trait `{}`", kind)
),
ty::PredicateKind::ConstEvaluatable(ct) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(ct)) => {
p!("the constant `", print(ct), "` can be evaluated")
}
ty::PredicateKind::ConstEquate(c1, c2) => {

View file

@ -179,6 +179,10 @@ impl<'tcx> fmt::Debug for ty::Clause<'tcx> {
ty::Clause::RegionOutlives(ref pair) => pair.fmt(f),
ty::Clause::TypeOutlives(ref pair) => pair.fmt(f),
ty::Clause::Projection(ref pair) => pair.fmt(f),
ty::Clause::WellFormed(ref data) => write!(f, "WellFormed({:?})", data),
ty::Clause::ConstEvaluatable(ct) => {
write!(f, "ConstEvaluatable({ct:?})")
}
}
}
}
@ -189,16 +193,12 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
ty::PredicateKind::Clause(ref a) => a.fmt(f),
ty::PredicateKind::Subtype(ref pair) => pair.fmt(f),
ty::PredicateKind::Coerce(ref pair) => pair.fmt(f),
ty::PredicateKind::WellFormed(data) => write!(f, "WellFormed({:?})", data),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
write!(f, "ObjectSafe({:?})", trait_def_id)
}
ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind)
}
ty::PredicateKind::ConstEvaluatable(ct) => {
write!(f, "ConstEvaluatable({ct:?})")
}
ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
ty::PredicateKind::TypeWellFormedFromEnv(ty) => {
write!(f, "TypeWellFormedFromEnv({:?})", ty)

View file

@ -409,8 +409,15 @@ where
self.drop_ladder(fields, succ, unwind).0
}
/// Drops the T contained in a `Box<T>` if it has not been moved out of
#[instrument(level = "debug", ret)]
fn open_drop_for_box(&mut self, adt: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> BasicBlock {
fn open_drop_for_box_contents(
&mut self,
adt: ty::AdtDef<'tcx>,
substs: SubstsRef<'tcx>,
succ: BasicBlock,
unwind: Unwind,
) -> BasicBlock {
// drop glue is sent straight to codegen
// box cannot be directly dereferenced
let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), substs);
@ -425,11 +432,7 @@ where
let interior_path = self.elaborator.deref_subpath(self.path);
let succ = self.box_free_block(adt, substs, self.succ, self.unwind);
let unwind_succ =
self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup));
self.drop_subpath(interior, interior_path, succ, unwind_succ)
self.drop_subpath(interior, interior_path, succ, unwind)
}
#[instrument(level = "debug", ret)]
@ -453,7 +456,15 @@ where
self.open_drop_for_adt_contents(adt, substs)
};
if adt.has_dtor(self.tcx()) {
if adt.is_box() {
// we need to drop the inside of the box before running the destructor
let succ = self.destructor_call_block(contents_drop);
let unwind = contents_drop
.1
.map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup)));
self.open_drop_for_box_contents(adt, substs, succ, unwind)
} else if adt.has_dtor(self.tcx()) {
self.destructor_call_block(contents_drop)
} else {
contents_drop.0
@ -650,7 +661,13 @@ where
}),
is_cleanup: unwind.is_cleanup(),
};
self.elaborator.patch().new_block(result)
let destructor_block = self.elaborator.patch().new_block(result);
let block_start = Location { block: destructor_block, statement_index: 0 };
self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
self.drop_flag_test_block(destructor_block, succ, unwind)
}
/// Create a loop that drops an array:
@ -851,13 +868,7 @@ where
self.open_drop_for_tuple(&tys)
}
ty::Tuple(fields) => self.open_drop_for_tuple(fields),
ty::Adt(def, substs) => {
if def.is_box() {
self.open_drop_for_box(*def, substs)
} else {
self.open_drop_for_adt(*def, substs)
}
}
ty::Adt(def, substs) => self.open_drop_for_adt(*def, substs),
ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
ty::Array(ety, size) => {
let size = size.try_eval_target_usize(self.tcx(), self.elaborator.param_env());
@ -905,65 +916,6 @@ where
blk
}
/// Creates a block that frees the backing memory of a `Box` if its drop is required (either
/// statically or by checking its drop flag).
///
/// The contained value will not be dropped.
fn box_free_block(
&mut self,
adt: ty::AdtDef<'tcx>,
substs: SubstsRef<'tcx>,
target: BasicBlock,
unwind: Unwind,
) -> BasicBlock {
let block = self.unelaborated_free_block(adt, substs, target, unwind);
self.drop_flag_test_block(block, target, unwind)
}
/// Creates a block that frees the backing memory of a `Box` (without dropping the contained
/// value).
fn unelaborated_free_block(
&mut self,
adt: ty::AdtDef<'tcx>,
substs: SubstsRef<'tcx>,
target: BasicBlock,
unwind: Unwind,
) -> BasicBlock {
let tcx = self.tcx();
let unit_temp = Place::from(self.new_temp(tcx.mk_unit()));
let free_func = tcx.require_lang_item(LangItem::BoxFree, Some(self.source_info.span));
let args = adt
.variant(FIRST_VARIANT)
.fields
.iter()
.enumerate()
.map(|(i, f)| {
let field = FieldIdx::new(i);
let field_ty = f.ty(tcx, substs);
Operand::Move(tcx.mk_place_field(self.place, field, field_ty))
})
.collect();
let call = TerminatorKind::Call {
func: Operand::function_handle(tcx, free_func, substs, self.source_info.span),
args,
destination: unit_temp,
target: Some(target),
unwind: if unwind.is_cleanup() {
UnwindAction::Terminate
} else {
UnwindAction::Continue
},
from_hir_call: false,
fn_span: self.source_info.span,
}; // FIXME(#43234)
let free_block = self.new_block(unwind, call);
let block_start = Location { block: free_block, statement_index: 0 };
self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
free_block
}
fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
let block = TerminatorKind::Drop {
place: self.place,

View file

@ -9,7 +9,6 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::{Ty, TyCtxt, TypeAndMut};
use rustc_session::Session;
use rustc_target::spec::PanicStrategy;
pub struct CheckAlignment;
@ -241,11 +240,10 @@ fn insert_alignment_check<'tcx>(
required: Operand::Copy(alignment),
found: Operand::Copy(addr),
}),
unwind: if tcx.sess.panic_strategy() == PanicStrategy::Unwind {
UnwindAction::Terminate
} else {
UnwindAction::Unreachable
},
// The panic symbol that this calls is #[rustc_nounwind]. We never want to insert an
// unwind into unsafe code, because unwinding could make a failing UB check turn into
// much worse UB when we start unwinding.
unwind: UnwindAction::Unreachable,
},
});
}

View file

@ -118,8 +118,8 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
// Traverse the body in reverse post-order, to ensure that `FullConstProp` locals are
// assigned before being read.
let postorder = body.basic_blocks.postorder().to_vec();
for bb in postorder.into_iter().rev() {
let rpo = body.basic_blocks.reverse_postorder().to_vec();
for bb in rpo {
let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
optimization_finder.visit_basic_block_data(bb, data);
}

View file

@ -24,7 +24,7 @@ impl<'tcx> MirPass<'tcx> for ReorderBasicBlocks {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let rpo: IndexVec<BasicBlock, BasicBlock> =
body.basic_blocks.postorder().iter().copied().rev().collect();
body.basic_blocks.reverse_postorder().iter().copied().collect();
if rpo.iter().is_sorted() {
return;
}

View file

@ -123,12 +123,6 @@
//! pointers to these functions even if they never get called anywhere. This can
//! be seen as a special case of taking a function reference.
//!
//! #### Boxes
//! Since `Box` expression have special compiler support, no explicit calls to
//! `exchange_malloc()` and `box_free()` may show up in MIR, even if the
//! compiler will generate them. We have to observe `Rvalue::Box` expressions
//! and Box-typed drop-statements for that purpose.
//!
//!
//! Interaction with Cross-Crate Inlining
//! -------------------------------------

View file

@ -182,8 +182,8 @@ where
ct.visit_with(self)?;
ty.visit_with(self)
}
ty::PredicateKind::ConstEvaluatable(ct) => ct.visit_with(self),
ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(ct)) => ct.visit_with(self),
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => arg.visit_with(self),
ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
@ -1270,13 +1270,13 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
);
for (pred, _) in bounds.predicates() {
match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
match pred.skip_binder() {
ty::Clause::Trait(trait_predicate) => {
if self.visit_trait(trait_predicate.trait_ref).is_break() {
return;
}
}
ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) => {
ty::Clause::Projection(proj_predicate) => {
let term = self.visit(proj_predicate.term);
if term.is_break()
|| self.visit_projection_ty(proj_predicate.projection_ty).is_break()

View file

@ -432,7 +432,6 @@ symbols! {
bool,
borrowck_graphviz_format,
borrowck_graphviz_postflow,
box_free,
box_new,
box_patterns,
box_syntax,

View file

@ -319,14 +319,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
self.compute_well_formed_goal(Goal { param_env, predicate: arg })
}
ty::PredicateKind::Ambiguous => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
// FIXME: implement this predicate :)
ty::PredicateKind::ConstEvaluatable(_) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(_)) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
ty::PredicateKind::ConstEquate(_, _) => {

View file

@ -119,10 +119,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
)
}
ty::PredicateKind::Clause(_)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,

View file

@ -826,14 +826,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// we start out with a `ParamEnv` with no inference variables,
// and these don't correspond to adding any new bounds to
// the `ParamEnv`.
ty::PredicateKind::WellFormed(..)
ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::Coerce(..) => {}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("predicate should only exist in the environment: {bound_predicate:?}")

View file

@ -207,7 +207,7 @@ fn satisfied_from_param_env<'tcx>(
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(ce) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(ce)) => {
let b_ct = tcx.expand_abstract_consts(ce);
let mut v = Visitor { ct, infcx, param_env, single_match };
let _ = b_ct.visit_with(&mut v);

View file

@ -1048,7 +1048,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.report_closure_error(&obligation, closure_def_id, found_kind, kind)
}
ty::PredicateKind::WellFormed(ty) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(ty)) => {
match self.tcx.sess.opts.unstable_opts.trait_solver {
TraitSolver::Classic => {
// WF predicates cannot themselves make
@ -1069,7 +1069,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
ty::PredicateKind::ConstEvaluatable(..) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..)) => {
// Errors for `ConstEvaluatable` predicates show up as
// `SelectionError::ConstEvalFailure`,
// not `Unimplemented`.
@ -2415,7 +2415,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err
}
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
// Same hacky approach as above to avoid deluging user
// with error messages.
if arg.references_error()
@ -2487,7 +2487,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
ty::PredicateKind::ConstEvaluatable(data) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(data)) => {
if predicate.references_error() || self.tainted_by_errors().is_some() {
return;
}
@ -3325,7 +3325,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(ct) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(ct)) => {
let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
};

View file

@ -354,12 +354,12 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(_))
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..) => {
let pred =
ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder));
@ -433,7 +433,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
match wf::obligations(
self.selcx.infcx,
obligation.param_env,
@ -498,7 +498,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
ty::PredicateKind::ConstEvaluatable(uv) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.selcx.infcx,
uv,

View file

@ -310,7 +310,7 @@ fn predicate_references_self<'tcx>(
ty::PredicateKind::AliasRelate(..) => bug!("`AliasRelate` not allowed as assumption"),
ty::PredicateKind::WellFormed(..)
ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
@ -318,7 +318,7 @@ fn predicate_references_self<'tcx>(
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
// FIXME(generic_const_exprs): this can mention `Self`
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
@ -361,11 +361,11 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Ambiguous

View file

@ -67,7 +67,8 @@ fn relate_mir_and_user_ty<'tcx>(
ocx.eq(&cause, param_env, mir_ty, user_ty)?;
// FIXME(#104764): We should check well-formedness before normalization.
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into()));
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(user_ty.into())));
ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
Ok(())
}
@ -119,7 +120,9 @@ fn relate_mir_and_user_substs<'tcx>(
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()));
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(
impl_self_ty.into(),
)));
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
}
@ -134,7 +137,7 @@ fn relate_mir_and_user_substs<'tcx>(
// them? This would only be relevant if some input
// type were ill-formed but did not appear in `ty`,
// which...could happen with normalization...
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()));
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(ty.into())));
ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
Ok(())
}

View file

@ -130,14 +130,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
| ty::PredicateKind::Clause(ty::Clause::Projection(..))
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
// We need to search through *all* WellFormed predicates
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
wf_args.push(arg);
}

View file

@ -674,7 +674,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
// So, there is a bit going on here. First, `WellFormed` predicates
// are coinductive, like trait predicates with auto traits.
// This means that we need to detect if we have recursively
@ -862,7 +862,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
ty::PredicateKind::ConstEvaluatable(uv) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.infcx,
uv,

View file

@ -160,11 +160,11 @@ pub fn predicate_obligations<'tcx>(
wf.compute(ct.into());
wf.compute(ty.into());
}
ty::PredicateKind::WellFormed(arg) => {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => {
wf.compute(arg);
}
ty::PredicateKind::ConstEvaluatable(ct) => {
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(ct)) => {
wf.compute(ct.into());
}
@ -386,7 +386,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
depth,
param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(arg))),
)
}),
);
@ -478,7 +478,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause.clone(),
depth,
param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(arg))),
)
}),
);
@ -521,8 +521,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let obligations = self.nominal_obligations(uv.def, uv.substs);
self.out.extend(obligations);
let predicate =
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::Clause::ConstEvaluatable(ct),
));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
@ -541,7 +542,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
ty::Binder::dummy(ty::PredicateKind::Clause(
ty::Clause::WellFormed(ct.into()),
)),
));
}
ty::ConstKind::Expr(_) => {
@ -552,8 +555,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// the future we may allow directly lowering to `ConstKind::Expr` in which case
// we would not be proving bounds we should.
let predicate =
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::Clause::ConstEvaluatable(ct),
));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
@ -784,7 +788,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::WellFormed(
ty.into(),
))),
));
}
}
@ -969,11 +975,11 @@ pub(crate) fn required_region_bounds<'tcx>(
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasRelate(..)

View file

@ -122,7 +122,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
predicate.lower_into(interner),
))
}
ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => match arg.unpack() {
ty::GenericArgKind::Type(ty) => chalk_ir::DomainGoal::WellFormed(
chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
),
@ -137,7 +137,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", predicate),
};
@ -192,7 +192,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
))
}
ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
ty::PredicateKind::Clause(ty::Clause::WellFormed(arg)) => match arg.unpack() {
GenericArgKind::Type(ty) => match ty.kind() {
// FIXME(chalk): In Chalk, a placeholder is WellFormed if it
// `FromEnv`. However, when we "lower" Params, we don't update
@ -231,7 +231,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::ConstEquate(..) => {
chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
@ -672,7 +672,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)))
}
ty::PredicateKind::WellFormed(_ty) => None,
ty::PredicateKind::Clause(ty::Clause::WellFormed(_ty)) => None,
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None,
ty::PredicateKind::ObjectSafe(..)
@ -680,7 +680,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {
@ -807,7 +807,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<Ru
))
}
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_predicate)) => None,
ty::PredicateKind::WellFormed(_ty) => None,
ty::PredicateKind::Clause(ty::Clause::WellFormed(_ty)) => None,
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None,
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
@ -816,7 +816,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<Ru
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {

View file

@ -62,12 +62,12 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
| ty::PredicateKind::Clause(ty::Clause::Projection(..))
| ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => true,

View file

@ -4,8 +4,10 @@
#[cfg(not(test))]
use core::intrinsics;
#[cfg(all(bootstrap, not(test)))]
use core::intrinsics::{min_align_of_val, size_of_val};
#[cfg(all(bootstrap, not(test)))]
use core::ptr::Unique;
#[cfg(not(test))]
use core::ptr::{self, NonNull};
@ -335,14 +337,15 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
}
}
#[cfg_attr(not(test), lang = "box_free")]
#[cfg(all(bootstrap, not(test)))]
#[lang = "box_free"]
#[inline]
// This signature has to be the same as `Box`, otherwise an ICE will happen.
// When an additional parameter to `Box` is added (like `A: Allocator`), this has to be added here as
// well.
// For example if `Box` is changed to `struct Box<T: ?Sized, A: Allocator>(Unique<T>, A)`,
// this function has to be changed to `fn box_free<T: ?Sized, A: Allocator>(Unique<T>, A)` as well.
pub(crate) unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A) {
unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A) {
unsafe {
let size = size_of_val(ptr.as_ref());
let align = min_align_of_val(ptr.as_ref());

View file

@ -1211,8 +1211,16 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> {
#[inline]
fn drop(&mut self) {
// FIXME: Do nothing, drop is currently performed by compiler.
// the T in the Box is dropped by the compiler before the destructor is run
let ptr = self.0;
unsafe {
let layout = Layout::for_value_raw(ptr.as_ptr());
self.1.deallocate(From::from(ptr.cast()), layout)
}
}
}

View file

@ -270,7 +270,7 @@ use core::slice::from_raw_parts_mut;
#[cfg(not(no_global_oom_handling))]
use crate::alloc::handle_alloc_error;
#[cfg(not(no_global_oom_handling))]
use crate::alloc::{box_free, WriteCloneIntoRaw};
use crate::alloc::WriteCloneIntoRaw;
use crate::alloc::{AllocError, Allocator, Global, Layout};
use crate::borrow::{Cow, ToOwned};
#[cfg(not(no_global_oom_handling))]
@ -1442,23 +1442,21 @@ impl<T: ?Sized> Rc<T> {
}
#[cfg(not(no_global_oom_handling))]
fn from_box(v: Box<T>) -> Rc<T> {
fn from_box(src: Box<T>) -> Rc<T> {
unsafe {
let (box_unique, alloc) = Box::into_unique(v);
let bptr = box_unique.as_ptr();
let value_size = size_of_val(&*bptr);
let ptr = Self::allocate_for_ptr(bptr);
let value_size = size_of_val(&*src);
let ptr = Self::allocate_for_ptr(&*src);
// Copy value as bytes
ptr::copy_nonoverlapping(
bptr as *const T as *const u8,
&*src as *const T as *const u8,
&mut (*ptr).value as *mut _ as *mut u8,
value_size,
);
// Free the allocation without dropping its contents
box_free(box_unique, alloc);
let src = Box::from_raw(Box::into_raw(src) as *mut mem::ManuallyDrop<T>);
drop(src);
Self::from_ptr(ptr)
}

View file

@ -33,7 +33,7 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
#[cfg(not(no_global_oom_handling))]
use crate::alloc::handle_alloc_error;
#[cfg(not(no_global_oom_handling))]
use crate::alloc::{box_free, WriteCloneIntoRaw};
use crate::alloc::WriteCloneIntoRaw;
use crate::alloc::{AllocError, Allocator, Global, Layout};
use crate::borrow::{Cow, ToOwned};
use crate::boxed::Box;
@ -1360,23 +1360,21 @@ impl<T: ?Sized> Arc<T> {
}
#[cfg(not(no_global_oom_handling))]
fn from_box(v: Box<T>) -> Arc<T> {
fn from_box(src: Box<T>) -> Arc<T> {
unsafe {
let (box_unique, alloc) = Box::into_unique(v);
let bptr = box_unique.as_ptr();
let value_size = size_of_val(&*bptr);
let ptr = Self::allocate_for_ptr(bptr);
let value_size = size_of_val(&*src);
let ptr = Self::allocate_for_ptr(&*src);
// Copy value as bytes
ptr::copy_nonoverlapping(
bptr as *const T as *const u8,
&*src as *const T as *const u8,
&mut (*ptr).data as *mut _ as *mut u8,
value_size,
);
// Free the allocation without dropping its contents
box_free(box_unique, alloc);
let src = Box::from_raw(Box::into_raw(src) as *mut mem::ManuallyDrop<T>);
drop(src);
Self::from_ptr(ptr)
}

View file

@ -348,7 +348,7 @@ macro_rules! nonzero_unsigned_operations {
}
/// Adds an unsigned integer to a non-zero value.
#[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")]
#[doc = concat!("Return [`", stringify!($Ty), "::MAX`] on overflow.")]
///
/// # Examples
///
@ -579,7 +579,7 @@ macro_rules! nonzero_signed_operations {
/// Checked absolute value.
/// Checks for overflow and returns [`None`] if
#[doc = concat!("`self == ", stringify!($Int), "::MIN`.")]
#[doc = concat!("`self == ", stringify!($Ty), "::MIN`.")]
/// The result cannot be zero.
///
/// # Example
@ -800,7 +800,8 @@ macro_rules! nonzero_signed_operations {
self.get().is_negative()
}
/// Checked negation. Computes `-self`, returning `None` if `self == i32::MIN`.
/// Checked negation. Computes `-self`,
#[doc = concat!("returning `None` if `self == ", stringify!($Ty), "::MIN`.")]
///
/// # Example
///
@ -859,8 +860,10 @@ macro_rules! nonzero_signed_operations {
((unsafe { $Ty::new_unchecked(result) }), overflow)
}
/// Saturating negation. Computes `-self`, returning `MAX` if
/// `self == i32::MIN` instead of overflowing.
/// Saturating negation. Computes `-self`,
#[doc = concat!("returning [`", stringify!($Ty), "::MAX`]")]
#[doc = concat!("if `self == ", stringify!($Ty), "::MIN`")]
/// instead of overflowing.
///
/// # Example
///
@ -993,7 +996,7 @@ macro_rules! nonzero_unsigned_signed_operations {
}
/// Multiplies two non-zero integers together.
#[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")]
#[doc = concat!("Return [`", stringify!($Ty), "::MAX`] on overflow.")]
///
/// # Examples
///
@ -1102,11 +1105,11 @@ macro_rules! nonzero_unsigned_signed_operations {
#[doc = sign_dependent_expr!{
$signedness ?
if signed {
concat!("Return [`", stringify!($Int), "::MIN`] ",
"or [`", stringify!($Int), "::MAX`] on overflow.")
concat!("Return [`", stringify!($Ty), "::MIN`] ",
"or [`", stringify!($Ty), "::MAX`] on overflow.")
}
if unsigned {
concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")
concat!("Return [`", stringify!($Ty), "::MAX`] on overflow.")
}
}]
///

View file

@ -166,14 +166,15 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[track_caller]
#[lang = "panic_misaligned_pointer_dereference"] // needed by codegen for panic on misaligned pointer deref
#[rustc_nounwind] // `CheckAlignment` MIR pass requires this function to never unwind
fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
if cfg!(feature = "panic_immediate_abort") {
super::intrinsics::abort()
}
panic!(
panic_nounwind_fmt(format_args!(
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
)
))
}
/// Panic because we cannot unwind out of a function.

View file

@ -38,7 +38,7 @@ features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive']
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
rand_xorshift = "0.3.0"
[target.'cfg(any(all(target_family = "wasm", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] }
[target.x86_64-fortanix-unknown-sgx.dependencies]

View file

@ -222,7 +222,7 @@ impl<R: ?Sized> BufReader<R> {
/// Invalidates all data in the internal buffer.
#[inline]
fn discard_buffer(&mut self) {
pub(in crate::io) fn discard_buffer(&mut self) {
self.buf.discard_buffer()
}
}

View file

@ -1,6 +1,9 @@
use super::{BorrowedBuf, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
use crate::mem::MaybeUninit;
#[cfg(test)]
mod tests;
/// Copies the entire contents of a reader into a writer.
///
/// This function will continuously read data from `reader` and then
@ -71,32 +74,113 @@ where
R: Read,
W: Write,
{
BufferedCopySpec::copy_to(reader, writer)
let read_buf = BufferedReaderSpec::buffer_size(reader);
let write_buf = BufferedWriterSpec::buffer_size(writer);
if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf {
return BufferedReaderSpec::copy_to(reader, writer);
}
BufferedWriterSpec::copy_from(writer, reader)
}
/// Specialization of the read-write loop that reuses the internal
/// buffer of a BufReader. If there's no buffer then the writer side
/// should be used intead.
trait BufferedReaderSpec {
fn buffer_size(&self) -> usize;
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64>;
}
impl<T> BufferedReaderSpec for T
where
Self: Read,
T: ?Sized,
{
#[inline]
default fn buffer_size(&self) -> usize {
0
}
default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> {
unimplemented!("only called from specializations");
}
}
impl<I> BufferedReaderSpec for BufReader<I>
where
Self: Read,
I: ?Sized,
{
fn buffer_size(&self) -> usize {
self.capacity()
}
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
let mut len = 0;
loop {
// Hack: this relies on `impl Read for BufReader` always calling fill_buf
// if the buffer is empty, even for empty slices.
// It can't be called directly here since specialization prevents us
// from adding I: Read
match self.read(&mut []) {
Ok(_) => {}
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
let buf = self.buffer();
if self.buffer().len() == 0 {
return Ok(len);
}
// In case the writer side is a BufWriter then its write_all
// implements an optimization that passes through large
// buffers to the underlying writer. That code path is #[cold]
// but we're still avoiding redundant memcopies when doing
// a copy between buffered inputs and outputs.
to.write_all(buf)?;
len += buf.len() as u64;
self.discard_buffer();
}
}
}
/// Specialization of the read-write loop that either uses a stack buffer
/// or reuses the internal buffer of a BufWriter
trait BufferedCopySpec: Write {
fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64>;
trait BufferedWriterSpec: Write {
fn buffer_size(&self) -> usize;
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64>;
}
impl<W: Write + ?Sized> BufferedCopySpec for W {
default fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
stack_buffer_copy(reader, writer)
impl<W: Write + ?Sized> BufferedWriterSpec for W {
#[inline]
default fn buffer_size(&self) -> usize {
0
}
default fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
stack_buffer_copy(reader, self)
}
}
impl<I: ?Sized + Write> BufferedCopySpec for BufWriter<I> {
fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
if writer.capacity() < DEFAULT_BUF_SIZE {
return stack_buffer_copy(reader, writer);
impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
fn buffer_size(&self) -> usize {
self.capacity()
}
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
if self.capacity() < DEFAULT_BUF_SIZE {
return stack_buffer_copy(reader, self);
}
let mut len = 0;
let mut init = 0;
loop {
let buf = writer.buffer_mut();
let buf = self.buffer_mut();
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
unsafe {
@ -127,7 +211,7 @@ impl<I: ?Sized + Write> BufferedCopySpec for BufWriter<I> {
Err(e) => return Err(e),
}
} else {
writer.flush_buf()?;
self.flush_buf()?;
init = 0;
}
}

View file

@ -0,0 +1,108 @@
use crate::cmp::{max, min};
use crate::io::*;
#[test]
fn copy_copies() {
let mut r = repeat(0).take(4);
let mut w = sink();
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
let mut r = repeat(0).take(1 << 17);
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
}
struct ShortReader {
cap: usize,
read_size: usize,
observed_buffer: usize,
}
impl Read for ShortReader {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let bytes = min(self.cap, self.read_size);
self.cap -= bytes;
self.observed_buffer = max(self.observed_buffer, buf.len());
Ok(bytes)
}
}
struct WriteObserver {
observed_buffer: usize,
}
impl Write for WriteObserver {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.observed_buffer = max(self.observed_buffer, buf.len());
Ok(buf.len())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[test]
fn copy_specializes_bufwriter() {
let cap = 117 * 1024;
let buf_sz = 16 * 1024;
let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
assert_eq!(
copy(&mut r, &mut w).unwrap(),
cap as u64,
"expected the whole capacity to be copied"
);
assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
}
#[test]
fn copy_specializes_bufreader() {
let mut source = vec![0; 768 * 1024];
source[1] = 42;
let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source));
let mut sink = Vec::new();
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
assert_eq!(source.as_slice(), sink.as_slice());
let buf_sz = 71 * 1024;
assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition");
let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source));
let mut sink = WriteObserver { observed_buffer: 0 };
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
assert_eq!(
sink.observed_buffer, buf_sz,
"expected a large buffer to be provided to the writer"
);
}
#[cfg(unix)]
mod io_benches {
use crate::fs::File;
use crate::fs::OpenOptions;
use crate::io::prelude::*;
use crate::io::BufReader;
use test::Bencher;
#[bench]
fn bench_copy_buf_reader(b: &mut Bencher) {
let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed");
// use dyn to avoid specializations unrelated to readbuf
let dyn_in = &mut file_in as &mut dyn Read;
let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0));
let mut writer =
OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed");
const BYTES: u64 = 1024 * 1024;
b.bytes = BYTES;
b.iter(|| {
reader.get_mut().set_limit(BYTES);
crate::io::copy(&mut reader, &mut writer).unwrap()
});
}
}

View file

@ -1416,17 +1416,18 @@ pub trait Write {
///
/// This function will attempt to write the entire contents of `buf`, but
/// the entire write might not succeed, or the write may also generate an
/// error. A call to `write` represents *at most one* attempt to write to
/// error. Typically, a call to `write` represents one attempt to write to
/// any wrapped object.
///
/// Calls to `write` are not guaranteed to block waiting for data to be
/// written, and a write which would otherwise block can be indicated through
/// an [`Err`] variant.
///
/// If the return value is [`Ok(n)`] then it must be guaranteed that
/// `n <= buf.len()`. A return value of `0` typically means that the
/// underlying object is no longer able to accept bytes and will likely not
/// be able to in the future as well, or that the buffer provided is empty.
/// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`].
/// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`.
/// Unless `buf` is empty, this function shouldnt return `Ok(0)` since the
/// caller may interpret that as an error. To indicate lack of space,
/// implementors should return [`ErrorKind::StorageFull`] error instead.
///
/// # Errors
///

View file

@ -1,67 +1,8 @@
use crate::cmp::{max, min};
use crate::io::prelude::*;
use crate::io::{
copy, empty, repeat, sink, BorrowedBuf, BufWriter, Empty, Repeat, Result, SeekFrom, Sink,
DEFAULT_BUF_SIZE,
};
use crate::io::{empty, repeat, sink, BorrowedBuf, Empty, Repeat, SeekFrom, Sink};
use crate::mem::MaybeUninit;
#[test]
fn copy_copies() {
let mut r = repeat(0).take(4);
let mut w = sink();
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
let mut r = repeat(0).take(1 << 17);
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
}
struct ShortReader {
cap: usize,
read_size: usize,
observed_buffer: usize,
}
impl Read for ShortReader {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let bytes = min(self.cap, self.read_size);
self.cap -= bytes;
self.observed_buffer = max(self.observed_buffer, buf.len());
Ok(bytes)
}
}
struct WriteObserver {
observed_buffer: usize,
}
impl Write for WriteObserver {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.observed_buffer = max(self.observed_buffer, buf.len());
Ok(buf.len())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[test]
fn copy_specializes_bufwriter() {
let cap = 117 * 1024;
let buf_sz = 16 * 1024;
let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
assert_eq!(
copy(&mut r, &mut w).unwrap(),
cap as u64,
"expected the whole capacity to be copied"
);
assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
}
#[test]
fn sink_sinks() {
let mut s = sink();

View file

@ -11,7 +11,13 @@ use crate::slice::from_raw_parts;
use crate::sys::net::Socket;
// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
#[cfg(all(doc, not(target_os = "linux"), not(target_os = "android"), not(target_os = "netbsd")))]
#[cfg(all(
doc,
not(target_os = "linux"),
not(target_os = "android"),
not(target_os = "netbsd"),
not(target_os = "freebsd")
))]
#[allow(non_camel_case_types)]
mod libc {
pub use libc::c_int;

View file

@ -29,7 +29,7 @@ cfg_if::cfg_if! {
all(target_family = "windows", target_env = "gnu"),
target_os = "psp",
target_os = "solid_asp3",
all(target_family = "unix", not(target_os = "espidf")),
all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")),
all(target_vendor = "fortanix", target_env = "sgx"),
))] {
mod gcc;

View file

@ -10,7 +10,7 @@ macro_rules! unimpl {
pub mod net {
#![allow(warnings)]
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
@ -218,6 +218,10 @@ pub mod net {
unimpl!();
}
pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> {
unimpl!();
}
pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
unimpl!();
}

View file

@ -836,31 +836,47 @@ fn signal_string(signal: i32) -> &'static str {
libc::SIGILL => " (SIGILL)",
libc::SIGTRAP => " (SIGTRAP)",
libc::SIGABRT => " (SIGABRT)",
#[cfg(not(target_os = "l4re"))]
libc::SIGBUS => " (SIGBUS)",
libc::SIGFPE => " (SIGFPE)",
libc::SIGKILL => " (SIGKILL)",
#[cfg(not(target_os = "l4re"))]
libc::SIGUSR1 => " (SIGUSR1)",
libc::SIGSEGV => " (SIGSEGV)",
#[cfg(not(target_os = "l4re"))]
libc::SIGUSR2 => " (SIGUSR2)",
libc::SIGPIPE => " (SIGPIPE)",
libc::SIGALRM => " (SIGALRM)",
libc::SIGTERM => " (SIGTERM)",
#[cfg(not(target_os = "l4re"))]
libc::SIGCHLD => " (SIGCHLD)",
#[cfg(not(target_os = "l4re"))]
libc::SIGCONT => " (SIGCONT)",
#[cfg(not(target_os = "l4re"))]
libc::SIGSTOP => " (SIGSTOP)",
#[cfg(not(target_os = "l4re"))]
libc::SIGTSTP => " (SIGTSTP)",
#[cfg(not(target_os = "l4re"))]
libc::SIGTTIN => " (SIGTTIN)",
#[cfg(not(target_os = "l4re"))]
libc::SIGTTOU => " (SIGTTOU)",
#[cfg(not(target_os = "l4re"))]
libc::SIGURG => " (SIGURG)",
#[cfg(not(target_os = "l4re"))]
libc::SIGXCPU => " (SIGXCPU)",
#[cfg(not(target_os = "l4re"))]
libc::SIGXFSZ => " (SIGXFSZ)",
#[cfg(not(target_os = "l4re"))]
libc::SIGVTALRM => " (SIGVTALRM)",
#[cfg(not(target_os = "l4re"))]
libc::SIGPROF => " (SIGPROF)",
#[cfg(not(target_os = "l4re"))]
libc::SIGWINCH => " (SIGWINCH)",
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "l4re")))]
libc::SIGIO => " (SIGIO)",
#[cfg(target_os = "haiku")]
libc::SIGPOLL => " (SIGPOLL)",
#[cfg(not(target_os = "l4re"))]
libc::SIGSYS => " (SIGSYS)",
// For information on Linux signals, run `man 7 signal`
#[cfg(all(

@ -1 +1 @@
Subproject commit 7e2cdc675b92165c5f8c4c794620252be4605e74
Subproject commit d77878b7299dd7e286799a6e8447048b65d2a861

View file

@ -1766,7 +1766,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
//
// Note that if we encounter `PATH` we make sure to append to our own `PATH`
// rather than stomp over it.
if target.contains("msvc") {
if !builder.config.dry_run() && target.contains("msvc") {
for &(ref k, ref v) in builder.cc.borrow()[&target].env() {
if k != "PATH" {
cmd.env(k, v);

View file

@ -25,13 +25,9 @@ from typing import ClassVar, List, Optional
@dataclass
class TestEnvironment:
rust_dir: str
rust_build_dir: str
sdk_dir: str
target: str
package_server_pid: Optional[int] = None
emu_addr: Optional[str] = None
libstd_name: Optional[str] = None
libtest_name: Optional[str] = None
verbose: bool = False
@staticmethod
@ -57,7 +53,7 @@ class TestEnvironment:
@classmethod
def from_args(cls, args):
return cls(
os.path.abspath(args.rust),
os.path.abspath(args.rust_build),
os.path.abspath(args.sdk),
args.target,
verbose=args.verbose,
@ -68,13 +64,9 @@ class TestEnvironment:
with open(cls.env_file_path(), encoding="utf-8") as f:
test_env = json.loads(f.read())
return cls(
test_env["rust_dir"],
test_env["rust_build_dir"],
test_env["sdk_dir"],
test_env["target"],
libstd_name=test_env["libstd_name"],
libtest_name=test_env["libtest_name"],
emu_addr=test_env["emu_addr"],
package_server_pid=test_env["package_server_pid"],
verbose=test_env["verbose"],
)
@ -82,18 +74,6 @@ class TestEnvironment:
with open(self.env_file_path(), "w", encoding="utf-8") as f:
f.write(json.dumps(self.__dict__))
def ssh_dir(self):
return os.path.join(self.tmp_dir(), "ssh")
def ssh_keyfile_path(self):
return os.path.join(self.ssh_dir(), "fuchsia_ed25519")
def ssh_authfile_path(self):
return os.path.join(self.ssh_dir(), "fuchsia_authorized_keys")
def vdl_output_path(self):
return os.path.join(self.tmp_dir(), "vdl_output")
def package_server_log_path(self):
return os.path.join(self.tmp_dir(), "package_server_log")
@ -113,7 +93,9 @@ class TestEnvironment:
def libs_dir(self):
return os.path.join(
self.rust_dir,
self.rust_build_dir,
"host",
"stage2",
"lib",
)
@ -212,21 +194,19 @@ class TestEnvironment:
# Set configs
configs = {
"log.enabled": "true",
"ssh.pub": self.ssh_authfile_path(),
"ssh.priv": self.ssh_keyfile_path(),
"test.is_isolated": "true",
"test.experimental_structured_output": "true",
}
for key, value in configs.items():
subprocess.check_call(
[
self.tool_path("ffx"),
ffx_path,
"config",
"set",
key,
value,
],
env=self.ffx_cmd_env(),
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
@ -248,6 +228,7 @@ class TestEnvironment:
self.tool_path("ffx"),
"daemon",
"stop",
"-w",
],
env=self.ffx_cmd_env(),
stdout=self.subprocess_output(),
@ -275,86 +256,61 @@ class TestEnvironment:
elif len(os.listdir(self.tmp_dir())) != 0:
raise Exception(f"Temp directory is not clean (in {self.tmp_dir()})")
os.mkdir(self.ssh_dir())
os.mkdir(self.output_dir())
# Find libstd and libtest
libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so"))
libtest_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libtest-*.so"))
if not libstd_paths:
raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})")
if not libtest_paths:
raise Exception(f"Failed to locate libtest (in {self.rustlibs_dir()})")
self.libstd_name = os.path.basename(libstd_paths[0])
self.libtest_name = os.path.basename(libtest_paths[0])
# Generate SSH keys for the emulator to use
self.log_info("Generating SSH keys...")
subprocess.check_call(
[
"ssh-keygen",
"-N",
"",
"-t",
"ed25519",
"-f",
self.ssh_keyfile_path(),
"-C",
"Generated by fuchsia-test-runner.py",
],
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
authfile_contents = subprocess.check_output(
[
"ssh-keygen",
"-y",
"-f",
self.ssh_keyfile_path(),
],
stderr=self.subprocess_output(),
)
with open(self.ssh_authfile_path(), "wb") as authfile:
authfile.write(authfile_contents)
ffx_path = self.tool_path("ffx")
ffx_env = self.ffx_cmd_env()
# Start ffx isolation
self.log_info("Starting ffx isolation...")
self.start_ffx_isolation()
# Start emulator (this will generate the vdl output)
self.log_info("Starting emulator...")
# Stop any running emulators (there shouldn't be any)
subprocess.check_call(
[
self.tool_path("fvdl"),
"--sdk",
"start",
"--tuntap",
"--headless",
"--nointeractive",
"--ssh",
self.ssh_dir(),
"--vdl-output",
self.vdl_output_path(),
"--emulator-log",
self.emulator_log_path(),
"--image-name",
"qemu-" + self.triple_to_arch(self.target),
ffx_path,
"emu",
"stop",
"--all",
],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
# Parse vdl output for relevant information
with open(self.vdl_output_path(), encoding="utf-8") as f:
vdl_content = f.read()
matches = re.search(
r'network_address:\s+"\[([0-9a-f]{1,4}:(:[0-9a-f]{1,4}){4}%qemu)\]"',
vdl_content,
)
self.emu_addr = matches.group(1)
# Start emulator
self.log_info("Starting emulator...")
product_bundle = "terminal.qemu-" + self.triple_to_arch(self.target)
subprocess.check_call(
[
ffx_path,
"product-bundle",
"get",
product_bundle,
],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
# FIXME: condition --accel hyper on target arch matching host arch
subprocess.check_call(
[
ffx_path,
"emu",
"start",
product_bundle,
"--headless",
"--log",
self.emulator_log_path(),
"--net",
"tap",
"--accel",
"hyper",
],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
# Create new package repo
self.log_info("Creating package repo...")
@ -369,55 +325,40 @@ class TestEnvironment:
stderr=self.subprocess_output(),
)
# Start package server
self.log_info("Starting package server...")
with open(
self.package_server_log_path(), "w", encoding="utf-8"
) as package_server_log:
# We want this to be a long-running process that persists after the script finishes
# pylint: disable=consider-using-with
self.package_server_pid = subprocess.Popen(
[
self.tool_path("pm"),
"serve",
"-vt",
"-repo",
self.repo_dir(),
"-l",
":8084",
],
stdout=package_server_log,
stderr=package_server_log,
).pid
# Register package server with emulator
self.log_info("Registering package server...")
ssh_client = subprocess.check_output(
[
"ssh",
"-i",
self.ssh_keyfile_path(),
"-o",
"StrictHostKeyChecking=accept-new",
self.emu_addr,
"-f",
"echo $SSH_CLIENT",
],
text=True,
)
repo_addr = ssh_client.split()[0].replace("%", "%25")
repo_url = f"http://[{repo_addr}]:8084/config.json"
# Add repo
subprocess.check_call(
[
"ssh",
"-i",
self.ssh_keyfile_path(),
"-o",
"StrictHostKeyChecking=accept-new",
self.emu_addr,
"-f",
f"pkgctl repo add url -f 1 -n {self.TEST_REPO_NAME} {repo_url}",
ffx_path,
"repository",
"add-from-pm",
self.repo_dir(),
"--repository",
self.TEST_REPO_NAME,
],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
# Start repository server
subprocess.check_call(
[ffx_path, "repository", "server", "start", "--address", "[::]:0"],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
# Register with newly-started emulator
subprocess.check_call(
[
ffx_path,
"target",
"repository",
"register",
"--repository",
self.TEST_REPO_NAME,
],
env=ffx_env,
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
@ -471,8 +412,8 @@ class TestEnvironment:
meta/package={package_dir}/meta/package
meta/{package_name}.cm={package_dir}/meta/{package_name}.cm
bin/{exe_name}={bin_path}
lib/{libstd_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libstd_name}
lib/{libtest_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libtest_name}
lib/{libstd_name}={libstd_path}
lib/{libtest_name}={libtest_path}
lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/dist/lib/ld.so.1
lib/libfdio.so={sdk_dir}/arch/{target_arch}/dist/libfdio.so
"""
@ -502,6 +443,16 @@ class TestEnvironment:
bin_path = os.path.abspath(args.bin_path)
# Find libstd and libtest
libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so"))
libtest_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libtest-*.so"))
if not libstd_paths:
raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})")
if not libtest_paths:
raise Exception(f"Failed to locate libtest (in {self.rustlibs_dir()})")
# Build a unique, deterministic name for the test using the name of the
# binary and the last 6 hex digits of the hash of the full path
def path_checksum(path):
@ -604,11 +555,12 @@ class TestEnvironment:
exe_name=exe_name,
package_dir=package_dir,
package_name=package_name,
rust_dir=self.rust_dir,
rustlib_dir=self.target,
target=self.target,
sdk_dir=self.sdk_dir,
libstd_name=self.libstd_name,
libtest_name=self.libtest_name,
libstd_name=os.path.basename(libstd_paths[0]),
libtest_name=os.path.basename(libtest_paths[0]),
libstd_path=libstd_paths[0],
libtest_path=libtest_paths[0],
target_arch=self.triple_to_arch(self.target),
)
)
@ -779,20 +731,15 @@ class TestEnvironment:
else:
self.log_debug("No ffx daemon log found")
# Stop package server
self.log_info("Stopping package server...")
os.kill(self.package_server_pid, signal.SIGTERM)
# Shut down the emulator
self.log_info("Stopping emulator...")
subprocess.check_call(
[
self.tool_path("fvdl"),
"--sdk",
"kill",
"--launched-proto",
self.vdl_output_path(),
self.tool_path("ffx"),
"emu",
"stop",
],
env=self.ffx_cmd_env(),
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
)
@ -969,8 +916,8 @@ def main():
"start", help="initializes the testing environment"
)
start_parser.add_argument(
"--rust",
help="the directory of the installed Rust compiler for Fuchsia",
"--rust-build",
help="the current compiler build directory (`$RUST_SRC/build` by default)",
required=True,
)
start_parser.add_argument(

View file

@ -681,12 +681,9 @@ local Rust source checkout:
cd ${RUST_SRC_PATH}
```
To run the Rust test suite on an emulated Fuchsia device, you must install the
Rust compiler locally. See "[Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)"
for the steps to build locally.
You'll also need to download a copy of the Fuchsia SDK. The current minimum
supported SDK version is [10.20221207.2.89][minimum_supported_sdk_version].
To run the Rust test suite on an emulated Fuchsia device, you'll also need to
download a copy of the Fuchsia SDK. The current minimum supported SDK version is
[10.20221207.2.89][minimum_supported_sdk_version].
[minimum_supported_sdk_version]: https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:10.20221207.2.89
@ -695,13 +692,13 @@ Fuchsia's test runner interacts with the Fuchsia emulator and is located at
test environment with:
```sh
src/ci/docker/scripts/fuchsia-test-runner.py start
--rust ${RUST_SRC_PATH}/install
--sdk ${SDK_PATH}
--target {x86_64-unknown-fuchsia|aarch64-unknown-fuchsia}
src/ci/docker/scripts/fuchsia-test-runner.py start \
--rust-build ${RUST_SRC_PATH}/build \
--sdk ${SDK_PATH} \
--target {x86_64-unknown-fuchsia|aarch64-unknown-fuchsia} \
```
Where `${RUST_SRC_PATH}/install` is the `prefix` set in `config.toml` and
Where `${RUST_SRC_PATH}/build` is the `build-dir` set in `config.toml` and
`${SDK_PATH}` is the path to the downloaded and unzipped SDK.
Once our environment is started, we can run our tests using `x.py` as usual. The

View file

@ -11,8 +11,8 @@ it exists. The marker is the attribute `#[lang = "..."]` and there are
various different values of `...`, i.e. various different 'lang
items'.
For example, `Box` pointers require two lang items, one for allocation
and one for deallocation. A freestanding program that uses the `Box`
For example, `Box` pointers require a lang item for allocation.
A freestanding program that uses the `Box`
sugar for dynamic allocations via `malloc` and `free`:
```rust,ignore (libc-is-finicky)
@ -48,9 +48,10 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
p
}
#[lang = "box_free"]
unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
libc::free(ptr as *mut libc::c_void)
impl<T> Drop for Box<T> {
fn drop(&mut self) {
libc::free(self.0.0.0 as *mut libc::c_void)
}
}
#[start]
@ -84,8 +85,8 @@ Other features provided by lang items include:
`contravariant_lifetime`, etc.
Lang items are loaded lazily by the compiler; e.g. if one never uses
`Box` then there is no need to define functions for `exchange_malloc`
and `box_free`. `rustc` will emit an error when an item is needed
`Box` then there is no need to define a function for `exchange_malloc`.
`rustc` will emit an error when an item is needed
but not found in the current crate or any that it depends on.
Most lang items are defined by `libcore`, but if you're trying to build
@ -250,7 +251,6 @@ the source code.
- Allocations
- `owned_box`: `liballoc/boxed.rs`
- `exchange_malloc`: `liballoc/heap.rs`
- `box_free`: `liballoc/heap.rs`
- Operands
- `not`: `libcore/ops/bit.rs`
- `bitand`: `libcore/ops/bit.rs`

View file

@ -344,8 +344,8 @@ pub(crate) fn clean_predicate<'tcx>(
Some(clean_projection_predicate(bound_predicate.rebind(pred), cx))
}
// FIXME(generic_const_exprs): should this do something?
ty::PredicateKind::ConstEvaluatable(..) => None,
ty::PredicateKind::WellFormed(..) => None,
ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..)) => None,
ty::PredicateKind::Clause(ty::Clause::WellFormed(..)) => None,
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None,
ty::PredicateKind::Subtype(..)

View file

@ -277,14 +277,18 @@ function preLoadCss(cssUrl) {
searchState.mouseMovedAfterSearch = false;
document.title = searchState.title;
},
hideResults: () => {
switchDisplayedElement(null);
removeQueryParameters: () => {
// We change the document title.
document.title = searchState.titleBeforeSearch;
// We also remove the query parameter from the URL.
if (browserSupportsHistoryApi()) {
history.replaceState(null, "", getNakedUrl() + window.location.hash);
}
},
hideResults: () => {
switchDisplayedElement(null);
// We also remove the query parameter from the URL.
searchState.removeQueryParameters();
},
getQueryStringParams: () => {
const params = {};
window.location.search.substring(1).split("&").

View file

@ -2071,6 +2071,20 @@ function initSearch(rawSearchIndex) {
if (go_to_first || (results.others.length === 1
&& getSettingValue("go-to-only-result") === "true")
) {
// Needed to force re-execution of JS when coming back to a page. Let's take this
// scenario as example:
//
// 1. You have the "Directly go to item in search if there is only one result" option
// enabled.
// 2. You make a search which results only one result, leading you automatically to
// this result.
// 3. You go back to previous page.
//
// Now, without the call below, the JS will not be re-executed and the previous state
// will be used, starting search again since the search input is not empty, leading you
// back to the previous page again.
window.onunload = () => {};
searchState.removeQueryParameters();
const elem = document.createElement("a");
elem.href = results.others[0].href;
removeClass(elem, "active");
@ -2185,6 +2199,18 @@ function initSearch(rawSearchIndex) {
printTab(currentTab);
}
function updateSearchHistory(url) {
if (!browserSupportsHistoryApi()) {
return;
}
const params = searchState.getQueryStringParams();
if (!history.state && !params.search) {
history.pushState(null, "", url);
} else {
history.replaceState(null, "", url);
}
}
/**
* Perform a search based on the current state of the search input element
* and display the results.
@ -2195,7 +2221,6 @@ function initSearch(rawSearchIndex) {
if (e) {
e.preventDefault();
}
const query = parseQuery(searchState.input.value.trim());
let filterCrates = getFilterCrates();
@ -2221,15 +2246,7 @@ function initSearch(rawSearchIndex) {
// Because searching is incremental by character, only the most
// recent search query is added to the browser history.
if (browserSupportsHistoryApi()) {
const newURL = buildUrl(query.original, filterCrates);
if (!history.state && !params.search) {
history.pushState(null, "", newURL);
} else {
history.replaceState(null, "", newURL);
}
}
updateSearchHistory(buildUrl(query.original, filterCrates));
showResults(
execQuery(query, searchWords, filterCrates, window.currentCrate),
@ -2695,13 +2712,8 @@ function initSearch(rawSearchIndex) {
function updateCrate(ev) {
if (ev.target.value === "all crates") {
// If we don't remove it from the URL, it'll be picked up again by the search.
const params = searchState.getQueryStringParams();
const query = searchState.input.value.trim();
if (!history.state && !params.search) {
history.pushState(null, "", buildUrl(query, null));
} else {
history.replaceState(null, "", buildUrl(query, null));
}
updateSearchHistory(buildUrl(query, null));
}
// In case you "cut" the entry from the search input, then change the crate filter
// before paste back the previous search, you get the old search results without

View file

@ -33,8 +33,8 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
| ty::Clause::Trait(..)
| ty::Clause::ConstArgHasType(..),
)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Clause(ty::Clause::WellFormed(_))
| ty::PredicateKind::Clause(ty::Clause::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
ty::PredicateKind::AliasRelate(..) => panic!("alias relate predicate on function: {predicate:#?}"),

View file

@ -1951,7 +1951,9 @@ impl<'test> TestCx<'test> {
rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
// Optionally prevent default --sysroot if specified in test compile-flags.
if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot")) {
if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
&& !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
{
// In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot.
rustc.arg("--sysroot").arg(&self.config.sysroot_base);
}

View file

@ -9,7 +9,7 @@ LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::alloc::Global as std::alloc::Allocator>::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::box_free::<i32, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::boxed::Box<i32> as std::ops::Drop>::drop` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<i32>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
note: inside `main`

View file

@ -9,7 +9,7 @@ LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::alloc::Global as std::alloc::Allocator>::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::box_free::<i32, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::boxed::Box<i32> as std::ops::Drop>::drop` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<i32>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
note: inside closure

View file

@ -20,7 +20,7 @@ LL | fn inner(x: &mut i32, f: fn(&mut i32)) {
= note: BACKTRACE (of the first span):
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::alloc::Global as std::alloc::Allocator>::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::box_free::<i32, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `<std::boxed::Box<i32> as std::ops::Drop>::drop` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<i32>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
note: inside closure

View file

@ -370,7 +370,7 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
// all: __stack_chk_fail
// strong-NOT: __stack_chk_fail
// strong: __stack_chk_fail
// basic-NOT: __stack_chk_fail
// none-NOT: __stack_chk_fail
// missing-NOT: __stack_chk_fail

View file

@ -6,36 +6,41 @@
let mut _0: ();
let _2: ();
let mut _3: std::boxed::Box<[i32]>;
let mut _4: ();
let mut _4: &mut std::boxed::Box<[i32]>;
let mut _5: ();
let mut _6: ();
let mut _7: *const [i32];
let mut _6: &mut std::boxed::Box<[i32]>;
let mut _7: ();
let mut _8: &mut std::boxed::Box<[i32]>;
let mut _9: ();
let mut _10: *const [i32];
bb0: {
StorageLive(_2);
StorageLive(_3);
_3 = move _1;
_7 = (((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>).0: *const [i32]);
_2 = callee(move (*_7)) -> [return: bb3, unwind: bb4];
_10 = (((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>).0: *const [i32]);
_2 = callee(move (*_10)) -> [return: bb3, unwind: bb4];
}
bb1: {
bb1 (cleanup): {
resume;
}
bb2: {
StorageDead(_3);
StorageDead(_2);
_0 = const ();
return;
}
bb2 (cleanup): {
resume;
}
bb3: {
_4 = alloc::alloc::box_free::<[i32], std::alloc::Global>(move (_3.0: std::ptr::Unique<[i32]>), move (_3.1: std::alloc::Global)) -> bb1;
_4 = &mut _3;
_5 = <Box<[i32]> as Drop>::drop(move _4) -> [return: bb2, unwind: bb1];
}
bb4 (cleanup): {
_6 = alloc::alloc::box_free::<[i32], std::alloc::Global>(move (_3.0: std::ptr::Unique<[i32]>), move (_3.1: std::alloc::Global)) -> [return: bb2, unwind terminate];
_8 = &mut _3;
_9 = <Box<[i32]> as Drop>::drop(move _8) -> [return: bb1, unwind terminate];
}
}

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>spanview_block.main.built.after</title>
<style>
.line {

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>spanview_statement.main.built.after</title>
<style>
.line {

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>spanview_terminator.main.built.after</title>
<style>
.line {

View file

@ -34,7 +34,14 @@ go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
// We enter it into the search.
write: (".search-input", "HasALongTraitWithParams")
wait-for-document-property: {"title": "HasALongTraitWithParams in lib2 - Rust"}
assert-document-property: ({"URL": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH)
assert-window-property: ({"location": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH)
// Regression test for <https://github.com/rust-lang/rust/issues/112676>.
// If "go-to-only-result" is enabled and you go back to history, it should not lead you back to the
// page result again automatically.
history-go-back:
wait-for-document-property: {"title": "lib2 - Rust"}
assert-window-property: ({"location": "/lib2/index.html"}, ENDS_WITH)
// We try again to see if it goes to the only result
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=HasALongTraitWithParams"

View file

@ -1,4 +1,4 @@
// only-x86_64
// needs-asm-support
#![feature(asm_const)]
@ -38,6 +38,9 @@ fn main() {
//~^ ERROR expected one of
asm!("{}", options(), const foo);
//~^ ERROR attempt to use a non-constant value in a constant
// test that asm!'s clobber_abi doesn't accept non-string literals
// see also https://github.com/rust-lang/rust/issues/112635
asm!("", clobber_abi());
//~^ ERROR at least one abi must be provided
asm!("", clobber_abi(foo));
@ -46,6 +49,25 @@ fn main() {
//~^ ERROR expected one of `)` or `,`, found `foo`
asm!("", clobber_abi("C", foo));
//~^ ERROR expected string literal
asm!("", clobber_abi(1));
//~^ ERROR expected string literal
asm!("", clobber_abi(()));
//~^ ERROR expected string literal
asm!("", clobber_abi(uwu));
//~^ ERROR expected string literal
asm!("", clobber_abi({}));
//~^ ERROR expected string literal
asm!("", clobber_abi(loop {}));
//~^ ERROR expected string literal
asm!("", clobber_abi(if));
//~^ ERROR expected string literal
asm!("", clobber_abi(do));
//~^ ERROR expected string literal
asm!("", clobber_abi(<));
//~^ ERROR expected string literal
asm!("", clobber_abi(.));
//~^ ERROR expected string literal
asm!("{}", clobber_abi("C"), const foo);
//~^ ERROR attempt to use a non-constant value in a constant
asm!("", options(), clobber_abi("C"));
@ -56,15 +78,7 @@ fn main() {
//~^^ ERROR argument never used
//~^^^ ERROR attempt to use a non-constant value in a constant
//~^^^^ ERROR attempt to use a non-constant value in a constant
asm!("", a = in("eax") foo);
//~^ ERROR explicit register arguments cannot have names
asm!("{a}", in("eax") foo, a = const bar);
//~^ ERROR attempt to use a non-constant value in a constant
asm!("{a}", in("eax") foo, a = const bar);
//~^ ERROR attempt to use a non-constant value in a constant
asm!("{1}", in("eax") foo, const bar);
//~^ ERROR positional arguments cannot follow named arguments or explicit register arguments
//~^^ ERROR attempt to use a non-constant value in a constant
asm!("", options(), "");
//~^ ERROR expected one of
asm!("{}", in(reg) foo, "{}", out(reg) foo);

View file

@ -83,31 +83,85 @@ LL | asm!("", options(nomem, foo));
| ^^^ expected one of 10 possible tokens
error: at least one abi must be provided as an argument to `clobber_abi`
--> $DIR/parse-error.rs:41:30
--> $DIR/parse-error.rs:44:30
|
LL | asm!("", clobber_abi());
| ^
error: expected string literal
--> $DIR/parse-error.rs:43:30
--> $DIR/parse-error.rs:46:30
|
LL | asm!("", clobber_abi(foo));
| ^^^ not a string literal
error: expected one of `)` or `,`, found `foo`
--> $DIR/parse-error.rs:45:34
--> $DIR/parse-error.rs:48:34
|
LL | asm!("", clobber_abi("C" foo));
| ^^^ expected one of `)` or `,`
error: expected string literal
--> $DIR/parse-error.rs:47:35
--> $DIR/parse-error.rs:50:35
|
LL | asm!("", clobber_abi("C", foo));
| ^^^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:52:30
|
LL | asm!("", clobber_abi(1));
| ^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:54:30
|
LL | asm!("", clobber_abi(()));
| ^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:56:30
|
LL | asm!("", clobber_abi(uwu));
| ^^^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:58:30
|
LL | asm!("", clobber_abi({}));
| ^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:60:30
|
LL | asm!("", clobber_abi(loop {}));
| ^^^^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:62:30
|
LL | asm!("", clobber_abi(if));
| ^^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:64:30
|
LL | asm!("", clobber_abi(do));
| ^^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:66:30
|
LL | asm!("", clobber_abi(<));
| ^ not a string literal
error: expected string literal
--> $DIR/parse-error.rs:68:30
|
LL | asm!("", clobber_abi(.));
| ^ not a string literal
error: duplicate argument named `a`
--> $DIR/parse-error.rs:54:36
--> $DIR/parse-error.rs:76:36
|
LL | asm!("{a}", a = const foo, a = const bar);
| ------------- ^^^^^^^^^^^^^ duplicate argument
@ -115,41 +169,27 @@ LL | asm!("{a}", a = const foo, a = const bar);
| previously here
error: argument never used
--> $DIR/parse-error.rs:54:36
--> $DIR/parse-error.rs:76:36
|
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^^^^^^^^^^^ argument never used
|
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: explicit register arguments cannot have names
--> $DIR/parse-error.rs:59:18
|
LL | asm!("", a = in("eax") foo);
| ^^^^^^^^^^^^^^^^^
error: positional arguments cannot follow named arguments or explicit register arguments
--> $DIR/parse-error.rs:65:36
|
LL | asm!("{1}", in("eax") foo, const bar);
| ------------- ^^^^^^^^^ positional argument
| |
| explicit register argument
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
--> $DIR/parse-error.rs:68:29
--> $DIR/parse-error.rs:82:29
|
LL | asm!("", options(), "");
| ^^ expected one of 9 possible tokens
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:70:33
--> $DIR/parse-error.rs:84:33
|
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
| ^^^^ expected one of 9 possible tokens
error: asm template must be a string literal
--> $DIR/parse-error.rs:72:14
--> $DIR/parse-error.rs:86:14
|
LL | asm!(format!("{{{}}}", 0), in(reg) foo);
| ^^^^^^^^^^^^^^^^^^^^
@ -157,7 +197,7 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo);
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
--> $DIR/parse-error.rs:74:21
--> $DIR/parse-error.rs:88:21
|
LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
| ^^^^^^^^^^^^^^^^^^^^
@ -165,121 +205,121 @@ LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: _ cannot be used for input operands
--> $DIR/parse-error.rs:76:28
--> $DIR/parse-error.rs:90:28
|
LL | asm!("{}", in(reg) _);
| ^
error: _ cannot be used for input operands
--> $DIR/parse-error.rs:78:31
--> $DIR/parse-error.rs:92:31
|
LL | asm!("{}", inout(reg) _);
| ^
error: _ cannot be used for input operands
--> $DIR/parse-error.rs:80:35
--> $DIR/parse-error.rs:94:35
|
LL | asm!("{}", inlateout(reg) _);
| ^
error: requires at least a template string argument
--> $DIR/parse-error.rs:87:1
--> $DIR/parse-error.rs:101:1
|
LL | global_asm!();
| ^^^^^^^^^^^^^
error: asm template must be a string literal
--> $DIR/parse-error.rs:89:13
--> $DIR/parse-error.rs:103:13
|
LL | global_asm!(FOO);
| ^^^
error: expected token: `,`
--> $DIR/parse-error.rs:91:18
--> $DIR/parse-error.rs:105:18
|
LL | global_asm!("{}" FOO);
| ^^^ expected `,`
error: expected operand, options, or additional template string
--> $DIR/parse-error.rs:93:19
--> $DIR/parse-error.rs:107:19
|
LL | global_asm!("{}", FOO);
| ^^^ expected operand, options, or additional template string
error: expected expression, found end of macro arguments
--> $DIR/parse-error.rs:95:24
--> $DIR/parse-error.rs:109:24
|
LL | global_asm!("{}", const);
| ^ expected expression
error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
--> $DIR/parse-error.rs:97:30
--> $DIR/parse-error.rs:111:30
|
LL | global_asm!("{}", const(reg) FOO);
| ^^^ expected one of `,`, `.`, `?`, or an operator
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
--> $DIR/parse-error.rs:99:25
--> $DIR/parse-error.rs:113:25
|
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:101:25
--> $DIR/parse-error.rs:115:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:103:25
--> $DIR/parse-error.rs:117:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected string literal
--> $DIR/parse-error.rs:106:29
--> $DIR/parse-error.rs:120:29
|
LL | global_asm!("", clobber_abi(FOO));
| ^^^ not a string literal
error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:108:33
--> $DIR/parse-error.rs:122:33
|
LL | global_asm!("", clobber_abi("C" FOO));
| ^^^ expected one of `)` or `,`
error: expected string literal
--> $DIR/parse-error.rs:110:34
--> $DIR/parse-error.rs:124:34
|
LL | global_asm!("", clobber_abi("C", FOO));
| ^^^ not a string literal
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:112:19
--> $DIR/parse-error.rs:126:19
|
LL | global_asm!("{}", clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:114:28
--> $DIR/parse-error.rs:128:28
|
LL | global_asm!("", options(), clobber_abi("C"));
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:116:30
--> $DIR/parse-error.rs:130:30
|
LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:118:17
--> $DIR/parse-error.rs:132:17
|
LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
| ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
error: duplicate argument named `a`
--> $DIR/parse-error.rs:120:35
--> $DIR/parse-error.rs:134:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ------------- ^^^^^^^^^^^^^ duplicate argument
@ -287,7 +327,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
| previously here
error: argument never used
--> $DIR/parse-error.rs:120:35
--> $DIR/parse-error.rs:134:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ^^^^^^^^^^^^^ argument never used
@ -295,19 +335,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
--> $DIR/parse-error.rs:123:28
--> $DIR/parse-error.rs:137:28
|
LL | global_asm!("", options(), "");
| ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:125:30
--> $DIR/parse-error.rs:139:30
|
LL | global_asm!("{}", const FOO, "{}", const FOO);
| ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
error: asm template must be a string literal
--> $DIR/parse-error.rs:127:13
--> $DIR/parse-error.rs:141:13
|
LL | global_asm!(format!("{{{}}}", 0), const FOO);
| ^^^^^^^^^^^^^^^^^^^^
@ -315,7 +355,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
--> $DIR/parse-error.rs:129:20
--> $DIR/parse-error.rs:143:20
|
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| ^^^^^^^^^^^^^^^^^^^^
@ -332,7 +372,7 @@ LL | asm!("{}", options(), const foo);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:49:44
--> $DIR/parse-error.rs:71:44
|
LL | let mut foo = 0;
| ----------- help: consider using `const` instead of `let`: `const foo`
@ -341,7 +381,7 @@ LL | asm!("{}", clobber_abi("C"), const foo);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:52:55
--> $DIR/parse-error.rs:74:55
|
LL | let mut foo = 0;
| ----------- help: consider using `const` instead of `let`: `const foo`
@ -350,7 +390,7 @@ LL | asm!("{}", options(), clobber_abi("C"), const foo);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:54:31
--> $DIR/parse-error.rs:76:31
|
LL | let mut foo = 0;
| ----------- help: consider using `const` instead of `let`: `const foo`
@ -359,7 +399,7 @@ LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:54:46
--> $DIR/parse-error.rs:76:46
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
@ -367,33 +407,6 @@ LL | let mut bar = 0;
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:61:46
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("eax") foo, a = const bar);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:63:46
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("eax") foo, a = const bar);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:65:42
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{1}", in("eax") foo, const bar);
| ^^^ non-constant value
error: aborting due to 59 previous errors
error: aborting due to 63 previous errors
For more information about this error, try `rustc --explain E0435`.

View file

@ -0,0 +1,21 @@
// only-x86_64
#![feature(asm_const)]
use std::arch::asm;
fn main() {
let mut foo = 0;
let mut bar = 0;
unsafe {
asm!("", a = in("eax") foo);
//~^ ERROR explicit register arguments cannot have names
asm!("{a}", in("eax") foo, a = const bar);
//~^ ERROR attempt to use a non-constant value in a constant
asm!("{a}", in("eax") foo, a = const bar);
//~^ ERROR attempt to use a non-constant value in a constant
asm!("{1}", in("eax") foo, const bar);
//~^ ERROR positional arguments cannot follow named arguments or explicit register arguments
//~^^ ERROR attempt to use a non-constant value in a constant
}
}

View file

@ -0,0 +1,44 @@
error: explicit register arguments cannot have names
--> $DIR/x86_64_parse_error.rs:11:18
|
LL | asm!("", a = in("eax") foo);
| ^^^^^^^^^^^^^^^^^
error: positional arguments cannot follow named arguments or explicit register arguments
--> $DIR/x86_64_parse_error.rs:17:36
|
LL | asm!("{1}", in("eax") foo, const bar);
| ------------- ^^^^^^^^^ positional argument
| |
| explicit register argument
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/x86_64_parse_error.rs:13:46
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("eax") foo, a = const bar);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/x86_64_parse_error.rs:15:46
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("eax") foo, a = const bar);
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/x86_64_parse_error.rs:17:42
|
LL | let mut bar = 0;
| ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{1}", in("eax") foo, const bar);
| ^^^ non-constant value
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0435`.

View file

@ -13,13 +13,10 @@ struct Unique<T: ?Sized>(NonNull<T>);
pub struct Box<T: ?Sized>(Unique<T>);
impl<T: ?Sized> Drop for Box<T> {
fn drop(&mut self) {}
}
#[lang = "box_free"]
#[inline(always)]
unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
dealloc(ptr.0.0)
#[inline(always)]
fn drop(&mut self) {
dealloc(self.0.0.0)
}
}
#[inline(never)]

View file

@ -477,6 +477,13 @@ message = "This PR changes src/bootstrap/defaults/config.compiler.toml. If appro
[mentions."src/bootstrap/defaults/config.codegen.toml"]
message = "This PR changes src/bootstrap/defaults/config.codegen.toml. If appropriate, please also update `config.compiler.toml` so the defaults are in sync."
[mentions."src/bootstrap/llvm.rs"]
message = "This PR changes how LLVM is built. Consider updating src/bootstrap/download-ci-llvm-stamp."
[mentions."compiler/rustc_llvm/build.rs"]
message = "This PR changes how LLVM is built. Consider updating src/bootstrap/download-ci-llvm-stamp."
[mentions."compiler/rustc_llvm/llvm-wrapper"]
message = "This PR changes how LLVM is built. Consider updating src/bootstrap/download-ci-llvm-stamp."
[mentions."tests/ui/deriving/deriving-all-codegen.stdout"]
message = "Changes to the code generated for builtin derived traits."
cc = ["@nnethercote"]