Merge from rustc
This commit is contained in:
commit
7cef28657e
97 changed files with 1663 additions and 1297 deletions
139
.github/workflows/dependencies.yml
vendored
Normal file
139
.github/workflows/dependencies.yml
vendored
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
583
compiler/rustc_hir_analysis/src/astconv/bounds.rs
Normal file
583
compiler/rustc_hir_analysis/src/astconv/bounds.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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!(),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
))),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(..) => (),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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()))),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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]))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//! -------------------------------------
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -432,7 +432,6 @@ symbols! {
|
|||
bool,
|
||||
borrowck_graphviz_format,
|
||||
borrowck_graphviz_postflow,
|
||||
box_free,
|
||||
box_new,
|
||||
box_patterns,
|
||||
box_syntax,
|
||||
|
|
|
|||
|
|
@ -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(_, _) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:?}")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:?}`");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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(..) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
}
|
||||
}]
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
108
library/std/src/io/copy/tests.rs
Normal file
108
library/std/src/io/copy/tests.rs
Normal 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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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 shouldn’t 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
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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("&").
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:#?}"),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>spanview_block.main.built.after</title>
|
||||
<style>
|
||||
.line {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>spanview_statement.main.built.after</title>
|
||||
<style>
|
||||
.line {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>spanview_terminator.main.built.after</title>
|
||||
<style>
|
||||
.line {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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`.
|
||||
21
tests/ui/asm/x86_64/x86_64_parse_error.rs
Normal file
21
tests/ui/asm/x86_64/x86_64_parse_error.rs
Normal 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
|
||||
}
|
||||
}
|
||||
44
tests/ui/asm/x86_64/x86_64_parse_error.stderr
Normal file
44
tests/ui/asm/x86_64/x86_64_parse_error.stderr
Normal 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`.
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue