Merge from rustc
This commit is contained in:
commit
a4e601ff40
309 changed files with 5872 additions and 2695 deletions
1
.clang-format
Normal file
1
.clang-format
Normal file
|
|
@ -0,0 +1 @@
|
|||
BasedOnStyle: LLVM
|
||||
|
|
@ -29,6 +29,7 @@ Files: compiler/*
|
|||
x
|
||||
x.ps1
|
||||
x.py
|
||||
.clang-format
|
||||
.editorconfig
|
||||
.git-blame-ignore-revs
|
||||
.gitattributes
|
||||
|
|
|
|||
|
|
@ -5627,6 +5627,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rustc-hash",
|
||||
"semver",
|
||||
"similar",
|
||||
"termcolor",
|
||||
"walkdir",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2126,7 +2126,8 @@ pub struct BareFnTy {
|
|||
pub ext: Extern,
|
||||
pub generic_params: ThinVec<GenericParam>,
|
||||
pub decl: P<FnDecl>,
|
||||
/// Span of the `fn(...) -> ...` part.
|
||||
/// Span of the `[unsafe] [extern] fn(...) -> ...` part, i.e. everything
|
||||
/// after the generic params (if there are any, e.g. `for<'a>`).
|
||||
pub decl_span: Span,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -233,8 +233,7 @@ pub const PREC_JUMP: i8 = -30;
|
|||
pub const PREC_RANGE: i8 = -10;
|
||||
// The range 2..=14 is reserved for AssocOp binary operator precedences.
|
||||
pub const PREC_PREFIX: i8 = 50;
|
||||
pub const PREC_POSTFIX: i8 = 60;
|
||||
pub const PREC_PAREN: i8 = 99;
|
||||
pub const PREC_UNAMBIGUOUS: i8 = 60;
|
||||
pub const PREC_FORCE_PAREN: i8 = 100;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -325,37 +324,35 @@ impl ExprPrecedence {
|
|||
| ExprPrecedence::Let
|
||||
| ExprPrecedence::Unary => PREC_PREFIX,
|
||||
|
||||
// Unary, postfix
|
||||
ExprPrecedence::Await
|
||||
| ExprPrecedence::Call
|
||||
| ExprPrecedence::MethodCall
|
||||
| ExprPrecedence::Field
|
||||
| ExprPrecedence::Index
|
||||
| ExprPrecedence::Try
|
||||
| ExprPrecedence::InlineAsm
|
||||
| ExprPrecedence::Mac
|
||||
| ExprPrecedence::FormatArgs
|
||||
| ExprPrecedence::OffsetOf
|
||||
| ExprPrecedence::PostfixMatch => PREC_POSTFIX,
|
||||
|
||||
// Never need parens
|
||||
ExprPrecedence::Array
|
||||
| ExprPrecedence::Repeat
|
||||
| ExprPrecedence::Tup
|
||||
| ExprPrecedence::Lit
|
||||
| ExprPrecedence::Path
|
||||
| ExprPrecedence::Paren
|
||||
| ExprPrecedence::If
|
||||
| ExprPrecedence::While
|
||||
| ExprPrecedence::ForLoop
|
||||
| ExprPrecedence::Loop
|
||||
| ExprPrecedence::Match
|
||||
| ExprPrecedence::ConstBlock
|
||||
| ExprPrecedence::Await
|
||||
| ExprPrecedence::Block
|
||||
| ExprPrecedence::TryBlock
|
||||
| ExprPrecedence::Call
|
||||
| ExprPrecedence::ConstBlock
|
||||
| ExprPrecedence::Field
|
||||
| ExprPrecedence::ForLoop
|
||||
| ExprPrecedence::FormatArgs
|
||||
| ExprPrecedence::Gen
|
||||
| ExprPrecedence::If
|
||||
| ExprPrecedence::Index
|
||||
| ExprPrecedence::InlineAsm
|
||||
| ExprPrecedence::Lit
|
||||
| ExprPrecedence::Loop
|
||||
| ExprPrecedence::Mac
|
||||
| ExprPrecedence::Match
|
||||
| ExprPrecedence::MethodCall
|
||||
| ExprPrecedence::OffsetOf
|
||||
| ExprPrecedence::Paren
|
||||
| ExprPrecedence::Path
|
||||
| ExprPrecedence::PostfixMatch
|
||||
| ExprPrecedence::Repeat
|
||||
| ExprPrecedence::Struct
|
||||
| ExprPrecedence::Err => PREC_PAREN,
|
||||
| ExprPrecedence::Try
|
||||
| ExprPrecedence::TryBlock
|
||||
| ExprPrecedence::Tup
|
||||
| ExprPrecedence::While
|
||||
| ExprPrecedence::Err => PREC_UNAMBIGUOUS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,9 @@ ast_lowering_never_pattern_with_guard =
|
|||
|
||||
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
|
||||
|
||||
ast_lowering_no_precise_captures_on_rpitit = `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
|
||||
.note = currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
|
||||
|
||||
ast_lowering_previously_used_here = previously used here
|
||||
|
||||
ast_lowering_register1 = register `{$reg1_name}`
|
||||
|
|
|
|||
|
|
@ -66,14 +66,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let Ok(sig_id) = sig_id else {
|
||||
return false;
|
||||
};
|
||||
if let Some(local_sig_id) = sig_id.as_local() {
|
||||
self.has_self(sig_id, span)
|
||||
}
|
||||
|
||||
fn has_self(&self, def_id: DefId, span: Span) -> bool {
|
||||
if let Some(local_sig_id) = def_id.as_local() {
|
||||
// The value may be missing due to recursive delegation.
|
||||
// Error will be emmited later during HIR ty lowering.
|
||||
self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self)
|
||||
} else {
|
||||
match self.tcx.def_kind(sig_id) {
|
||||
match self.tcx.def_kind(def_id) {
|
||||
DefKind::Fn => false,
|
||||
DefKind::AssocFn => self.tcx.associated_item(sig_id).fn_has_self_parameter,
|
||||
DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter,
|
||||
_ => span_bug!(span, "unexpected DefKind for delegation item"),
|
||||
}
|
||||
}
|
||||
|
|
@ -107,12 +111,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
span: Span,
|
||||
) -> Result<DefId, ErrorGuaranteed> {
|
||||
let sig_id = if self.is_in_trait_impl { item_id } else { path_id };
|
||||
let sig_id =
|
||||
self.resolver.get_partial_res(sig_id).and_then(|r| r.expect_full_res().opt_def_id());
|
||||
sig_id.ok_or_else(|| {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.span_delayed_bug(span, "LoweringContext: couldn't resolve delegation item")
|
||||
self.get_resolution_id(sig_id, span)
|
||||
}
|
||||
|
||||
fn get_resolution_id(&self, node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
|
||||
let def_id =
|
||||
self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id());
|
||||
def_id.ok_or_else(|| {
|
||||
self.tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
format!("LoweringContext: couldn't resolve node {:?} in delegation item", node_id),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +131,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
predicates: &[],
|
||||
has_where_clause_predicates: false,
|
||||
where_clause_span: span,
|
||||
span: span,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -222,12 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}));
|
||||
|
||||
let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments });
|
||||
|
||||
hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
|
||||
span,
|
||||
}
|
||||
self.mk_expr(hir::ExprKind::Path(hir::QPath::Resolved(None, path)), span)
|
||||
}
|
||||
|
||||
fn lower_delegation_body(
|
||||
|
|
@ -236,19 +240,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
param_count: usize,
|
||||
span: Span,
|
||||
) -> BodyId {
|
||||
let path = self.lower_qpath(
|
||||
delegation.id,
|
||||
&delegation.qself,
|
||||
&delegation.path,
|
||||
ParamMode::Optional,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
let block = delegation.body.as_deref();
|
||||
|
||||
self.lower_body(|this| {
|
||||
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
|
||||
let mut args: Vec<hir::Expr<'hir>> = Vec::new();
|
||||
let mut parameters: Vec<hir::Param<'_>> = Vec::with_capacity(param_count);
|
||||
let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
|
||||
|
||||
for idx in 0..param_count {
|
||||
let (param, pat_node_id) = this.generate_param(span);
|
||||
|
|
@ -264,11 +260,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
};
|
||||
self_resolver.visit_block(block);
|
||||
let block = this.lower_block(block, false);
|
||||
hir::Expr {
|
||||
hir_id: this.next_id(),
|
||||
kind: hir::ExprKind::Block(block, None),
|
||||
span: block.span,
|
||||
}
|
||||
this.mk_expr(hir::ExprKind::Block(block, None), block.span)
|
||||
} else {
|
||||
let pat_hir_id = this.lower_node_id(pat_node_id);
|
||||
this.generate_arg(pat_hir_id, span)
|
||||
|
|
@ -276,43 +268,41 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
args.push(arg);
|
||||
}
|
||||
|
||||
let args = self.arena.alloc_from_iter(args);
|
||||
let final_expr = this.generate_call(path, args);
|
||||
let final_expr = this.finalize_body_lowering(delegation, args, span);
|
||||
(this.arena.alloc_from_iter(parameters), final_expr)
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_call(
|
||||
// Generates fully qualified call for the resulting body.
|
||||
fn finalize_body_lowering(
|
||||
&mut self,
|
||||
path: hir::QPath<'hir>,
|
||||
args: &'hir [hir::Expr<'hir>],
|
||||
delegation: &Delegation,
|
||||
args: Vec<hir::Expr<'hir>>,
|
||||
span: Span,
|
||||
) -> hir::Expr<'hir> {
|
||||
let callee = self.arena.alloc(hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Path(path),
|
||||
span: path.span(),
|
||||
});
|
||||
let path = self.lower_qpath(
|
||||
delegation.id,
|
||||
&delegation.qself,
|
||||
&delegation.path,
|
||||
ParamMode::Optional,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
|
||||
let expr = self.arena.alloc(hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Call(callee, args),
|
||||
span: path.span(),
|
||||
});
|
||||
let args = self.arena.alloc_from_iter(args);
|
||||
let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
|
||||
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span));
|
||||
|
||||
let block = self.arena.alloc(hir::Block {
|
||||
stmts: &[],
|
||||
expr: Some(expr),
|
||||
expr: Some(call),
|
||||
hir_id: self.next_id(),
|
||||
rules: hir::BlockCheckMode::DefaultBlock,
|
||||
span: path.span(),
|
||||
span,
|
||||
targeted_by_break: false,
|
||||
});
|
||||
|
||||
hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Block(block, None),
|
||||
span: path.span(),
|
||||
}
|
||||
self.mk_expr(hir::ExprKind::Block(block, None), span)
|
||||
}
|
||||
|
||||
fn generate_delegation_error(
|
||||
|
|
@ -333,11 +323,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let header = self.generate_header_error();
|
||||
let sig = hir::FnSig { decl, header, span };
|
||||
|
||||
let body_id = self.lower_body(|this| {
|
||||
let expr =
|
||||
hir::Expr { hir_id: this.next_id(), kind: hir::ExprKind::Err(err), span: span };
|
||||
(&[], expr)
|
||||
});
|
||||
let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
|
||||
DelegationResults { generics, body_id, sig }
|
||||
}
|
||||
|
||||
|
|
@ -349,6 +335,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
abi: abi::Abi::Rust,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mk_expr(&mut self, kind: hir::ExprKind<'hir>, span: Span) -> hir::Expr<'hir> {
|
||||
hir::Expr { hir_id: self.next_id(), kind, span }
|
||||
}
|
||||
}
|
||||
|
||||
struct SelfResolver<'a> {
|
||||
|
|
|
|||
|
|
@ -424,6 +424,14 @@ pub(crate) struct NoPreciseCapturesOnApit {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_no_precise_captures_on_rpitit)]
|
||||
#[note]
|
||||
pub(crate) struct NoPreciseCapturesOnRpitit {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_yield_in_closure)]
|
||||
pub(crate) struct YieldInClosure {
|
||||
|
|
|
|||
|
|
@ -1594,6 +1594,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
debug!(?captured_lifetimes_to_duplicate);
|
||||
|
||||
match fn_kind {
|
||||
// Deny `use<>` on RPITIT in trait/trait-impl for now.
|
||||
Some(FnDeclKind::Trait | FnDeclKind::Impl) => {
|
||||
if let Some(span) = bounds.iter().find_map(|bound| match *bound {
|
||||
ast::GenericBound::Use(_, span) => Some(span),
|
||||
_ => None,
|
||||
}) {
|
||||
self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span });
|
||||
}
|
||||
}
|
||||
None
|
||||
| Some(
|
||||
FnDeclKind::Fn
|
||||
| FnDeclKind::Inherent
|
||||
| FnDeclKind::ExternFn
|
||||
| FnDeclKind::Closure
|
||||
| FnDeclKind::Pointer,
|
||||
) => {}
|
||||
}
|
||||
|
||||
self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
origin,
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ impl<'a> AstValidator<'a> {
|
|||
{
|
||||
self.dcx().emit_err(errors::InvalidSafetyOnExtern {
|
||||
item_span: span,
|
||||
block: self.current_extern_span(),
|
||||
block: self.current_extern_span().shrink_to_lo(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ pub enum ExternBlockSuggestion {
|
|||
pub struct InvalidSafetyOnExtern {
|
||||
#[primary_span]
|
||||
pub item_span: Span,
|
||||
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||
#[suggestion(code = "unsafe ", applicability = "machine-applicable", style = "verbose")]
|
||||
pub block: Span,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ impl<'a> State<'a> {
|
|||
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
|
||||
let prec = match func.kind {
|
||||
ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
||||
_ => parser::PREC_POSTFIX,
|
||||
_ => parser::PREC_UNAMBIGUOUS,
|
||||
};
|
||||
|
||||
// Independent of parenthesization related to precedence, we must
|
||||
|
|
@ -257,7 +257,7 @@ impl<'a> State<'a> {
|
|||
// boundaries, `$receiver.method()` can be parsed back as a statement
|
||||
// containing an expression if and only if `$receiver` can be parsed as
|
||||
// a statement containing an expression.
|
||||
self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX, fixup);
|
||||
self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS, fixup);
|
||||
|
||||
self.word(".");
|
||||
self.print_ident(segment.ident);
|
||||
|
|
@ -489,7 +489,7 @@ impl<'a> State<'a> {
|
|||
self.space();
|
||||
}
|
||||
MatchKind::Postfix => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
|
||||
self.word_nbsp(".match");
|
||||
}
|
||||
}
|
||||
|
|
@ -549,7 +549,7 @@ impl<'a> State<'a> {
|
|||
self.print_block_with_attrs(blk, attrs);
|
||||
}
|
||||
ast::ExprKind::Await(expr, _) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
|
||||
self.word(".await");
|
||||
}
|
||||
ast::ExprKind::Assign(lhs, rhs, _) => {
|
||||
|
|
@ -568,14 +568,14 @@ impl<'a> State<'a> {
|
|||
self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression());
|
||||
}
|
||||
ast::ExprKind::Field(expr, ident) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS, fixup);
|
||||
self.word(".");
|
||||
self.print_ident(*ident);
|
||||
}
|
||||
ast::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_maybe_paren(
|
||||
expr,
|
||||
parser::PREC_POSTFIX,
|
||||
parser::PREC_UNAMBIGUOUS,
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.word("[");
|
||||
|
|
@ -713,7 +713,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
ast::ExprKind::Try(e) => {
|
||||
self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup);
|
||||
self.print_expr_maybe_paren(e, parser::PREC_UNAMBIGUOUS, fixup);
|
||||
self.word("?")
|
||||
}
|
||||
ast::ExprKind::TryBlock(blk) => {
|
||||
|
|
|
|||
|
|
@ -1151,7 +1151,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
|||
// Get the arguments for the found method, only specifying that `Self` is the receiver type.
|
||||
let Some(possible_rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) else { return };
|
||||
let args = GenericArgs::for_item(tcx, method_def_id, |param, _| {
|
||||
if param.index == 0 {
|
||||
if let ty::GenericParamDefKind::Lifetime = param.kind {
|
||||
tcx.lifetimes.re_erased.into()
|
||||
} else if param.index == 0 && param.name == kw::SelfUpper {
|
||||
possible_rcvr_ty.into()
|
||||
} else if param.index == closure_param.index {
|
||||
closure_ty.into()
|
||||
|
|
@ -1168,7 +1170,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
|||
Obligation::misc(tcx, span, self.mir_def_id(), self.param_env, pred)
|
||||
}));
|
||||
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
if ocx.select_all_or_error().is_empty() && count > 0 {
|
||||
diag.span_suggestion_verbose(
|
||||
tcx.hir().body(*body).value.peel_blocks().span.shrink_to_lo(),
|
||||
"dereference the return value",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
|
|
@ -12,7 +13,6 @@
|
|||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(lint_reasons)]
|
||||
#![feature(proc_macro_internals)]
|
||||
#![feature(proc_macro_quote)]
|
||||
#![feature(rustdoc_internals)]
|
||||
|
|
|
|||
|
|
@ -1490,11 +1490,6 @@ fn print_native_static_libs(
|
|||
let mut lib_args: Vec<_> = all_native_libs
|
||||
.iter()
|
||||
.filter(|l| relevant_lib(sess, l))
|
||||
// Deduplication of successive repeated libraries, see rust-lang/rust#113209
|
||||
//
|
||||
// note: we don't use PartialEq/Eq because NativeLib transitively depends on local
|
||||
// elements like spans, which we don't care about and would make the deduplication impossible
|
||||
.dedup_by(|l1, l2| l1.name == l2.name && l1.kind == l2.kind && l1.verbatim == l2.verbatim)
|
||||
.filter_map(|lib| {
|
||||
let name = lib.name;
|
||||
match lib.kind {
|
||||
|
|
@ -1521,6 +1516,8 @@ fn print_native_static_libs(
|
|||
| NativeLibKind::RawDylib => None,
|
||||
}
|
||||
})
|
||||
// deduplication of consecutive repeated libraries, see rust-lang/rust#113209
|
||||
.dedup()
|
||||
.collect();
|
||||
for path in all_rust_dylibs {
|
||||
// FIXME deduplicate with add_dynamic_crate
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![cfg_attr(not(parallel_compiler), feature(cell_leak))]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
|
|
@ -24,7 +25,6 @@
|
|||
#![feature(extend_one)]
|
||||
#![feature(hash_raw_entry)]
|
||||
#![feature(hasher_prefixfree_extras)]
|
||||
#![feature(lint_reasons)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(min_specialization)]
|
||||
|
|
|
|||
|
|
@ -135,7 +135,12 @@ impl Emitter for JsonEmitter {
|
|||
let data: Vec<FutureBreakageItem<'_>> = diags
|
||||
.into_iter()
|
||||
.map(|mut diag| {
|
||||
if diag.level == crate::Level::Allow {
|
||||
// Allowed or expected lints don't normally (by definition) emit a lint
|
||||
// but future incompat lints are special and are emitted anyway.
|
||||
//
|
||||
// So to avoid ICEs and confused users we "upgrade" the lint level for
|
||||
// those `FutureBreakageItem` to warn.
|
||||
if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) {
|
||||
diag.level = crate::Level::Warning;
|
||||
}
|
||||
FutureBreakageItem {
|
||||
|
|
|
|||
|
|
@ -232,6 +232,8 @@ declare_features! (
|
|||
(accepted, label_break_value, "1.65.0", Some(48594)),
|
||||
/// Allows `let...else` statements.
|
||||
(accepted, let_else, "1.65.0", Some(87335)),
|
||||
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
||||
(accepted, lint_reasons, "CURRENT_RUSTC_VERSION", Some(54503)),
|
||||
/// Allows `break {expr}` with a value inside `loop`s.
|
||||
(accepted, loop_break_value, "1.19.0", Some(37339)),
|
||||
/// Allows use of `?` as the Kleene "at most one" operator in macros.
|
||||
|
|
|
|||
|
|
@ -369,9 +369,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
DuplicatesOk, EncodeCrossCrate::No,
|
||||
),
|
||||
gated!(
|
||||
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
|
||||
EncodeCrossCrate::No, lint_reasons, experimental!(expect)
|
||||
ungated!(
|
||||
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
DuplicatesOk, EncodeCrossCrate::No,
|
||||
),
|
||||
ungated!(
|
||||
forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
|
||||
|
|
|
|||
|
|
@ -512,8 +512,6 @@ declare_features! (
|
|||
/// Allows using `#[link(kind = "link-arg", name = "...")]`
|
||||
/// to pass custom arguments to the linker.
|
||||
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
|
||||
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
|
||||
(unstable, lint_reasons, "1.31.0", Some(54503)),
|
||||
/// Give access to additional metadata about declarative macro meta-variables.
|
||||
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
||||
/// Provides a way to concatenate identifiers using metavariable expressions.
|
||||
|
|
|
|||
|
|
@ -171,10 +171,10 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
|||
}
|
||||
// Resolve any lifetime variables that may have been introduced during normalization.
|
||||
let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
|
||||
// This code path is not reached in any tests, but may be reachable. If
|
||||
// this is triggered, it should be converted to `delayed_bug` and the
|
||||
// triggering case turned into a test.
|
||||
tcx.dcx().bug("encountered errors when checking RPITIT refinement (resolution)");
|
||||
// If resolution didn't fully complete, we cannot continue checking RPITIT refinement, and
|
||||
// delay a bug as the original code contains load-bearing errors.
|
||||
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (resolution)");
|
||||
return;
|
||||
};
|
||||
|
||||
// For quicker lookup, use an `IndexSet` (we don't use one earlier because
|
||||
|
|
|
|||
|
|
@ -211,11 +211,18 @@ fn missing_items_err(
|
|||
.collect::<Vec<_>>()
|
||||
.join("`, `");
|
||||
|
||||
// `Span` before impl block closing brace.
|
||||
let hi = full_impl_span.hi() - BytePos(1);
|
||||
// Point at the place right before the closing brace of the relevant `impl` to suggest
|
||||
// adding the associated item at the end of its body.
|
||||
let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
|
||||
let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span)
|
||||
&& snippet.ends_with("}")
|
||||
{
|
||||
// `Span` before impl block closing brace.
|
||||
let hi = full_impl_span.hi() - BytePos(1);
|
||||
// Point at the place right before the closing brace of the relevant `impl` to suggest
|
||||
// adding the associated item at the end of its body.
|
||||
full_impl_span.with_lo(hi).with_hi(hi)
|
||||
} else {
|
||||
full_impl_span.shrink_to_hi()
|
||||
};
|
||||
|
||||
// Obtain the level of indentation ending in `sugg_sp`.
|
||||
let padding =
|
||||
tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
|
||||
|
|
|
|||
|
|
@ -1120,7 +1120,7 @@ impl<'a> State<'a> {
|
|||
fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let prec = match func.kind {
|
||||
hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
||||
_ => parser::PREC_POSTFIX,
|
||||
_ => parser::PREC_UNAMBIGUOUS,
|
||||
};
|
||||
|
||||
self.print_expr_maybe_paren(func, prec);
|
||||
|
|
@ -1134,7 +1134,7 @@ impl<'a> State<'a> {
|
|||
args: &[hir::Expr<'_>],
|
||||
) {
|
||||
let base_args = args;
|
||||
self.print_expr_maybe_paren(receiver, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(receiver, parser::PREC_UNAMBIGUOUS);
|
||||
self.word(".");
|
||||
self.print_ident(segment.ident);
|
||||
|
||||
|
|
@ -1478,12 +1478,12 @@ impl<'a> State<'a> {
|
|||
self.print_expr_maybe_paren(rhs, prec);
|
||||
}
|
||||
hir::ExprKind::Field(expr, ident) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS);
|
||||
self.word(".");
|
||||
self.print_ident(ident);
|
||||
}
|
||||
hir::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_UNAMBIGUOUS);
|
||||
self.word("[");
|
||||
self.print_expr(index);
|
||||
self.word("]");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use super::method::MethodCallee;
|
|||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
|
||||
use crate::errors;
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
|
||||
use rustc_hir::def::{self, CtorKind, Namespace, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
|
@ -656,7 +656,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
|
||||
if let Ok(rest_snippet) = rest_snippet {
|
||||
let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX {
|
||||
let sugg = if callee_expr.precedence().order() >= PREC_UNAMBIGUOUS {
|
||||
vec![
|
||||
(up_to_rcvr_span, "".to_string()),
|
||||
(rest_span, format!(".{}({rest_snippet}", segment.ident)),
|
||||
|
|
|
|||
|
|
@ -946,7 +946,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
|
||||
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
|
||||
let expr_prec = self.expr.precedence().order();
|
||||
let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX;
|
||||
let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
|
||||
let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
|
||||
let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
|||
use core::cmp::min;
|
||||
use core::iter;
|
||||
use hir::def_id::LocalDefId;
|
||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_UNAMBIGUOUS};
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -1329,7 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
{
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
|
||||
|
||||
let mut sugg = if expr.precedence().order() >= PREC_POSTFIX {
|
||||
let mut sugg = if expr.precedence().order() >= PREC_UNAMBIGUOUS {
|
||||
vec![(span.shrink_to_hi(), ".into()".to_owned())]
|
||||
} else {
|
||||
vec![
|
||||
|
|
@ -2868,7 +2868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
"change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
|
||||
);
|
||||
|
||||
let close_paren = if expr.precedence().order() < PREC_POSTFIX {
|
||||
let close_paren = if expr.precedence().order() < PREC_UNAMBIGUOUS {
|
||||
sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
|
||||
")"
|
||||
} else {
|
||||
|
|
@ -2893,7 +2893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let len = src.trim_end_matches(&checked_ty.to_string()).len();
|
||||
expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
|
||||
},
|
||||
if expr.precedence().order() < PREC_POSTFIX {
|
||||
if expr.precedence().order() < PREC_UNAMBIGUOUS {
|
||||
// Readd `)`
|
||||
format!("{expected_ty})")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -209,8 +209,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
||||
{
|
||||
ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
|
||||
Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
|
||||
ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
|
||||
origin_expr.then_some(ConsiderAddingAwait::FutureSugg {
|
||||
span: then_span.shrink_to_hi(),
|
||||
})
|
||||
}
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use rustc_middle::query::Providers;
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
|
||||
use rustc_session::lint::LintExpectationId;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
|
|
@ -11,10 +10,6 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
}
|
||||
|
||||
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
|
||||
if !tcx.features().active(sym::lint_reasons) {
|
||||
return;
|
||||
}
|
||||
|
||||
let lint_expectations = tcx.lint_expectations(());
|
||||
let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids();
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ use rustc_session::lint::{
|
|||
},
|
||||
Level, Lint, LintExpectationId, LintId,
|
||||
};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
|
@ -788,15 +787,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
ast::MetaItemKind::NameValue(ref name_value) => {
|
||||
if item.path == sym::reason {
|
||||
if let ast::LitKind::Str(rationale, _) = name_value.kind {
|
||||
if !self.features.lint_reasons {
|
||||
feature_err(
|
||||
&self.sess,
|
||||
sym::lint_reasons,
|
||||
item.span,
|
||||
"lint reasons are experimental",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
reason = Some(rationale);
|
||||
} else {
|
||||
sess.dcx().emit_err(MalformedAttribute {
|
||||
|
|
|
|||
|
|
@ -608,13 +608,13 @@ declare_lint! {
|
|||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `unfulfilled_lint_expectations` lint detects lint trigger expectations
|
||||
/// that have not been fulfilled.
|
||||
/// The `unfulfilled_lint_expectations` lint detects when a lint expectation is
|
||||
/// unfulfilled.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(lint_reasons)]
|
||||
/// #![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
///
|
||||
/// #[expect(unused_variables)]
|
||||
/// let x = 10;
|
||||
|
|
@ -625,24 +625,14 @@ declare_lint! {
|
|||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// It was expected that the marked code would emit a lint. This expectation
|
||||
/// has not been fulfilled.
|
||||
/// The `#[expect]` attribute can be used to create a lint expectation. The
|
||||
/// expectation is fulfilled, if a `#[warn]` attribute at the same location
|
||||
/// would result in a lint emission. If the expectation is unfulfilled,
|
||||
/// because no lint was emitted, this lint will be emitted on the attribute.
|
||||
///
|
||||
/// The `expect` attribute can be removed if this is intended behavior otherwise
|
||||
/// it should be investigated why the expected lint is no longer issued.
|
||||
///
|
||||
/// In rare cases, the expectation might be emitted at a different location than
|
||||
/// shown in the shown code snippet. In most cases, the `#[expect]` attribute
|
||||
/// works when added to the outer scope. A few lints can only be expected
|
||||
/// on a crate level.
|
||||
///
|
||||
/// Part of RFC 2383. The progress is being tracked in [#54503]
|
||||
///
|
||||
/// [#54503]: https://github.com/rust-lang/rust/issues/54503
|
||||
pub UNFULFILLED_LINT_EXPECTATIONS,
|
||||
Warn,
|
||||
"unfulfilled lint expectation",
|
||||
@feature_gate = rustc_span::sym::lint_reasons;
|
||||
"unfulfilled lint expectation"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,7 @@ struct RustArchiveMember {
|
|||
Archive::Child Child;
|
||||
|
||||
RustArchiveMember()
|
||||
: Filename(nullptr), Name(nullptr),
|
||||
Child(nullptr, nullptr, nullptr)
|
||||
{
|
||||
}
|
||||
: Filename(nullptr), Name(nullptr), Child(nullptr, nullptr, nullptr) {}
|
||||
~RustArchiveMember() {}
|
||||
};
|
||||
|
||||
|
|
@ -27,11 +24,8 @@ struct RustArchiveIterator {
|
|||
std::unique_ptr<Error> Err;
|
||||
|
||||
RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End,
|
||||
std::unique_ptr<Error> Err)
|
||||
: First(true),
|
||||
Cur(Cur),
|
||||
End(End),
|
||||
Err(std::move(Err)) {}
|
||||
std::unique_ptr<Error> Err)
|
||||
: First(true), Cur(Cur), End(End), Err(std::move(Err)) {}
|
||||
};
|
||||
|
||||
enum class LLVMRustArchiveKind {
|
||||
|
|
@ -66,8 +60,8 @@ typedef Archive::Child const *LLVMRustArchiveChildConstRef;
|
|||
typedef RustArchiveIterator *LLVMRustArchiveIteratorRef;
|
||||
|
||||
extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr =
|
||||
MemoryBuffer::getFile(Path, /*IsText*/false, /*RequiresNullTerminator=*/false);
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = MemoryBuffer::getFile(
|
||||
Path, /*IsText*/ false, /*RequiresNullTerminator=*/false);
|
||||
if (!BufOr) {
|
||||
LLVMRustSetLastError(BufOr.getError().message().c_str());
|
||||
return nullptr;
|
||||
|
|
@ -146,8 +140,8 @@ extern "C" const char *
|
|||
LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) {
|
||||
Expected<StringRef> NameOrErr = Child->getName();
|
||||
if (!NameOrErr) {
|
||||
// rustc_codegen_llvm currently doesn't use this error string, but it might be
|
||||
// useful in the future, and in the meantime this tells LLVM that the
|
||||
// rustc_codegen_llvm currently doesn't use this error string, but it might
|
||||
// be useful in the future, and in the meantime this tells LLVM that the
|
||||
// error was not ignored and that it shouldn't abort the process.
|
||||
LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str());
|
||||
return nullptr;
|
||||
|
|
@ -172,10 +166,9 @@ extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
|
|||
delete Member;
|
||||
}
|
||||
|
||||
extern "C" LLVMRustResult
|
||||
LLVMRustWriteArchive(char *Dst, size_t NumMembers,
|
||||
const LLVMRustArchiveMemberRef *NewMembers,
|
||||
bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {
|
||||
extern "C" LLVMRustResult LLVMRustWriteArchive(
|
||||
char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers,
|
||||
bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {
|
||||
|
||||
std::vector<NewArchiveMember> Members;
|
||||
auto Kind = fromRust(RustKind);
|
||||
|
|
@ -206,8 +199,10 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers,
|
|||
#if LLVM_VERSION_LT(18, 0)
|
||||
auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false);
|
||||
#else
|
||||
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab : SymtabWritingMode::NoSymtab;
|
||||
auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
|
||||
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab
|
||||
: SymtabWritingMode::NoSymtab;
|
||||
auto Result =
|
||||
writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
|
||||
#endif
|
||||
if (!Result)
|
||||
return LLVMRustResult::Success;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include "SuppressLLVMWarnings.h"
|
||||
#include "llvm/Linker/Linker.h"
|
||||
#include "SuppressLLVMWarnings.h"
|
||||
|
||||
#include "LLVMWrapper.h"
|
||||
|
||||
|
|
@ -9,26 +9,18 @@ struct RustLinker {
|
|||
Linker L;
|
||||
LLVMContext &Ctx;
|
||||
|
||||
RustLinker(Module &M) :
|
||||
L(M),
|
||||
Ctx(M.getContext())
|
||||
{}
|
||||
RustLinker(Module &M) : L(M), Ctx(M.getContext()) {}
|
||||
};
|
||||
|
||||
extern "C" RustLinker*
|
||||
LLVMRustLinkerNew(LLVMModuleRef DstRef) {
|
||||
extern "C" RustLinker *LLVMRustLinkerNew(LLVMModuleRef DstRef) {
|
||||
Module *Dst = unwrap(DstRef);
|
||||
|
||||
return new RustLinker(*Dst);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
LLVMRustLinkerFree(RustLinker *L) {
|
||||
delete L;
|
||||
}
|
||||
extern "C" void LLVMRustLinkerFree(RustLinker *L) { delete L; }
|
||||
|
||||
extern "C" bool
|
||||
LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) {
|
||||
extern "C" bool LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) {
|
||||
std::unique_ptr<MemoryBuffer> Buf =
|
||||
MemoryBuffer::getMemBufferCopy(StringRef(BC, Len));
|
||||
|
||||
|
|
|
|||
|
|
@ -2,23 +2,25 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "LLVMWrapper.h"
|
||||
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||
#include "llvm/Bitcode/BitcodeWriter.h"
|
||||
#include "llvm/CodeGen/CommandFlags.h"
|
||||
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
||||
#include "llvm/IR/AutoUpgrade.h"
|
||||
#include "llvm/IR/AssemblyAnnotationWriter.h"
|
||||
#include "llvm/IR/AutoUpgrade.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Object/IRObjectFile.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/Passes/PassPlugin.h"
|
||||
#include "llvm/Passes/StandardInstrumentations.h"
|
||||
|
|
@ -33,26 +35,24 @@
|
|||
#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
|
||||
#include "llvm/Transforms/Utils/AddDiscriminators.h"
|
||||
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Bitcode/BitcodeWriter.h"
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#endif
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h"
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
#include "llvm/Support/PGOOptions.h"
|
||||
#endif
|
||||
#include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
|
||||
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
|
||||
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
|
||||
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
|
||||
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
|
||||
#include "llvm/Transforms/Utils.h"
|
||||
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
|
||||
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
|
||||
#include "llvm/Transforms/Utils.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ extern "C" void LLVMRustTimeTraceProfilerFinishThread() {
|
|||
timeTraceProfilerFinishThread();
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustTimeTraceProfilerFinish(const char* FileName) {
|
||||
extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) {
|
||||
auto FN = StringRef(FileName);
|
||||
std::error_code EC;
|
||||
auto OS = raw_fd_ostream(FN, EC, sys::fs::CD_CreateAlways);
|
||||
|
|
@ -188,7 +188,7 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char* FileName) {
|
|||
SUBTARGET_HEXAGON \
|
||||
SUBTARGET_XTENSA \
|
||||
SUBTARGET_RISCV \
|
||||
SUBTARGET_LOONGARCH \
|
||||
SUBTARGET_LOONGARCH
|
||||
|
||||
#define SUBTARGET(x) \
|
||||
namespace llvm { \
|
||||
|
|
@ -215,8 +215,7 @@ enum class LLVMRustCodeModel {
|
|||
None,
|
||||
};
|
||||
|
||||
static std::optional<CodeModel::Model>
|
||||
fromRust(LLVMRustCodeModel Model) {
|
||||
static std::optional<CodeModel::Model> fromRust(LLVMRustCodeModel Model) {
|
||||
switch (Model) {
|
||||
case LLVMRustCodeModel::Tiny:
|
||||
return CodeModel::Tiny;
|
||||
|
|
@ -243,9 +242,9 @@ enum class LLVMRustCodeGenOptLevel {
|
|||
};
|
||||
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
using CodeGenOptLevelEnum = llvm::CodeGenOptLevel;
|
||||
using CodeGenOptLevelEnum = llvm::CodeGenOptLevel;
|
||||
#else
|
||||
using CodeGenOptLevelEnum = llvm::CodeGenOpt::Level;
|
||||
using CodeGenOptLevelEnum = llvm::CodeGenOpt::Level;
|
||||
#endif
|
||||
|
||||
static CodeGenOptLevelEnum fromRust(LLVMRustCodeGenOptLevel Level) {
|
||||
|
|
@ -319,48 +318,49 @@ static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) {
|
|||
}
|
||||
|
||||
/// getLongestEntryLength - Return the length of the longest entry in the table.
|
||||
template<typename KV>
|
||||
static size_t getLongestEntryLength(ArrayRef<KV> Table) {
|
||||
template <typename KV> static size_t getLongestEntryLength(ArrayRef<KV> Table) {
|
||||
size_t MaxLen = 0;
|
||||
for (auto &I : Table)
|
||||
MaxLen = std::max(MaxLen, std::strlen(I.Key));
|
||||
return MaxLen;
|
||||
}
|
||||
|
||||
using PrintBackendInfo = void(void*, const char* Data, size_t Len);
|
||||
using PrintBackendInfo = void(void *, const char *Data, size_t Len);
|
||||
|
||||
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
|
||||
const char* TargetCPU,
|
||||
PrintBackendInfo Print,
|
||||
void* Out) {
|
||||
const char *TargetCPU,
|
||||
PrintBackendInfo Print, void *Out) {
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch();
|
||||
const Triple::ArchType HostArch =
|
||||
Triple(sys::getDefaultTargetTriple()).getArch();
|
||||
const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
|
||||
|
||||
std::ostringstream Buf;
|
||||
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getAllProcessorDescriptions();
|
||||
const ArrayRef<SubtargetSubTypeKV> CPUTable =
|
||||
MCInfo->getAllProcessorDescriptions();
|
||||
unsigned MaxCPULen = getLongestEntryLength(CPUTable);
|
||||
|
||||
Buf << "Available CPUs for this target:\n";
|
||||
// Don't print the "native" entry when the user specifies --target with a
|
||||
// different arch since that could be wrong or misleading.
|
||||
if (HostArch == TargetArch) {
|
||||
MaxCPULen = std::max(MaxCPULen, (unsigned) std::strlen("native"));
|
||||
MaxCPULen = std::max(MaxCPULen, (unsigned)std::strlen("native"));
|
||||
const StringRef HostCPU = sys::getHostCPUName();
|
||||
Buf << " " << std::left << std::setw(MaxCPULen) << "native"
|
||||
<< " - Select the CPU of the current host "
|
||||
"(currently " << HostCPU.str() << ").\n";
|
||||
"(currently "
|
||||
<< HostCPU.str() << ").\n";
|
||||
}
|
||||
for (auto &CPU : CPUTable) {
|
||||
// Compare cpu against current target to label the default
|
||||
if (strcmp(CPU.Key, TargetCPU) == 0) {
|
||||
Buf << " " << std::left << std::setw(MaxCPULen) << CPU.Key
|
||||
<< " - This is the default target CPU for the current build target "
|
||||
"(currently " << Target->getTargetTriple().str() << ").";
|
||||
}
|
||||
else {
|
||||
"(currently "
|
||||
<< Target->getTargetTriple().str() << ").";
|
||||
} else {
|
||||
Buf << " " << CPU.Key;
|
||||
}
|
||||
Buf << "\n";
|
||||
|
|
@ -374,7 +374,8 @@ extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
|||
#if LLVM_VERSION_GE(18, 0)
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable =
|
||||
MCInfo->getAllProcessorFeatures();
|
||||
return FeatTable.size();
|
||||
#else
|
||||
return 0;
|
||||
|
|
@ -382,18 +383,20 @@ extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
|||
}
|
||||
|
||||
extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index,
|
||||
const char** Feature, const char** Desc) {
|
||||
const char **Feature,
|
||||
const char **Desc) {
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable =
|
||||
MCInfo->getAllProcessorFeatures();
|
||||
const SubtargetFeatureKV Feat = FeatTable[Index];
|
||||
*Feature = Feat.Key;
|
||||
*Desc = Feat.Desc;
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" const char* LLVMRustGetHostCPUName(size_t *len) {
|
||||
extern "C" const char *LLVMRustGetHostCPUName(size_t *len) {
|
||||
StringRef Name = sys::getHostCPUName();
|
||||
*len = Name.size();
|
||||
return Name.data();
|
||||
|
|
@ -403,19 +406,11 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
const char *TripleStr, const char *CPU, const char *Feature,
|
||||
const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc,
|
||||
LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat,
|
||||
bool FunctionSections,
|
||||
bool DataSections,
|
||||
bool UniqueSectionNames,
|
||||
bool TrapUnreachable,
|
||||
bool Singlethread,
|
||||
bool AsmComments,
|
||||
bool EmitStackSizeSection,
|
||||
bool RelaxELFRelocations,
|
||||
bool UseInitArray,
|
||||
const char *SplitDwarfFile,
|
||||
const char *OutputObjFile,
|
||||
const char *DebugInfoCompression,
|
||||
bool UseEmulatedTls,
|
||||
bool FunctionSections, bool DataSections, bool UniqueSectionNames,
|
||||
bool TrapUnreachable, bool Singlethread, bool AsmComments,
|
||||
bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray,
|
||||
const char *SplitDwarfFile, const char *OutputObjFile,
|
||||
const char *DebugInfoCompression, bool UseEmulatedTls,
|
||||
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
|
||||
|
||||
auto OptLevel = fromRust(RustOptLevel);
|
||||
|
|
@ -444,18 +439,20 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
Options.MCOptions.PreserveAsmComments = AsmComments;
|
||||
Options.MCOptions.ABIName = ABIStr;
|
||||
if (SplitDwarfFile) {
|
||||
Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
|
||||
Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
|
||||
}
|
||||
if (OutputObjFile) {
|
||||
Options.ObjectFilenameForDebug = OutputObjFile;
|
||||
Options.ObjectFilenameForDebug = OutputObjFile;
|
||||
}
|
||||
if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) {
|
||||
if (!strcmp("zlib", DebugInfoCompression) &&
|
||||
llvm::compression::zlib::isAvailable()) {
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
Options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib;
|
||||
#else
|
||||
Options.CompressDebugSections = DebugCompressionType::Zlib;
|
||||
#endif
|
||||
} else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) {
|
||||
} else if (!strcmp("zstd", DebugInfoCompression) &&
|
||||
llvm::compression::zstd::isAvailable()) {
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
Options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd;
|
||||
#else
|
||||
|
|
@ -499,24 +496,21 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
|
||||
Options.EmitStackSizeSection = EmitStackSizeSection;
|
||||
|
||||
|
||||
if (ArgsCstrBuff != nullptr)
|
||||
{
|
||||
if (ArgsCstrBuff != nullptr) {
|
||||
int buffer_offset = 0;
|
||||
assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0');
|
||||
|
||||
const size_t arg0_len = std::strlen(ArgsCstrBuff);
|
||||
char* arg0 = new char[arg0_len + 1];
|
||||
char *arg0 = new char[arg0_len + 1];
|
||||
memcpy(arg0, ArgsCstrBuff, arg0_len);
|
||||
arg0[arg0_len] = '\0';
|
||||
buffer_offset += arg0_len + 1;
|
||||
|
||||
const int num_cmd_arg_strings =
|
||||
std::count(&ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0');
|
||||
const int num_cmd_arg_strings = std::count(
|
||||
&ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0');
|
||||
|
||||
std::string* cmd_arg_strings = new std::string[num_cmd_arg_strings];
|
||||
for (int i = 0; i < num_cmd_arg_strings; ++i)
|
||||
{
|
||||
std::string *cmd_arg_strings = new std::string[num_cmd_arg_strings];
|
||||
for (int i = 0; i < num_cmd_arg_strings; ++i) {
|
||||
assert(buffer_offset < ArgsCstrBuffLen);
|
||||
const int len = std::strlen(ArgsCstrBuff + buffer_offset);
|
||||
cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len);
|
||||
|
|
@ -527,7 +521,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
|
||||
Options.MCOptions.Argv0 = arg0;
|
||||
Options.MCOptions.CommandLineArgs =
|
||||
llvm::ArrayRef<std::string>(cmd_arg_strings, num_cmd_arg_strings);
|
||||
llvm::ArrayRef<std::string>(cmd_arg_strings, num_cmd_arg_strings);
|
||||
}
|
||||
|
||||
TargetMachine *TM = TheTarget->createTargetMachine(
|
||||
|
|
@ -537,7 +531,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
|
||||
extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
|
||||
|
||||
MCTargetOptions& MCOptions = unwrap(TM)->Options.MCOptions;
|
||||
MCTargetOptions &MCOptions = unwrap(TM)->Options.MCOptions;
|
||||
delete[] MCOptions.Argv0;
|
||||
delete[] MCOptions.CommandLineArgs.data();
|
||||
|
||||
|
|
@ -613,7 +607,7 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
|
|||
auto DOS = raw_fd_ostream(DwoPath, EC, sys::fs::OF_None);
|
||||
EC.clear();
|
||||
if (EC)
|
||||
ErrorInfo = EC.message();
|
||||
ErrorInfo = EC.message();
|
||||
if (ErrorInfo != "") {
|
||||
LLVMRustSetLastError(ErrorInfo.c_str());
|
||||
return LLVMRustResult::Failure;
|
||||
|
|
@ -633,10 +627,12 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
|
|||
return LLVMRustResult::Success;
|
||||
}
|
||||
|
||||
extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler
|
||||
const char*, // pass name
|
||||
const char*); // IR name
|
||||
extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler
|
||||
extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(
|
||||
void *, // LlvmSelfProfiler
|
||||
const char *, // pass name
|
||||
const char *); // IR name
|
||||
extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(
|
||||
void *); // LlvmSelfProfiler
|
||||
|
||||
std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) {
|
||||
if (const auto *Cast = any_cast<const Module *>(&WrappedIr))
|
||||
|
|
@ -650,35 +646,35 @@ std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) {
|
|||
return "<UNKNOWN>";
|
||||
}
|
||||
|
||||
|
||||
void LLVMSelfProfileInitializeCallbacks(
|
||||
PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler,
|
||||
PassInstrumentationCallbacks &PIC, void *LlvmSelfProfiler,
|
||||
LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
|
||||
LLVMRustSelfProfileAfterPassCallback AfterPassCallback) {
|
||||
PIC.registerBeforeNonSkippedPassCallback([LlvmSelfProfiler, BeforePassCallback](
|
||||
StringRef Pass, llvm::Any Ir) {
|
||||
std::string PassName = Pass.str();
|
||||
std::string IrName = LLVMRustwrappedIrGetName(Ir);
|
||||
BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
|
||||
});
|
||||
PIC.registerBeforeNonSkippedPassCallback(
|
||||
[LlvmSelfProfiler, BeforePassCallback](StringRef Pass, llvm::Any Ir) {
|
||||
std::string PassName = Pass.str();
|
||||
std::string IrName = LLVMRustwrappedIrGetName(Ir);
|
||||
BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
|
||||
});
|
||||
|
||||
PIC.registerAfterPassCallback(
|
||||
[LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any IR,
|
||||
const PreservedAnalyses &Preserved) {
|
||||
[LlvmSelfProfiler, AfterPassCallback](
|
||||
StringRef Pass, llvm::Any IR, const PreservedAnalyses &Preserved) {
|
||||
AfterPassCallback(LlvmSelfProfiler);
|
||||
});
|
||||
|
||||
PIC.registerAfterPassInvalidatedCallback(
|
||||
[LlvmSelfProfiler, AfterPassCallback](StringRef Pass, const PreservedAnalyses &Preserved) {
|
||||
[LlvmSelfProfiler,
|
||||
AfterPassCallback](StringRef Pass, const PreservedAnalyses &Preserved) {
|
||||
AfterPassCallback(LlvmSelfProfiler);
|
||||
});
|
||||
|
||||
PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback](
|
||||
StringRef Pass, llvm::Any Ir) {
|
||||
std::string PassName = Pass.str();
|
||||
std::string IrName = LLVMRustwrappedIrGetName(Ir);
|
||||
BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
|
||||
});
|
||||
PIC.registerBeforeAnalysisCallback(
|
||||
[LlvmSelfProfiler, BeforePassCallback](StringRef Pass, llvm::Any Ir) {
|
||||
std::string PassName = Pass.str();
|
||||
std::string IrName = LLVMRustwrappedIrGetName(Ir);
|
||||
BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
|
||||
});
|
||||
|
||||
PIC.registerAfterAnalysisCallback(
|
||||
[LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) {
|
||||
|
|
@ -704,7 +700,7 @@ struct LLVMRustSanitizerOptions {
|
|||
bool SanitizeKCFI;
|
||||
bool SanitizeMemory;
|
||||
bool SanitizeMemoryRecover;
|
||||
int SanitizeMemoryTrackOrigins;
|
||||
int SanitizeMemoryTrackOrigins;
|
||||
bool SanitizeThread;
|
||||
bool SanitizeHWAddress;
|
||||
bool SanitizeHWAddressRecover;
|
||||
|
|
@ -712,31 +708,25 @@ struct LLVMRustSanitizerOptions {
|
|||
bool SanitizeKernelAddressRecover;
|
||||
};
|
||||
|
||||
extern "C" LLVMRustResult
|
||||
LLVMRustOptimize(
|
||||
LLVMModuleRef ModuleRef,
|
||||
LLVMTargetMachineRef TMRef,
|
||||
LLVMRustPassBuilderOptLevel OptLevelRust,
|
||||
LLVMRustOptStage OptStage,
|
||||
bool IsLinkerPluginLTO,
|
||||
bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers,
|
||||
bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize,
|
||||
bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
|
||||
LLVMRustSanitizerOptions *SanitizerOptions,
|
||||
const char *PGOGenPath, const char *PGOUsePath,
|
||||
bool InstrumentCoverage, const char *InstrProfileOutput,
|
||||
bool InstrumentGCOV,
|
||||
extern "C" LLVMRustResult LLVMRustOptimize(
|
||||
LLVMModuleRef ModuleRef, LLVMTargetMachineRef TMRef,
|
||||
LLVMRustPassBuilderOptLevel OptLevelRust, LLVMRustOptStage OptStage,
|
||||
bool IsLinkerPluginLTO, bool NoPrepopulatePasses, bool VerifyIR,
|
||||
bool UseThinLTOBuffers, bool MergeFunctions, bool UnrollLoops,
|
||||
bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls,
|
||||
bool EmitLifetimeMarkers, LLVMRustSanitizerOptions *SanitizerOptions,
|
||||
const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage,
|
||||
const char *InstrProfileOutput, bool InstrumentGCOV,
|
||||
const char *PGOSampleUsePath, bool DebugInfoForProfiling,
|
||||
void* LlvmSelfProfiler,
|
||||
void *LlvmSelfProfiler,
|
||||
LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
|
||||
LLVMRustSelfProfileAfterPassCallback AfterPassCallback,
|
||||
const char *ExtraPasses, size_t ExtraPassesLen,
|
||||
const char *LLVMPlugins, size_t LLVMPluginsLen) {
|
||||
const char *ExtraPasses, size_t ExtraPassesLen, const char *LLVMPlugins,
|
||||
size_t LLVMPluginsLen) {
|
||||
Module *TheModule = unwrap(ModuleRef);
|
||||
TargetMachine *TM = unwrap(TMRef);
|
||||
OptimizationLevel OptLevel = fromRust(OptLevelRust);
|
||||
|
||||
|
||||
PipelineTuningOptions PTO;
|
||||
PTO.LoopUnrolling = UnrollLoops;
|
||||
PTO.LoopInterleaving = UnrollLoops;
|
||||
|
|
@ -751,38 +741,39 @@ LLVMRustOptimize(
|
|||
StandardInstrumentations SI(TheModule->getContext(), DebugPassManager);
|
||||
SI.registerCallbacks(PIC);
|
||||
|
||||
if (LlvmSelfProfiler){
|
||||
LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback);
|
||||
if (LlvmSelfProfiler) {
|
||||
LLVMSelfProfileInitializeCallbacks(PIC, LlvmSelfProfiler,
|
||||
BeforePassCallback, AfterPassCallback);
|
||||
}
|
||||
|
||||
std::optional<PGOOptions> PGOOpt;
|
||||
auto FS = vfs::getRealFileSystem();
|
||||
if (PGOGenPath) {
|
||||
assert(!PGOUsePath && !PGOSampleUsePath);
|
||||
PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS,
|
||||
PGOOptions::IRInstr, PGOOptions::NoCSAction,
|
||||
PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS, PGOOptions::IRInstr,
|
||||
PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
} else if (PGOUsePath) {
|
||||
assert(!PGOSampleUsePath);
|
||||
PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS,
|
||||
PGOOptions::IRUse, PGOOptions::NoCSAction,
|
||||
PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS, PGOOptions::IRUse,
|
||||
PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
} else if (PGOSampleUsePath) {
|
||||
PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS,
|
||||
PGOOptions::SampleUse, PGOOptions::NoCSAction,
|
||||
PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse,
|
||||
PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
DebugInfoForProfiling);
|
||||
} else if (DebugInfoForProfiling) {
|
||||
PGOOpt = PGOOptions("", "", "", "", FS,
|
||||
PGOOptions::NoAction, PGOOptions::NoCSAction,
|
||||
PGOOpt = PGOOptions("", "", "", "", FS, PGOOptions::NoAction,
|
||||
PGOOptions::NoCSAction,
|
||||
#if LLVM_VERSION_GE(19, 0)
|
||||
PGOOptions::ColdFuncOpt::Default,
|
||||
#endif
|
||||
|
|
@ -799,7 +790,7 @@ LLVMRustOptimize(
|
|||
auto PluginsStr = StringRef(LLVMPlugins, LLVMPluginsLen);
|
||||
SmallVector<StringRef> Plugins;
|
||||
PluginsStr.split(Plugins, ',', -1, false);
|
||||
for (auto PluginPath: Plugins) {
|
||||
for (auto PluginPath : Plugins) {
|
||||
auto Plugin = PassPlugin::Load(PluginPath.str());
|
||||
if (!Plugin) {
|
||||
auto Err = Plugin.takeError();
|
||||
|
|
@ -814,7 +805,8 @@ LLVMRustOptimize(
|
|||
FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); });
|
||||
|
||||
Triple TargetTriple(TheModule->getTargetTriple());
|
||||
std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple));
|
||||
std::unique_ptr<TargetLibraryInfoImpl> TLII(
|
||||
new TargetLibraryInfoImpl(TargetTriple));
|
||||
if (DisableSimplifyLibCalls)
|
||||
TLII->disableAllFunctions();
|
||||
FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
|
||||
|
|
@ -825,58 +817,53 @@ LLVMRustOptimize(
|
|||
PB.registerLoopAnalyses(LAM);
|
||||
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
|
||||
|
||||
// We manually collect pipeline callbacks so we can apply them at O0, where the
|
||||
// PassBuilder does not create a pipeline.
|
||||
// We manually collect pipeline callbacks so we can apply them at O0, where
|
||||
// the PassBuilder does not create a pipeline.
|
||||
std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>>
|
||||
PipelineStartEPCallbacks;
|
||||
std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>>
|
||||
OptimizerLastEPCallbacks;
|
||||
|
||||
if (!IsLinkerPluginLTO
|
||||
&& SanitizerOptions && SanitizerOptions->SanitizeCFI
|
||||
&& !NoPrepopulatePasses) {
|
||||
if (!IsLinkerPluginLTO && SanitizerOptions && SanitizerOptions->SanitizeCFI &&
|
||||
!NoPrepopulatePasses) {
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(LowerTypeTestsPass(/*ExportSummary=*/nullptr,
|
||||
/*ImportSummary=*/nullptr,
|
||||
/*DropTypeTests=*/false));
|
||||
}
|
||||
);
|
||||
[](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(LowerTypeTestsPass(/*ExportSummary=*/nullptr,
|
||||
/*ImportSummary=*/nullptr,
|
||||
/*DropTypeTests=*/false));
|
||||
});
|
||||
}
|
||||
|
||||
if (VerifyIR) {
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[VerifyIR](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(VerifierPass());
|
||||
}
|
||||
);
|
||||
[VerifyIR](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(VerifierPass());
|
||||
});
|
||||
}
|
||||
|
||||
if (InstrumentGCOV) {
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(GCOVProfilerPass(GCOVOptions::getDefault()));
|
||||
}
|
||||
);
|
||||
[](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(GCOVProfilerPass(GCOVOptions::getDefault()));
|
||||
});
|
||||
}
|
||||
|
||||
if (InstrumentCoverage) {
|
||||
PipelineStartEPCallbacks.push_back(
|
||||
[InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
InstrProfOptions Options;
|
||||
if (InstrProfileOutput) {
|
||||
Options.InstrProfileOutput = InstrProfileOutput;
|
||||
}
|
||||
// cargo run tests in multhreading mode by default
|
||||
// so use atomics for coverage counters
|
||||
Options.Atomic = true;
|
||||
[InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
InstrProfOptions Options;
|
||||
if (InstrProfileOutput) {
|
||||
Options.InstrProfileOutput = InstrProfileOutput;
|
||||
}
|
||||
// cargo run tests in multhreading mode by default
|
||||
// so use atomics for coverage counters
|
||||
Options.Atomic = true;
|
||||
#if LLVM_VERSION_GE(18, 0)
|
||||
MPM.addPass(InstrProfilingLoweringPass(Options, false));
|
||||
MPM.addPass(InstrProfilingLoweringPass(Options, false));
|
||||
#else
|
||||
MPM.addPass(InstrProfiling(Options, false));
|
||||
MPM.addPass(InstrProfiling(Options, false));
|
||||
#endif
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (SanitizerOptions) {
|
||||
|
|
@ -886,10 +873,9 @@ LLVMRustOptimize(
|
|||
SanitizerOptions->SanitizeDataFlowABIList +
|
||||
SanitizerOptions->SanitizeDataFlowABIListLen);
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(DataFlowSanitizerPass(ABIListFiles));
|
||||
}
|
||||
);
|
||||
[ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(DataFlowSanitizerPass(ABIListFiles));
|
||||
});
|
||||
}
|
||||
|
||||
if (SanitizerOptions->SanitizeMemory) {
|
||||
|
|
@ -899,54 +885,54 @@ LLVMRustOptimize(
|
|||
/*CompileKernel=*/false,
|
||||
/*EagerChecks=*/true);
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[Options](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(MemorySanitizerPass(Options));
|
||||
}
|
||||
);
|
||||
[Options](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(MemorySanitizerPass(Options));
|
||||
});
|
||||
}
|
||||
|
||||
if (SanitizerOptions->SanitizeThread) {
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
MPM.addPass(ModuleThreadSanitizerPass());
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
|
||||
}
|
||||
);
|
||||
OptimizerLastEPCallbacks.push_back([](ModulePassManager &MPM,
|
||||
OptimizationLevel Level) {
|
||||
MPM.addPass(ModuleThreadSanitizerPass());
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
|
||||
});
|
||||
}
|
||||
|
||||
if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) {
|
||||
if (SanitizerOptions->SanitizeAddress ||
|
||||
SanitizerOptions->SanitizeKernelAddress) {
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
auto CompileKernel = SanitizerOptions->SanitizeKernelAddress;
|
||||
AddressSanitizerOptions opts = AddressSanitizerOptions{
|
||||
CompileKernel,
|
||||
SanitizerOptions->SanitizeAddressRecover
|
||||
|| SanitizerOptions->SanitizeKernelAddressRecover,
|
||||
/*UseAfterScope=*/true,
|
||||
AsanDetectStackUseAfterReturnMode::Runtime,
|
||||
};
|
||||
MPM.addPass(AddressSanitizerPass(opts));
|
||||
}
|
||||
);
|
||||
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
auto CompileKernel = SanitizerOptions->SanitizeKernelAddress;
|
||||
AddressSanitizerOptions opts = AddressSanitizerOptions{
|
||||
CompileKernel,
|
||||
SanitizerOptions->SanitizeAddressRecover ||
|
||||
SanitizerOptions->SanitizeKernelAddressRecover,
|
||||
/*UseAfterScope=*/true,
|
||||
AsanDetectStackUseAfterReturnMode::Runtime,
|
||||
};
|
||||
MPM.addPass(AddressSanitizerPass(opts));
|
||||
});
|
||||
}
|
||||
if (SanitizerOptions->SanitizeHWAddress) {
|
||||
OptimizerLastEPCallbacks.push_back(
|
||||
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
HWAddressSanitizerOptions opts(
|
||||
/*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover,
|
||||
/*DisableOptimization=*/false);
|
||||
MPM.addPass(HWAddressSanitizerPass(opts));
|
||||
}
|
||||
);
|
||||
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
|
||||
HWAddressSanitizerOptions opts(
|
||||
/*CompileKernel=*/false,
|
||||
SanitizerOptions->SanitizeHWAddressRecover,
|
||||
/*DisableOptimization=*/false);
|
||||
MPM.addPass(HWAddressSanitizerPass(opts));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ModulePassManager MPM;
|
||||
bool NeedThinLTOBufferPasses = UseThinLTOBuffers;
|
||||
if (!NoPrepopulatePasses) {
|
||||
// The pre-link pipelines don't support O0 and require using buildO0DefaultPipeline() instead.
|
||||
// At the same time, the LTO pipelines do support O0 and using them is required.
|
||||
bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO || OptStage == LLVMRustOptStage::FatLTO;
|
||||
// The pre-link pipelines don't support O0 and require using
|
||||
// buildO0DefaultPipeline() instead. At the same time, the LTO pipelines do
|
||||
// support O0 and using them is required.
|
||||
bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO ||
|
||||
OptStage == LLVMRustOptStage::FatLTO;
|
||||
if (OptLevel == OptimizationLevel::O0 && !IsLTO) {
|
||||
for (const auto &C : PipelineStartEPCallbacks)
|
||||
PB.registerPipelineStartEPCallback(C);
|
||||
|
|
@ -993,7 +979,8 @@ LLVMRustOptimize(
|
|||
}
|
||||
|
||||
if (ExtraPassesLen) {
|
||||
if (auto Err = PB.parsePassPipeline(MPM, StringRef(ExtraPasses, ExtraPassesLen))) {
|
||||
if (auto Err =
|
||||
PB.parsePassPipeline(MPM, StringRef(ExtraPasses, ExtraPassesLen))) {
|
||||
std::string ErrMsg = toString(std::move(Err));
|
||||
LLVMRustSetLastError(ErrMsg.c_str());
|
||||
return LLVMRustResult::Failure;
|
||||
|
|
@ -1020,8 +1007,7 @@ LLVMRustOptimize(
|
|||
// * output buffer
|
||||
// * output buffer len
|
||||
// Returns len of demangled string, or 0 if demangle failed.
|
||||
typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t);
|
||||
|
||||
typedef size_t (*DemangleFn)(const char *, size_t, char *, size_t);
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -1064,7 +1050,7 @@ public:
|
|||
formatted_raw_ostream &OS) override {
|
||||
StringRef Demangled = CallDemangle(F->getName());
|
||||
if (Demangled.empty()) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
OS << "; " << Demangled << "\n";
|
||||
|
|
@ -1077,7 +1063,7 @@ public:
|
|||
if (const CallInst *CI = dyn_cast<CallInst>(I)) {
|
||||
Name = "call";
|
||||
Value = CI->getCalledOperand();
|
||||
} else if (const InvokeInst* II = dyn_cast<InvokeInst>(I)) {
|
||||
} else if (const InvokeInst *II = dyn_cast<InvokeInst>(I)) {
|
||||
Name = "invoke";
|
||||
Value = II->getCalledOperand();
|
||||
} else {
|
||||
|
|
@ -1101,8 +1087,8 @@ public:
|
|||
|
||||
} // namespace
|
||||
|
||||
extern "C" LLVMRustResult
|
||||
LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) {
|
||||
extern "C" LLVMRustResult LLVMRustPrintModule(LLVMModuleRef M, const char *Path,
|
||||
DemangleFn Demangle) {
|
||||
std::string ErrorInfo;
|
||||
std::error_code EC;
|
||||
auto OS = raw_fd_ostream(Path, EC, sys::fs::OF_None);
|
||||
|
|
@ -1264,11 +1250,9 @@ getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) {
|
|||
// The main entry point for creating the global ThinLTO analysis. The structure
|
||||
// here is basically the same as before threads are spawned in the `run`
|
||||
// function of `lib/LTO/ThinLTOCodeGenerator.cpp`.
|
||||
extern "C" LLVMRustThinLTOData*
|
||||
LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
||||
int num_modules,
|
||||
const char **preserved_symbols,
|
||||
int num_symbols) {
|
||||
extern "C" LLVMRustThinLTOData *
|
||||
LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, int num_modules,
|
||||
const char **preserved_symbols, int num_symbols) {
|
||||
auto Ret = std::make_unique<LLVMRustThinLTOData>();
|
||||
|
||||
// Load each module's summary and merge it into one combined index
|
||||
|
|
@ -1290,7 +1274,8 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
}
|
||||
|
||||
// Collect for each module the list of function it defines (GUID -> Summary)
|
||||
Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries);
|
||||
Ret->Index.collectDefinedGVSummariesPerModule(
|
||||
Ret->ModuleToDefinedGVSummaries);
|
||||
|
||||
// Convert the preserved symbols set from string to GUID, this is then needed
|
||||
// for internalization.
|
||||
|
|
@ -1310,7 +1295,8 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
// crate, so we need `ImportEnabled = false` to limit internalization.
|
||||
// Otherwise, we sometimes lose `static` values -- see #60184.
|
||||
computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols,
|
||||
deadIsPrevailing, /* ImportEnabled = */ false);
|
||||
deadIsPrevailing,
|
||||
/* ImportEnabled = */ false);
|
||||
// Resolve LinkOnce/Weak symbols, this has to be computed early be cause it
|
||||
// impacts the caching.
|
||||
//
|
||||
|
|
@ -1319,7 +1305,8 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
|
||||
for (auto &I : Ret->Index) {
|
||||
if (I.second.SummaryList.size() > 1)
|
||||
PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList);
|
||||
PrevailingCopy[I.first] =
|
||||
getFirstDefinitionForLinker(I.second.SummaryList);
|
||||
}
|
||||
auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
|
||||
const auto &Prevailing = PrevailingCopy.find(GUID);
|
||||
|
|
@ -1327,13 +1314,8 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
return true;
|
||||
return Prevailing->second == S;
|
||||
};
|
||||
ComputeCrossModuleImport(
|
||||
Ret->Index,
|
||||
Ret->ModuleToDefinedGVSummaries,
|
||||
isPrevailing,
|
||||
Ret->ImportLists,
|
||||
Ret->ExportLists
|
||||
);
|
||||
ComputeCrossModuleImport(Ret->Index, Ret->ModuleToDefinedGVSummaries,
|
||||
isPrevailing, Ret->ImportLists, Ret->ExportLists);
|
||||
|
||||
auto recordNewLinkage = [&](StringRef ModuleIdentifier,
|
||||
GlobalValue::GUID GUID,
|
||||
|
|
@ -1345,8 +1327,8 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
// formats. We probably could and should use ELF visibility scheme for many of
|
||||
// our targets, however.
|
||||
lto::Config conf;
|
||||
thinLTOResolvePrevailingInIndex(conf, Ret->Index, isPrevailing, recordNewLinkage,
|
||||
Ret->GUIDPreservedSymbols);
|
||||
thinLTOResolvePrevailingInIndex(conf, Ret->Index, isPrevailing,
|
||||
recordNewLinkage, Ret->GUIDPreservedSymbols);
|
||||
|
||||
// Here we calculate an `ExportedGUIDs` set for use in the `isExported`
|
||||
// callback below. This callback below will dictate the linkage for all
|
||||
|
|
@ -1355,7 +1337,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
// linkage will stay as external, and internal will stay as internal.
|
||||
std::set<GlobalValue::GUID> ExportedGUIDs;
|
||||
for (auto &List : Ret->Index) {
|
||||
for (auto &GVS: List.second.SummaryList) {
|
||||
for (auto &GVS : List.second.SummaryList) {
|
||||
if (GlobalValue::isLocalLinkage(GVS->linkage()))
|
||||
continue;
|
||||
auto GUID = GVS->getOriginalName();
|
||||
|
|
@ -1366,16 +1348,15 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
|
|||
auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) {
|
||||
const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
|
||||
return (ExportList != Ret->ExportLists.end() &&
|
||||
ExportList->second.count(VI)) ||
|
||||
ExportedGUIDs.count(VI.getGUID());
|
||||
ExportList->second.count(VI)) ||
|
||||
ExportedGUIDs.count(VI.getGUID());
|
||||
};
|
||||
thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing);
|
||||
|
||||
return Ret.release();
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) {
|
||||
extern "C" void LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) {
|
||||
delete Data;
|
||||
}
|
||||
|
||||
|
|
@ -1387,20 +1368,18 @@ LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) {
|
|||
// `ProcessThinLTOModule` function. Here they're split up into separate steps
|
||||
// so rustc can save off the intermediate bytecode between each step.
|
||||
|
||||
static bool
|
||||
clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) {
|
||||
static bool clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) {
|
||||
// When linking an ELF shared object, dso_local should be dropped. We
|
||||
// conservatively do this for -fpic.
|
||||
bool ClearDSOLocalOnDeclarations =
|
||||
TM.getTargetTriple().isOSBinFormatELF() &&
|
||||
TM.getRelocationModel() != Reloc::Static &&
|
||||
Mod.getPIELevel() == PIELevel::Default;
|
||||
bool ClearDSOLocalOnDeclarations = TM.getTargetTriple().isOSBinFormatELF() &&
|
||||
TM.getRelocationModel() != Reloc::Static &&
|
||||
Mod.getPIELevel() == PIELevel::Default;
|
||||
return ClearDSOLocalOnDeclarations;
|
||||
}
|
||||
|
||||
extern "C" bool
|
||||
LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
|
||||
LLVMTargetMachineRef TM) {
|
||||
extern "C" bool LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data,
|
||||
LLVMModuleRef M,
|
||||
LLVMTargetMachineRef TM) {
|
||||
Module &Mod = *unwrap(M);
|
||||
TargetMachine &Target = *unwrap(TM);
|
||||
|
||||
|
|
@ -1415,24 +1394,28 @@ LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
|
|||
}
|
||||
|
||||
extern "C" bool
|
||||
LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
|
||||
LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data,
|
||||
LLVMModuleRef M) {
|
||||
Module &Mod = *unwrap(M);
|
||||
const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
|
||||
const auto &DefinedGlobals =
|
||||
Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
|
||||
thinLTOFinalizeInModule(Mod, DefinedGlobals, /*PropagateAttrs=*/true);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" bool
|
||||
LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
|
||||
LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data,
|
||||
LLVMModuleRef M) {
|
||||
Module &Mod = *unwrap(M);
|
||||
const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
|
||||
const auto &DefinedGlobals =
|
||||
Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
|
||||
thinLTOInternalizeModule(Mod, DefinedGlobals);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" bool
|
||||
LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
|
||||
LLVMTargetMachineRef TM) {
|
||||
extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data,
|
||||
LLVMModuleRef M,
|
||||
LLVMTargetMachineRef TM) {
|
||||
Module &Mod = *unwrap(M);
|
||||
TargetMachine &Target = *unwrap(TM);
|
||||
|
||||
|
|
@ -1464,7 +1447,8 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
|
|||
return Ret;
|
||||
}
|
||||
|
||||
auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections");
|
||||
auto *WasmCustomSections =
|
||||
(*MOrErr)->getNamedMetadata("wasm.custom_sections");
|
||||
if (WasmCustomSections)
|
||||
WasmCustomSections->eraseFromParent();
|
||||
|
||||
|
|
@ -1498,7 +1482,7 @@ struct LLVMRustThinLTOBuffer {
|
|||
std::string thin_link_data;
|
||||
};
|
||||
|
||||
extern "C" LLVMRustThinLTOBuffer*
|
||||
extern "C" LLVMRustThinLTOBuffer *
|
||||
LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool emit_summary) {
|
||||
auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
|
||||
{
|
||||
|
|
@ -1520,7 +1504,8 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool emit_summary) {
|
|||
// We only pass ThinLinkOS to be filled in if we want the summary,
|
||||
// because otherwise LLVM does extra work and may double-emit some
|
||||
// errors or warnings.
|
||||
MPM.addPass(ThinLTOBitcodeWriterPass(OS, emit_summary ? &ThinLinkOS : nullptr));
|
||||
MPM.addPass(
|
||||
ThinLTOBitcodeWriterPass(OS, emit_summary ? &ThinLinkOS : nullptr));
|
||||
MPM.run(*unwrap(M), MAM);
|
||||
} else {
|
||||
WriteBitcodeToFile(*unwrap(M), OS);
|
||||
|
|
@ -1530,12 +1515,11 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool emit_summary) {
|
|||
return Ret.release();
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) {
|
||||
extern "C" void LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) {
|
||||
delete Buffer;
|
||||
}
|
||||
|
||||
extern "C" const void*
|
||||
extern "C" const void *
|
||||
LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) {
|
||||
return Buffer->data.data();
|
||||
}
|
||||
|
|
@ -1545,7 +1529,7 @@ LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) {
|
|||
return Buffer->data.length();
|
||||
}
|
||||
|
||||
extern "C" const void*
|
||||
extern "C" const void *
|
||||
LLVMRustThinLTOBufferThinLinkDataPtr(const LLVMRustThinLTOBuffer *Buffer) {
|
||||
return Buffer->thin_link_data.data();
|
||||
}
|
||||
|
|
@ -1558,11 +1542,10 @@ LLVMRustThinLTOBufferThinLinkDataLen(const LLVMRustThinLTOBuffer *Buffer) {
|
|||
// This is what we used to parse upstream bitcode for actual ThinLTO
|
||||
// processing. We'll call this once per module optimized through ThinLTO, and
|
||||
// it'll be called concurrently on many threads.
|
||||
extern "C" LLVMModuleRef
|
||||
LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
|
||||
const char *data,
|
||||
size_t len,
|
||||
const char *identifier) {
|
||||
extern "C" LLVMModuleRef LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
|
||||
const char *data,
|
||||
size_t len,
|
||||
const char *identifier) {
|
||||
auto Data = StringRef(data, len);
|
||||
auto Buffer = MemoryBufferRef(Data, identifier);
|
||||
unwrap(Context)->enableDebugTypeODRUniquing();
|
||||
|
|
@ -1614,8 +1597,9 @@ extern "C" const char *LLVMRustGetSliceFromObjectDataByName(const char *data,
|
|||
// of access globals, etc).
|
||||
// The precise details are determined by LLVM in `computeLTOCacheKey`, which is
|
||||
// used during the normal linker-plugin incremental thin-LTO process.
|
||||
extern "C" void
|
||||
LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, const char *ModId, LLVMRustThinLTOData *Data) {
|
||||
extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut,
|
||||
const char *ModId,
|
||||
LLVMRustThinLTOData *Data) {
|
||||
SmallString<40> Key;
|
||||
llvm::lto::Config conf;
|
||||
const auto &ImportList = Data->ImportLists.lookup(ModId);
|
||||
|
|
@ -1633,9 +1617,9 @@ LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, const char *ModId, LLVMRustThin
|
|||
CfiFunctionDecls.insert(
|
||||
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
|
||||
|
||||
llvm::computeLTOCacheKey(Key, conf, Data->Index, ModId,
|
||||
ImportList, ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls
|
||||
);
|
||||
llvm::computeLTOCacheKey(Key, conf, Data->Index, ModId, ImportList,
|
||||
ExportList, ResolvedODR, DefinedGlobals,
|
||||
CfiFunctionDefs, CfiFunctionDecls);
|
||||
|
||||
LLVMRustStringWriteImpl(KeyOut, Key.c_str(), Key.size());
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,17 @@
|
|||
#ifndef _rustc_llvm_SuppressLLVMWarnings_h
|
||||
#define _rustc_llvm_SuppressLLVMWarnings_h
|
||||
|
||||
// LLVM currently generates many warnings when compiled using MSVC. These warnings make it difficult
|
||||
// to diagnose real problems when working on C++ code, so we suppress them.
|
||||
// LLVM currently generates many warnings when compiled using MSVC. These
|
||||
// warnings make it difficult to diagnose real problems when working on C++
|
||||
// code, so we suppress them.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4530) // C++ exception handler used, but unwind semantics are not enabled.
|
||||
#pragma warning(disable:4624) // 'xxx': destructor was implicitly defined as deleted
|
||||
#pragma warning(disable:4244) // conversion from 'xxx' to 'yyy', possible loss of data
|
||||
#pragma warning(disable : 4530) // C++ exception handler used, but unwind
|
||||
// semantics are not enabled.
|
||||
#pragma warning( \
|
||||
disable : 4624) // 'xxx': destructor was implicitly defined as deleted
|
||||
#pragma warning( \
|
||||
disable : 4244) // conversion from 'xxx' to 'yyy', possible loss of data
|
||||
#endif
|
||||
|
||||
#endif // _rustc_llvm_SuppressLLVMWarnings_h
|
||||
|
|
|
|||
|
|
@ -34,14 +34,15 @@ static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
|
|||
typedef void *(*LLVMRustGetSymbolsCallback)(void *, const char *);
|
||||
typedef void *(*LLVMRustGetSymbolsErrorCallback)(const char *);
|
||||
|
||||
// Note: This is implemented in C++ instead of using the C api from Rust as IRObjectFile doesn't
|
||||
// implement getSymbolName, only printSymbolName, which is inaccessible from the C api.
|
||||
extern "C" void *LLVMRustGetSymbols(
|
||||
char *BufPtr, size_t BufLen, void *State, LLVMRustGetSymbolsCallback Callback,
|
||||
LLVMRustGetSymbolsErrorCallback ErrorCallback) {
|
||||
std::unique_ptr<MemoryBuffer> Buf =
|
||||
MemoryBuffer::getMemBuffer(StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"),
|
||||
false);
|
||||
// Note: This is implemented in C++ instead of using the C api from Rust as
|
||||
// IRObjectFile doesn't implement getSymbolName, only printSymbolName, which is
|
||||
// inaccessible from the C api.
|
||||
extern "C" void *
|
||||
LLVMRustGetSymbols(char *BufPtr, size_t BufLen, void *State,
|
||||
LLVMRustGetSymbolsCallback Callback,
|
||||
LLVMRustGetSymbolsErrorCallback ErrorCallback) {
|
||||
std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(
|
||||
StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false);
|
||||
SmallString<0> SymNameBuf;
|
||||
auto SymName = raw_svector_ostream(SymNameBuf);
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ extern "C" void *LLVMRustGetSymbols(
|
|||
|
||||
if (Type == file_magic::bitcode) {
|
||||
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
|
||||
Buf->getMemBufferRef(), file_magic::bitcode, &Context);
|
||||
Buf->getMemBufferRef(), file_magic::bitcode, &Context);
|
||||
if (!ObjOrErr) {
|
||||
Error E = ObjOrErr.takeError();
|
||||
SmallString<0> ErrorBuf;
|
||||
|
|
@ -67,7 +68,8 @@ extern "C" void *LLVMRustGetSymbols(
|
|||
}
|
||||
Obj = std::move(*ObjOrErr);
|
||||
} else {
|
||||
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf->getMemBufferRef());
|
||||
auto ObjOrErr =
|
||||
object::SymbolicFile::createSymbolicFile(Buf->getMemBufferRef());
|
||||
if (!ObjOrErr) {
|
||||
Error E = ObjOrErr.takeError();
|
||||
SmallString<0> ErrorBuf;
|
||||
|
|
@ -78,7 +80,6 @@ extern "C" void *LLVMRustGetSymbols(
|
|||
Obj = std::move(*ObjOrErr);
|
||||
}
|
||||
|
||||
|
||||
for (const object::BasicSymbolRef &S : Obj->symbols()) {
|
||||
if (!isArchiveSymbol(S))
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -358,8 +358,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
where
|
||||
'a: 'pat,
|
||||
{
|
||||
// Assemble a list of candidates: there is one candidate per pattern,
|
||||
// which means there may be more than one candidate *per arm*.
|
||||
// Assemble the initial list of candidates. These top-level candidates
|
||||
// are 1:1 with the original match arms, but other parts of match
|
||||
// lowering also introduce subcandidates (for subpatterns), and will
|
||||
// also flatten candidates in some cases. So in general a list of
|
||||
// candidates does _not_ necessarily correspond to a list of arms.
|
||||
arms.iter()
|
||||
.copied()
|
||||
.map(|arm| {
|
||||
|
|
@ -1031,6 +1034,12 @@ impl<'tcx> PatternExtraData<'tcx> {
|
|||
}
|
||||
|
||||
/// A pattern in a form suitable for generating code.
|
||||
///
|
||||
/// Here, "flat" indicates that the pattern's match pairs have been recursively
|
||||
/// simplified by [`Builder::simplify_match_pairs`]. They are not necessarily
|
||||
/// flat in an absolute sense.
|
||||
///
|
||||
/// Will typically be incorporated into a [`Candidate`].
|
||||
#[derive(Debug, Clone)]
|
||||
struct FlatPat<'pat, 'tcx> {
|
||||
/// To match the pattern, all of these must be satisfied...
|
||||
|
|
@ -1042,23 +1051,25 @@ struct FlatPat<'pat, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx, 'pat> FlatPat<'pat, 'tcx> {
|
||||
/// Creates a `FlatPat` containing a simplified [`MatchPair`] list/forest
|
||||
/// for the given pattern.
|
||||
fn new(
|
||||
place: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
) -> Self {
|
||||
let is_never = pattern.is_never_pattern();
|
||||
let mut flat_pat = FlatPat {
|
||||
match_pairs: vec![MatchPair::new(place, pattern, cx)],
|
||||
extra_data: PatternExtraData {
|
||||
span: pattern.span,
|
||||
bindings: Vec::new(),
|
||||
ascriptions: Vec::new(),
|
||||
is_never,
|
||||
},
|
||||
// First, recursively build a tree of match pairs for the given pattern.
|
||||
let mut match_pairs = vec![MatchPair::new(place, pattern, cx)];
|
||||
let mut extra_data = PatternExtraData {
|
||||
span: pattern.span,
|
||||
bindings: Vec::new(),
|
||||
ascriptions: Vec::new(),
|
||||
is_never: pattern.is_never_pattern(),
|
||||
};
|
||||
cx.simplify_match_pairs(&mut flat_pat.match_pairs, &mut flat_pat.extra_data);
|
||||
flat_pat
|
||||
// Partly-flatten and sort the match pairs, while recording extra data.
|
||||
cx.simplify_match_pairs(&mut match_pairs, &mut extra_data);
|
||||
|
||||
Self { match_pairs, extra_data }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1104,9 +1115,12 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
|||
has_guard: bool,
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
) -> Self {
|
||||
// Use `FlatPat` to build simplified match pairs, then immediately
|
||||
// incorporate them into a new candidate.
|
||||
Self::from_flat_pat(FlatPat::new(place, pattern, cx), has_guard)
|
||||
}
|
||||
|
||||
/// Incorporates an already-simplified [`FlatPat`] into a new candidate.
|
||||
fn from_flat_pat(flat_pat: FlatPat<'pat, 'tcx>, has_guard: bool) -> Self {
|
||||
Candidate {
|
||||
match_pairs: flat_pat.match_pairs,
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
/// Recursively builds a `MatchPair` tree for the given pattern and its
|
||||
/// subpatterns.
|
||||
pub(in crate::build) fn new(
|
||||
mut place_builder: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, (I::Term, I::Term, ty::AliasRelationDirection)>,
|
||||
) -> QueryResult<I> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
||||
debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
|
||||
|
||||
// Structurally normalize the lhs.
|
||||
let lhs = if let Some(alias) = lhs.to_alias_term() {
|
||||
let term = self.next_term_infer_of_kind(lhs);
|
||||
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||
self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
|
||||
term
|
||||
} else {
|
||||
lhs
|
||||
|
|
@ -48,7 +48,7 @@ where
|
|||
// Structurally normalize the rhs.
|
||||
let rhs = if let Some(alias) = rhs.to_alias_term() {
|
||||
let term = self.next_term_infer_of_kind(rhs);
|
||||
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||
self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
|
||||
term
|
||||
} else {
|
||||
rhs
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ where
|
|||
{
|
||||
fn self_ty(self) -> I::Ty;
|
||||
|
||||
fn trait_ref(self, tcx: I) -> ty::TraitRef<I>;
|
||||
fn trait_ref(self, cx: I) -> ty::TraitRef<I>;
|
||||
|
||||
fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self;
|
||||
fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self;
|
||||
|
||||
fn trait_def_id(self, tcx: I) -> I::DefId;
|
||||
fn trait_def_id(self, cx: I) -> I::DefId;
|
||||
|
||||
/// Try equating an assumption predicate against a goal's predicate. If it
|
||||
/// holds, then execute the `then` callback, which should do any additional
|
||||
|
|
@ -82,7 +82,7 @@ where
|
|||
assumption: I::Clause,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {
|
||||
panic!("expected object type in `probe_and_consider_object_bound_candidate`");
|
||||
};
|
||||
|
|
@ -91,7 +91,7 @@ where
|
|||
structural_traits::predicates_for_object_candidate(
|
||||
ecx,
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref(tcx),
|
||||
goal.predicate.trait_ref(cx),
|
||||
bounds,
|
||||
),
|
||||
);
|
||||
|
|
@ -340,15 +340,15 @@ where
|
|||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let tcx = self.cx();
|
||||
tcx.for_each_relevant_impl(
|
||||
goal.predicate.trait_def_id(tcx),
|
||||
let cx = self.cx();
|
||||
cx.for_each_relevant_impl(
|
||||
goal.predicate.trait_def_id(cx),
|
||||
goal.predicate.self_ty(),
|
||||
|impl_def_id| {
|
||||
// For every `default impl`, there's always a non-default `impl`
|
||||
// that will *also* apply. There's no reason to register a candidate
|
||||
// for this impl, since it is *not* proof that the trait goal holds.
|
||||
if tcx.impl_is_default(impl_def_id) {
|
||||
if cx.impl_is_default(impl_def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -366,8 +366,8 @@ where
|
|||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let tcx = self.cx();
|
||||
let trait_def_id = goal.predicate.trait_def_id(tcx);
|
||||
let cx = self.cx();
|
||||
let trait_def_id = goal.predicate.trait_def_id(cx);
|
||||
|
||||
// N.B. When assembling built-in candidates for lang items that are also
|
||||
// `auto` traits, then the auto trait candidate that is assembled in
|
||||
|
|
@ -378,47 +378,47 @@ where
|
|||
// `solve::trait_goals` instead.
|
||||
let result = if let Err(guar) = goal.predicate.error_reported() {
|
||||
G::consider_error_guaranteed_candidate(self, guar)
|
||||
} else if tcx.trait_is_auto(trait_def_id) {
|
||||
} else if cx.trait_is_auto(trait_def_id) {
|
||||
G::consider_auto_trait_candidate(self, goal)
|
||||
} else if tcx.trait_is_alias(trait_def_id) {
|
||||
} else if cx.trait_is_alias(trait_def_id) {
|
||||
G::consider_trait_alias_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
|
||||
G::consider_builtin_sized_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
|
||||
|| tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
|
||||
|| cx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
|
||||
{
|
||||
G::consider_builtin_copy_clone_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
|
||||
G::consider_builtin_pointer_like_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
|
||||
G::consider_builtin_fn_ptr_trait_candidate(self, goal)
|
||||
} else if let Some(kind) = self.cx().fn_trait_kind_from_def_id(trait_def_id) {
|
||||
G::consider_builtin_fn_trait_candidates(self, goal, kind)
|
||||
} else if let Some(kind) = self.cx().async_fn_trait_kind_from_def_id(trait_def_id) {
|
||||
G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
|
||||
G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
|
||||
G::consider_builtin_tuple_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
|
||||
G::consider_builtin_pointee_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
|
||||
G::consider_builtin_future_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
|
||||
G::consider_builtin_iterator_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
|
||||
G::consider_builtin_fused_iterator_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
|
||||
G::consider_builtin_async_iterator_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
|
||||
G::consider_builtin_coroutine_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
|
||||
G::consider_builtin_discriminant_kind_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
|
||||
G::consider_builtin_async_destruct_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
|
||||
G::consider_builtin_destruct_candidate(self, goal)
|
||||
} else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
|
||||
} else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
|
||||
G::consider_builtin_transmute_candidate(self, goal)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
|
|
@ -428,7 +428,7 @@ where
|
|||
|
||||
// There may be multiple unsize candidates for a trait with several supertraits:
|
||||
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
|
||||
if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) {
|
||||
if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) {
|
||||
candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal));
|
||||
}
|
||||
}
|
||||
|
|
@ -557,8 +557,8 @@ where
|
|||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let tcx = self.cx();
|
||||
if !tcx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(tcx)) {
|
||||
let cx = self.cx();
|
||||
if !cx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(cx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -596,7 +596,7 @@ where
|
|||
};
|
||||
|
||||
// Do not consider built-in object impls for non-object-safe types.
|
||||
if bounds.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
|
||||
if bounds.principal_def_id().is_some_and(|def_id| !cx.trait_is_object_safe(def_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +614,7 @@ where
|
|||
self,
|
||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
goal,
|
||||
bound.with_self_ty(tcx, self_ty),
|
||||
bound.with_self_ty(cx, self_ty),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -624,14 +624,13 @@ where
|
|||
// since we don't need to look at any supertrait or anything if we are doing
|
||||
// a projection goal.
|
||||
if let Some(principal) = bounds.principal() {
|
||||
let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
|
||||
for (idx, assumption) in D::elaborate_supertraits(tcx, principal_trait_ref).enumerate()
|
||||
{
|
||||
let principal_trait_ref = principal.with_self_ty(cx, self_ty);
|
||||
for (idx, assumption) in D::elaborate_supertraits(cx, principal_trait_ref).enumerate() {
|
||||
candidates.extend(G::probe_and_consider_object_bound_candidate(
|
||||
self,
|
||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)),
|
||||
goal,
|
||||
assumption.upcast(tcx),
|
||||
assumption.upcast(cx),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -649,11 +648,11 @@ where
|
|||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
|
||||
candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(
|
||||
|ecx| {
|
||||
let trait_ref = goal.predicate.trait_ref(tcx);
|
||||
let trait_ref = goal.predicate.trait_ref(cx);
|
||||
if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? {
|
||||
Err(NoSolution)
|
||||
} else {
|
||||
|
|
@ -678,9 +677,9 @@ where
|
|||
goal: Goal<I, G>,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let trait_goal: Goal<I, ty::TraitPredicate<I>> =
|
||||
goal.with(tcx, goal.predicate.trait_ref(tcx));
|
||||
goal.with(cx, goal.predicate.trait_ref(cx));
|
||||
|
||||
let mut trait_candidates_from_env = vec![];
|
||||
self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ where
|
|||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
match ty.kind() {
|
||||
ty::Uint(_)
|
||||
| ty::Int(_)
|
||||
|
|
@ -36,7 +36,7 @@ where
|
|||
| ty::Char => Ok(vec![]),
|
||||
|
||||
// Treat `str` like it's defined as `struct str([u8]);`
|
||||
ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, Ty::new_u8(tcx)))]),
|
||||
ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(cx, Ty::new_u8(cx)))]),
|
||||
|
||||
ty::Dynamic(..)
|
||||
| ty::Param(..)
|
||||
|
|
@ -79,21 +79,21 @@ where
|
|||
.cx()
|
||||
.bound_coroutine_hidden_types(def_id)
|
||||
.into_iter()
|
||||
.map(|bty| bty.instantiate(tcx, args))
|
||||
.map(|bty| bty.instantiate(cx, args))
|
||||
.collect()),
|
||||
|
||||
// For `PhantomData<T>`, we pass `T`.
|
||||
ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
|
||||
|
||||
ty::Adt(def, args) => {
|
||||
Ok(def.all_field_tys(tcx).iter_instantiated(tcx, args).map(ty::Binder::dummy).collect())
|
||||
Ok(def.all_field_tys(cx).iter_instantiated(cx, args).map(ty::Binder::dummy).collect())
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// We can resolve the `impl Trait` to its concrete type,
|
||||
// which enforces a DAG between the functions requiring
|
||||
// the auto trait bounds in question.
|
||||
Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))])
|
||||
Ok(vec![ty::Binder::dummy(cx.type_of(def_id).instantiate(cx, args))])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -247,18 +247,18 @@ where
|
|||
|
||||
// Returns a binder of the tupled inputs types and output type from a builtin callable type.
|
||||
pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Interner>(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
self_ty: I::Ty,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> Result<Option<ty::Binder<I, (I::Ty, I::Ty)>>, NoSolution> {
|
||||
match self_ty.kind() {
|
||||
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
|
||||
ty::FnDef(def_id, args) => {
|
||||
let sig = tcx.fn_sig(def_id);
|
||||
if sig.skip_binder().is_fn_trait_compatible() && !tcx.has_target_features(def_id) {
|
||||
let sig = cx.fn_sig(def_id);
|
||||
if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) {
|
||||
Ok(Some(
|
||||
sig.instantiate(tcx, args)
|
||||
.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs().as_slice()), sig.output())),
|
||||
sig.instantiate(cx, args)
|
||||
.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
|
||||
))
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
|
|
@ -268,7 +268,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
|
|||
ty::FnPtr(sig) => {
|
||||
if sig.is_fn_trait_compatible() {
|
||||
Ok(Some(
|
||||
sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs().as_slice()), sig.output())),
|
||||
sig.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
|
||||
))
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
|
|
@ -323,10 +323,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
|
|||
}
|
||||
|
||||
coroutine_closure_to_certain_coroutine(
|
||||
tcx,
|
||||
cx,
|
||||
goal_kind,
|
||||
// No captures by ref, so this doesn't matter.
|
||||
Region::new_static(tcx),
|
||||
Region::new_static(cx),
|
||||
def_id,
|
||||
args,
|
||||
sig,
|
||||
|
|
@ -339,9 +339,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern
|
|||
}
|
||||
|
||||
coroutine_closure_to_ambiguous_coroutine(
|
||||
tcx,
|
||||
cx,
|
||||
goal_kind, // No captures by ref, so this doesn't matter.
|
||||
Region::new_static(tcx),
|
||||
Region::new_static(cx),
|
||||
def_id,
|
||||
args,
|
||||
sig,
|
||||
|
|
@ -403,7 +403,7 @@ pub(in crate::solve) struct AsyncCallableRelevantTypes<I: Interner> {
|
|||
// which enforces the closure is actually callable with the given trait. When we
|
||||
// know the kind already, we can short-circuit this check.
|
||||
pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: Interner>(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
self_ty: I::Ty,
|
||||
goal_kind: ty::ClosureKind,
|
||||
env_region: I::Region,
|
||||
|
|
@ -422,9 +422,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
coroutine_closure_to_certain_coroutine(
|
||||
tcx, goal_kind, env_region, def_id, args, sig,
|
||||
)
|
||||
coroutine_closure_to_certain_coroutine(cx, goal_kind, env_region, def_id, args, sig)
|
||||
} else {
|
||||
// When we don't know the closure kind (and therefore also the closure's upvars,
|
||||
// which are computed at the same time), we must delay the computation of the
|
||||
|
|
@ -435,15 +433,15 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
// coroutine upvars respecting the closure kind.
|
||||
nested.push(
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper),
|
||||
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
|
||||
cx,
|
||||
cx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper),
|
||||
[kind_ty, Ty::from_closure_kind(cx, goal_kind)],
|
||||
)
|
||||
.upcast(tcx),
|
||||
.upcast(cx),
|
||||
);
|
||||
|
||||
coroutine_closure_to_ambiguous_coroutine(
|
||||
tcx, goal_kind, env_region, def_id, args, sig,
|
||||
cx, goal_kind, env_region, def_id, args, sig,
|
||||
)
|
||||
};
|
||||
|
||||
|
|
@ -458,21 +456,21 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
}
|
||||
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
let bound_sig = self_ty.fn_sig(tcx);
|
||||
let bound_sig = self_ty.fn_sig(cx);
|
||||
let sig = bound_sig.skip_binder();
|
||||
let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future);
|
||||
let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
|
||||
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
|
||||
// return type implements `Future`.
|
||||
let nested = vec![
|
||||
bound_sig
|
||||
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
|
||||
.upcast(tcx),
|
||||
.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()]))
|
||||
.upcast(cx),
|
||||
];
|
||||
let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput);
|
||||
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
|
||||
let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
|
||||
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
|
||||
Ok((
|
||||
bound_sig.rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs().as_slice()),
|
||||
tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()),
|
||||
output_coroutine_ty: sig.output(),
|
||||
coroutine_return_ty: future_output_ty,
|
||||
}),
|
||||
|
|
@ -483,13 +481,13 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
let args = args.as_closure();
|
||||
let bound_sig = args.sig();
|
||||
let sig = bound_sig.skip_binder();
|
||||
let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future);
|
||||
let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future);
|
||||
// `Closure`s only implement `AsyncFn*` when their return type
|
||||
// implements `Future`.
|
||||
let mut nested = vec![
|
||||
bound_sig
|
||||
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
|
||||
.upcast(tcx),
|
||||
.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()]))
|
||||
.upcast(cx),
|
||||
];
|
||||
|
||||
// Additionally, we need to check that the closure kind
|
||||
|
|
@ -501,7 +499,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
}
|
||||
} else {
|
||||
let async_fn_kind_trait_def_id =
|
||||
tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper);
|
||||
cx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper);
|
||||
// When we don't know the closure kind (and therefore also the closure's upvars,
|
||||
// which are computed at the same time), we must delay the computation of the
|
||||
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
|
||||
|
|
@ -511,16 +509,16 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
// coroutine upvars respecting the closure kind.
|
||||
nested.push(
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
cx,
|
||||
async_fn_kind_trait_def_id,
|
||||
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
|
||||
[kind_ty, Ty::from_closure_kind(cx, goal_kind)],
|
||||
)
|
||||
.upcast(tcx),
|
||||
.upcast(cx),
|
||||
);
|
||||
}
|
||||
|
||||
let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput);
|
||||
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
|
||||
let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput);
|
||||
let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]);
|
||||
Ok((
|
||||
bound_sig.rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: sig.inputs().get(0).unwrap(),
|
||||
|
|
@ -565,7 +563,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I:
|
|||
/// Given a coroutine-closure, project to its returned coroutine when we are *certain*
|
||||
/// that the closure's kind is compatible with the goal.
|
||||
fn coroutine_closure_to_certain_coroutine<I: Interner>(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
goal_kind: ty::ClosureKind,
|
||||
goal_region: I::Region,
|
||||
def_id: I::DefId,
|
||||
|
|
@ -573,9 +571,9 @@ fn coroutine_closure_to_certain_coroutine<I: Interner>(
|
|||
sig: ty::CoroutineClosureSignature<I>,
|
||||
) -> I::Ty {
|
||||
sig.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
cx,
|
||||
args.parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
cx.coroutine_for_closure(def_id),
|
||||
goal_kind,
|
||||
goal_region,
|
||||
args.tupled_upvars_ty(),
|
||||
|
|
@ -589,20 +587,20 @@ fn coroutine_closure_to_certain_coroutine<I: Interner>(
|
|||
///
|
||||
/// Note that we do not also push a `AsyncFnKindHelper` goal here.
|
||||
fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
goal_kind: ty::ClosureKind,
|
||||
goal_region: I::Region,
|
||||
def_id: I::DefId,
|
||||
args: ty::CoroutineClosureArgs<I>,
|
||||
sig: ty::CoroutineClosureSignature<I>,
|
||||
) -> I::Ty {
|
||||
let upvars_projection_def_id = tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars);
|
||||
let upvars_projection_def_id = cx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars);
|
||||
let tupled_upvars_ty = Ty::new_projection(
|
||||
tcx,
|
||||
cx,
|
||||
upvars_projection_def_id,
|
||||
[
|
||||
I::GenericArg::from(args.kind_ty()),
|
||||
Ty::from_closure_kind(tcx, goal_kind).into(),
|
||||
Ty::from_closure_kind(cx, goal_kind).into(),
|
||||
goal_region.into(),
|
||||
sig.tupled_inputs_ty.into(),
|
||||
args.tupled_upvars_ty().into(),
|
||||
|
|
@ -610,10 +608,10 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
|
|||
],
|
||||
);
|
||||
sig.to_coroutine(
|
||||
tcx,
|
||||
cx,
|
||||
args.parent_args(),
|
||||
Ty::from_closure_kind(tcx, goal_kind),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
Ty::from_closure_kind(cx, goal_kind),
|
||||
cx.coroutine_for_closure(def_id),
|
||||
tupled_upvars_ty,
|
||||
)
|
||||
}
|
||||
|
|
@ -668,28 +666,28 @@ where
|
|||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
let mut requirements = vec![];
|
||||
requirements
|
||||
.extend(tcx.super_predicates_of(trait_ref.def_id).iter_instantiated(tcx, trait_ref.args));
|
||||
.extend(cx.super_predicates_of(trait_ref.def_id).iter_instantiated(cx, trait_ref.args));
|
||||
|
||||
// FIXME(associated_const_equality): Also add associated consts to
|
||||
// the requirements here.
|
||||
for associated_type_def_id in tcx.associated_type_def_ids(trait_ref.def_id) {
|
||||
for associated_type_def_id in cx.associated_type_def_ids(trait_ref.def_id) {
|
||||
// associated types that require `Self: Sized` do not show up in the built-in
|
||||
// implementation of `Trait for dyn Trait`, and can be dropped here.
|
||||
if tcx.generics_require_sized_self(associated_type_def_id) {
|
||||
if cx.generics_require_sized_self(associated_type_def_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
requirements
|
||||
.extend(tcx.item_bounds(associated_type_def_id).iter_instantiated(tcx, trait_ref.args));
|
||||
.extend(cx.item_bounds(associated_type_def_id).iter_instantiated(cx, trait_ref.args));
|
||||
}
|
||||
|
||||
let mut replace_projection_with = HashMap::default();
|
||||
for bound in object_bounds.iter() {
|
||||
if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
|
||||
let proj = proj.with_self_ty(tcx, trait_ref.self_ty());
|
||||
let proj = proj.with_self_ty(cx, trait_ref.self_ty());
|
||||
let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
|
||||
assert_eq!(
|
||||
old_ty,
|
||||
|
|
@ -709,7 +707,7 @@ where
|
|||
folder
|
||||
.nested
|
||||
.into_iter()
|
||||
.chain(folded_requirements.into_iter().map(|clause| Goal::new(tcx, param_env, clause)))
|
||||
.chain(folded_requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -239,14 +239,14 @@ where
|
|||
/// This function takes care of setting up the inference context, setting the anchor,
|
||||
/// and registering opaques from the canonicalized input.
|
||||
fn enter_canonical<R>(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
search_graph: &'a mut search_graph::SearchGraph<I>,
|
||||
canonical_input: CanonicalInput<I>,
|
||||
canonical_goal_evaluation: &mut ProofTreeBuilder<D>,
|
||||
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
|
||||
) -> R {
|
||||
let (ref delegate, input, var_values) =
|
||||
SolverDelegate::build_with_canonical(tcx, search_graph.solver_mode(), &canonical_input);
|
||||
SolverDelegate::build_with_canonical(cx, search_graph.solver_mode(), &canonical_input);
|
||||
|
||||
let mut ecx = EvalCtxt {
|
||||
delegate,
|
||||
|
|
@ -292,9 +292,9 @@ where
|
|||
/// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
|
||||
/// if you're inside of the solver or [SolverDelegateEvalExt::evaluate_root_goal] if you're
|
||||
/// outside of it.
|
||||
#[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
|
||||
#[instrument(level = "debug", skip(cx, search_graph, goal_evaluation), ret)]
|
||||
fn evaluate_canonical_goal(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
search_graph: &'a mut search_graph::SearchGraph<I>,
|
||||
canonical_input: CanonicalInput<I>,
|
||||
goal_evaluation: &mut ProofTreeBuilder<D>,
|
||||
|
|
@ -307,12 +307,12 @@ where
|
|||
// The actual solver logic happens in `ecx.compute_goal`.
|
||||
let result = ensure_sufficient_stack(|| {
|
||||
search_graph.with_new_goal(
|
||||
tcx,
|
||||
cx,
|
||||
canonical_input,
|
||||
&mut canonical_goal_evaluation,
|
||||
|search_graph, canonical_goal_evaluation| {
|
||||
EvalCtxt::enter_canonical(
|
||||
tcx,
|
||||
cx,
|
||||
search_graph,
|
||||
canonical_input,
|
||||
canonical_goal_evaluation,
|
||||
|
|
@ -506,7 +506,7 @@ where
|
|||
///
|
||||
/// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
|
||||
fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let mut goals = core::mem::take(&mut self.nested_goals);
|
||||
|
||||
// If this loop did not result in any progress, what's our final certainty.
|
||||
|
|
@ -516,7 +516,7 @@ where
|
|||
// RHS does not affect projection candidate assembly.
|
||||
let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
|
||||
let unconstrained_goal = goal.with(
|
||||
tcx,
|
||||
cx,
|
||||
ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
|
||||
);
|
||||
|
||||
|
|
@ -777,7 +777,7 @@ where
|
|||
// NOTE: this check is purely an optimization, the structural eq would
|
||||
// always fail if the term is not an inference variable.
|
||||
if term.is_infer() {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
// We need to relate `alias` to `term` treating only the outermost
|
||||
// constructor as rigid, relating any contained generic arguments as
|
||||
// normal. We do this by first structurally equating the `term`
|
||||
|
|
@ -787,8 +787,8 @@ where
|
|||
// Alternatively we could modify `Equate` for this case by adding another
|
||||
// variant to `StructurallyRelateAliases`.
|
||||
let identity_args = self.fresh_args_for_item(alias.def_id);
|
||||
let rigid_ctor = ty::AliasTerm::new_from_args(tcx, alias.def_id, identity_args);
|
||||
let ctor_term = rigid_ctor.to_term(tcx);
|
||||
let rigid_ctor = ty::AliasTerm::new_from_args(cx, alias.def_id, identity_args);
|
||||
let ctor_term = rigid_ctor.to_term(cx);
|
||||
let obligations =
|
||||
self.delegate.eq_structurally_relating_aliases(param_env, term, ctor_term)?;
|
||||
debug_assert!(obligations.is_empty());
|
||||
|
|
|
|||
|
|
@ -323,13 +323,13 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
|
|||
|
||||
pub fn finalize_canonical_goal_evaluation(
|
||||
&mut self,
|
||||
tcx: I,
|
||||
cx: I,
|
||||
) -> Option<I::CanonicalGoalEvaluationStepRef> {
|
||||
self.as_mut().map(|this| match this {
|
||||
DebugSolver::CanonicalGoalEvaluation(evaluation) => {
|
||||
let final_revision = mem::take(&mut evaluation.final_revision).unwrap();
|
||||
let final_revision =
|
||||
tcx.intern_canonical_goal_evaluation_step(final_revision.finalize());
|
||||
cx.intern_canonical_goal_evaluation_step(final_revision.finalize());
|
||||
let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
|
||||
assert_eq!(evaluation.kind.replace(kind), None);
|
||||
final_revision
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use crate::delegate::SolverDelegate;
|
|||
/// How many fixpoint iterations we should attempt inside of the solver before bailing
|
||||
/// with overflow.
|
||||
///
|
||||
/// We previously used `tcx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
|
||||
/// We previously used `cx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
|
||||
/// However, it feels unlikely that uncreasing the recursion limit by a power of two
|
||||
/// to get one more itereation is every useful or desirable. We now instead used a constant
|
||||
/// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations
|
||||
|
|
@ -285,7 +285,7 @@ where
|
|||
}
|
||||
|
||||
fn response_no_constraints_raw<I: Interner>(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
max_universe: ty::UniverseIndex,
|
||||
variables: I::CanonicalVars,
|
||||
certainty: Certainty,
|
||||
|
|
@ -294,10 +294,10 @@ fn response_no_constraints_raw<I: Interner>(
|
|||
max_universe,
|
||||
variables,
|
||||
value: Response {
|
||||
var_values: ty::CanonicalVarValues::make_identity(tcx, variables),
|
||||
// FIXME: maybe we should store the "no response" version in tcx, like
|
||||
// we do for tcx.types and stuff.
|
||||
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
|
||||
var_values: ty::CanonicalVarValues::make_identity(cx, variables),
|
||||
// FIXME: maybe we should store the "no response" version in cx, like
|
||||
// we do for cx.types and stuff.
|
||||
external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()),
|
||||
certainty,
|
||||
},
|
||||
defining_opaque_types: Default::default(),
|
||||
|
|
|
|||
|
|
@ -19,21 +19,21 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, ty::NormalizesTo<I>>,
|
||||
) -> QueryResult<I> {
|
||||
let tcx = self.cx();
|
||||
let inherent = goal.predicate.alias.expect_ty(tcx);
|
||||
let cx = self.cx();
|
||||
let inherent = goal.predicate.alias.expect_ty(cx);
|
||||
|
||||
let impl_def_id = tcx.parent(inherent.def_id);
|
||||
let impl_def_id = cx.parent(inherent.def_id);
|
||||
let impl_args = self.fresh_args_for_item(impl_def_id);
|
||||
|
||||
// Equate impl header and add impl where clauses
|
||||
self.eq(
|
||||
goal.param_env,
|
||||
inherent.self_ty(),
|
||||
tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
|
||||
cx.type_of(impl_def_id).instantiate(cx, impl_args),
|
||||
)?;
|
||||
|
||||
// Equate IAT with the RHS of the project goal
|
||||
let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx);
|
||||
let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, cx);
|
||||
|
||||
// Check both where clauses on the impl and IAT
|
||||
//
|
||||
|
|
@ -43,12 +43,12 @@ where
|
|||
// and I don't think the assoc item where-bounds are allowed to be coinductive.
|
||||
self.add_goals(
|
||||
GoalSource::Misc,
|
||||
tcx.predicates_of(inherent.def_id)
|
||||
.iter_instantiated(tcx, inherent_args)
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
cx.predicates_of(inherent.def_id)
|
||||
.iter_instantiated(cx, inherent_args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
|
||||
let normalized = cx.type_of(inherent.def_id).instantiate(cx, inherent_args);
|
||||
self.instantiate_normalizes_to_term(goal, normalized.into());
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,16 +84,16 @@ where
|
|||
self.self_ty()
|
||||
}
|
||||
|
||||
fn trait_ref(self, tcx: I) -> ty::TraitRef<I> {
|
||||
self.alias.trait_ref(tcx)
|
||||
fn trait_ref(self, cx: I) -> ty::TraitRef<I> {
|
||||
self.alias.trait_ref(cx)
|
||||
}
|
||||
|
||||
fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
|
||||
self.with_self_ty(tcx, self_ty)
|
||||
fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
|
||||
self.with_self_ty(cx, self_ty)
|
||||
}
|
||||
|
||||
fn trait_def_id(self, tcx: I) -> I::DefId {
|
||||
self.trait_def_id(tcx)
|
||||
fn trait_def_id(self, cx: I) -> I::DefId {
|
||||
self.trait_def_id(cx)
|
||||
}
|
||||
|
||||
fn probe_and_match_goal_against_assumption(
|
||||
|
|
@ -105,7 +105,7 @@ where
|
|||
) -> Result<Candidate<I>, NoSolution> {
|
||||
if let Some(projection_pred) = assumption.as_projection_clause() {
|
||||
if projection_pred.projection_def_id() == goal.predicate.def_id() {
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
ecx.probe_trait_candidate(source).enter(|ecx| {
|
||||
let assumption_projection_pred =
|
||||
ecx.instantiate_binder_with_infer(projection_pred);
|
||||
|
|
@ -120,9 +120,9 @@ where
|
|||
// Add GAT where clauses from the trait's definition
|
||||
ecx.add_goals(
|
||||
GoalSource::Misc,
|
||||
tcx.own_predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(tcx, goal.predicate.alias.args)
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
cx.own_predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(cx, goal.predicate.alias.args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
then(ecx)
|
||||
|
|
@ -140,19 +140,19 @@ where
|
|||
goal: Goal<I, NormalizesTo<I>>,
|
||||
impl_def_id: I::DefId,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
|
||||
let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
|
||||
let goal_trait_ref = goal.predicate.alias.trait_ref(cx);
|
||||
let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
|
||||
if !ecx.cx().args_may_unify_deep(
|
||||
goal.predicate.alias.trait_ref(tcx).args,
|
||||
goal.predicate.alias.trait_ref(cx).args,
|
||||
impl_trait_ref.skip_binder().args,
|
||||
) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// We have to ignore negative impls when projecting.
|
||||
let impl_polarity = tcx.impl_polarity(impl_def_id);
|
||||
let impl_polarity = cx.impl_polarity(impl_def_id);
|
||||
match impl_polarity {
|
||||
ty::ImplPolarity::Negative => return Err(NoSolution),
|
||||
ty::ImplPolarity::Reservation => {
|
||||
|
|
@ -163,22 +163,22 @@ where
|
|||
|
||||
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
|
||||
let impl_args = ecx.fresh_args_for_item(impl_def_id);
|
||||
let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
|
||||
let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
|
||||
|
||||
ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
|
||||
|
||||
let where_clause_bounds = tcx
|
||||
let where_clause_bounds = cx
|
||||
.predicates_of(impl_def_id)
|
||||
.iter_instantiated(tcx, impl_args)
|
||||
.map(|pred| goal.with(tcx, pred));
|
||||
.iter_instantiated(cx, impl_args)
|
||||
.map(|pred| goal.with(cx, pred));
|
||||
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
|
||||
|
||||
// Add GAT where clauses from the trait's definition
|
||||
ecx.add_goals(
|
||||
GoalSource::Misc,
|
||||
tcx.own_predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(tcx, goal.predicate.alias.args)
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
cx.own_predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(cx, goal.predicate.alias.args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
// In case the associated item is hidden due to specialization, we have to
|
||||
|
|
@ -195,21 +195,21 @@ where
|
|||
};
|
||||
|
||||
let error_response = |ecx: &mut EvalCtxt<'_, D>, msg: &str| {
|
||||
let guar = tcx.delay_bug(msg);
|
||||
let error_term = match goal.predicate.alias.kind(tcx) {
|
||||
ty::AliasTermKind::ProjectionTy => Ty::new_error(tcx, guar).into(),
|
||||
ty::AliasTermKind::ProjectionConst => Const::new_error(tcx, guar).into(),
|
||||
let guar = cx.delay_bug(msg);
|
||||
let error_term = match goal.predicate.alias.kind(cx) {
|
||||
ty::AliasTermKind::ProjectionTy => Ty::new_error(cx, guar).into(),
|
||||
ty::AliasTermKind::ProjectionConst => Const::new_error(cx, guar).into(),
|
||||
kind => panic!("expected projection, found {kind:?}"),
|
||||
};
|
||||
ecx.instantiate_normalizes_to_term(goal, error_term);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
};
|
||||
|
||||
if !tcx.has_item_definition(target_item_def_id) {
|
||||
if !cx.has_item_definition(target_item_def_id) {
|
||||
return error_response(ecx, "missing item");
|
||||
}
|
||||
|
||||
let target_container_def_id = tcx.parent(target_item_def_id);
|
||||
let target_container_def_id = cx.parent(target_item_def_id);
|
||||
|
||||
// Getting the right args here is complex, e.g. given:
|
||||
// - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
|
||||
|
|
@ -229,22 +229,22 @@ where
|
|||
target_container_def_id,
|
||||
)?;
|
||||
|
||||
if !tcx.check_args_compatible(target_item_def_id, target_args) {
|
||||
if !cx.check_args_compatible(target_item_def_id, target_args) {
|
||||
return error_response(ecx, "associated item has mismatched arguments");
|
||||
}
|
||||
|
||||
// Finally we construct the actual value of the associated type.
|
||||
let term = match goal.predicate.alias.kind(tcx) {
|
||||
let term = match goal.predicate.alias.kind(cx) {
|
||||
ty::AliasTermKind::ProjectionTy => {
|
||||
tcx.type_of(target_item_def_id).map_bound(|ty| ty.into())
|
||||
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
|
||||
}
|
||||
ty::AliasTermKind::ProjectionConst => {
|
||||
if tcx.features().associated_const_equality() {
|
||||
if cx.features().associated_const_equality() {
|
||||
panic!("associated const projection is not supported yet")
|
||||
} else {
|
||||
ty::EarlyBinder::bind(
|
||||
Const::new_error_with_message(
|
||||
tcx,
|
||||
cx,
|
||||
"associated const projection is not supported yet",
|
||||
)
|
||||
.into(),
|
||||
|
|
@ -254,7 +254,7 @@ where
|
|||
kind => panic!("expected projection, found {kind:?}"),
|
||||
};
|
||||
|
||||
ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, target_args));
|
||||
ecx.instantiate_normalizes_to_term(goal, term.instantiate(cx, target_args));
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
|
|
@ -316,10 +316,10 @@ where
|
|||
goal: Goal<I, Self>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
let tupled_inputs_and_output =
|
||||
match structural_traits::extract_tupled_inputs_and_output_from_callable(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
)? {
|
||||
|
|
@ -329,19 +329,19 @@ where
|
|||
}
|
||||
};
|
||||
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
|
||||
ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
|
||||
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
|
||||
});
|
||||
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
|
||||
projection_term: ty::AliasTerm::new(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), inputs],
|
||||
),
|
||||
term: output.into(),
|
||||
})
|
||||
.upcast(tcx);
|
||||
.upcast(cx);
|
||||
|
||||
// A built-in `Fn` impl only holds if the output is sized.
|
||||
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
||||
|
|
@ -350,7 +350,7 @@ where
|
|||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
goal,
|
||||
pred,
|
||||
[(GoalSource::ImplWhereBound, goal.with(tcx, output_is_sized_pred))],
|
||||
[(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -359,27 +359,23 @@ where
|
|||
goal: Goal<I, Self>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
|
||||
let env_region = match goal_kind {
|
||||
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2),
|
||||
// Doesn't matter what this region is
|
||||
ty::ClosureKind::FnOnce => Region::new_static(tcx),
|
||||
ty::ClosureKind::FnOnce => Region::new_static(cx),
|
||||
};
|
||||
let (tupled_inputs_and_output_and_coroutine, nested_preds) =
|
||||
structural_traits::extract_tupled_inputs_and_output_from_async_callable(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
env_region,
|
||||
)?;
|
||||
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
||||
|AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::Sized),
|
||||
[output_ty],
|
||||
)
|
||||
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output_ty])
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -390,23 +386,23 @@ where
|
|||
output_coroutine_ty,
|
||||
coroutine_return_ty,
|
||||
}| {
|
||||
let (projection_term, term) = if tcx
|
||||
let (projection_term, term) = if cx
|
||||
.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture)
|
||||
{
|
||||
(
|
||||
ty::AliasTerm::new(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), tupled_inputs_ty],
|
||||
),
|
||||
output_coroutine_ty.into(),
|
||||
)
|
||||
} else if tcx
|
||||
} else if cx
|
||||
.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture)
|
||||
{
|
||||
(
|
||||
ty::AliasTerm::new(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
I::GenericArg::from(goal.predicate.self_ty()),
|
||||
|
|
@ -416,13 +412,13 @@ where
|
|||
),
|
||||
output_coroutine_ty.into(),
|
||||
)
|
||||
} else if tcx.is_lang_item(
|
||||
} else if cx.is_lang_item(
|
||||
goal.predicate.def_id(),
|
||||
TraitSolverLangItem::AsyncFnOnceOutput,
|
||||
) {
|
||||
(
|
||||
ty::AliasTerm::new(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
I::GenericArg::from(goal.predicate.self_ty()),
|
||||
|
|
@ -440,7 +436,7 @@ where
|
|||
ty::ProjectionPredicate { projection_term, term }
|
||||
},
|
||||
)
|
||||
.upcast(tcx);
|
||||
.upcast(cx);
|
||||
|
||||
// A built-in `AsyncFn` impl only holds if the output is sized.
|
||||
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
||||
|
|
@ -449,9 +445,9 @@ where
|
|||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
goal,
|
||||
pred,
|
||||
[goal.with(tcx, output_is_sized_pred)]
|
||||
[goal.with(cx, output_is_sized_pred)]
|
||||
.into_iter()
|
||||
.chain(nested_preds.into_iter().map(|pred| goal.with(tcx, pred)))
|
||||
.chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred)))
|
||||
.map(|goal| (GoalSource::ImplWhereBound, goal)),
|
||||
)
|
||||
}
|
||||
|
|
@ -514,8 +510,8 @@ where
|
|||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = ecx.cx();
|
||||
let metadata_def_id = tcx.require_lang_item(TraitSolverLangItem::Metadata);
|
||||
let cx = ecx.cx();
|
||||
let metadata_def_id = cx.require_lang_item(TraitSolverLangItem::Metadata);
|
||||
assert_eq!(metadata_def_id, goal.predicate.def_id());
|
||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
||||
let metadata_ty = match goal.predicate.self_ty().kind() {
|
||||
|
|
@ -537,16 +533,16 @@ where
|
|||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Foreign(..)
|
||||
| ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(tcx),
|
||||
| ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(cx),
|
||||
|
||||
ty::Error(e) => Ty::new_error(tcx, e),
|
||||
ty::Error(e) => Ty::new_error(cx, e),
|
||||
|
||||
ty::Str | ty::Slice(_) => Ty::new_usize(tcx),
|
||||
ty::Str | ty::Slice(_) => Ty::new_usize(cx),
|
||||
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
let dyn_metadata = tcx.require_lang_item(TraitSolverLangItem::DynMetadata);
|
||||
tcx.type_of(dyn_metadata)
|
||||
.instantiate(tcx, &[I::GenericArg::from(goal.predicate.self_ty())])
|
||||
let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata);
|
||||
cx.type_of(dyn_metadata)
|
||||
.instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())])
|
||||
}
|
||||
|
||||
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
|
||||
|
|
@ -555,26 +551,26 @@ where
|
|||
// FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
|
||||
// exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
|
||||
let sized_predicate = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::Sized),
|
||||
cx,
|
||||
cx.require_lang_item(TraitSolverLangItem::Sized),
|
||||
[I::GenericArg::from(goal.predicate.self_ty())],
|
||||
);
|
||||
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate));
|
||||
Ty::new_unit(tcx)
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate));
|
||||
Ty::new_unit(cx)
|
||||
}
|
||||
|
||||
ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(tcx) {
|
||||
None => Ty::new_unit(tcx),
|
||||
ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(cx) {
|
||||
None => Ty::new_unit(cx),
|
||||
Some(tail_ty) => {
|
||||
Ty::new_projection(tcx, metadata_def_id, [tail_ty.instantiate(tcx, args)])
|
||||
Ty::new_projection(cx, metadata_def_id, [tail_ty.instantiate(cx, args)])
|
||||
}
|
||||
},
|
||||
ty::Adt(_, _) => Ty::new_unit(tcx),
|
||||
ty::Adt(_, _) => Ty::new_unit(cx),
|
||||
|
||||
ty::Tuple(elements) => match elements.last() {
|
||||
None => Ty::new_unit(tcx),
|
||||
Some(tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
|
||||
None => Ty::new_unit(cx),
|
||||
Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]),
|
||||
},
|
||||
|
||||
ty::Infer(
|
||||
|
|
@ -601,8 +597,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not futures unless they come from `async` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_async(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_async(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -616,7 +612,7 @@ where
|
|||
projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.def_id(), [self_ty]),
|
||||
term,
|
||||
}
|
||||
.upcast(tcx),
|
||||
.upcast(cx),
|
||||
// Technically, we need to check that the future type is Sized,
|
||||
// but that's already proven by the coroutine being WF.
|
||||
[],
|
||||
|
|
@ -633,8 +629,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not Iterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_gen(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -648,7 +644,7 @@ where
|
|||
projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.def_id(), [self_ty]),
|
||||
term,
|
||||
}
|
||||
.upcast(tcx),
|
||||
.upcast(cx),
|
||||
// Technically, we need to check that the iterator type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
[],
|
||||
|
|
@ -672,8 +668,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not AsyncIterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_async_gen(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_async_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -682,12 +678,12 @@ where
|
|||
// Take `AsyncIterator<Item = I>` and turn it into the corresponding
|
||||
// coroutine yield ty `Poll<Option<I>>`.
|
||||
let wrapped_expected_ty = Ty::new_adt(
|
||||
tcx,
|
||||
tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Poll)),
|
||||
tcx.mk_args(&[Ty::new_adt(
|
||||
tcx,
|
||||
tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Option)),
|
||||
tcx.mk_args(&[expected_ty.into()]),
|
||||
cx,
|
||||
cx.adt_def(cx.require_lang_item(TraitSolverLangItem::Poll)),
|
||||
cx.mk_args(&[Ty::new_adt(
|
||||
cx,
|
||||
cx.adt_def(cx.require_lang_item(TraitSolverLangItem::Option)),
|
||||
cx.mk_args(&[expected_ty.into()]),
|
||||
)
|
||||
.into()]),
|
||||
);
|
||||
|
|
@ -708,18 +704,17 @@ where
|
|||
};
|
||||
|
||||
// `async`-desugared coroutines do not implement the coroutine trait
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.is_general_coroutine(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.is_general_coroutine(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let coroutine = args.as_coroutine();
|
||||
|
||||
let term = if tcx
|
||||
.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn)
|
||||
let term = if cx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn)
|
||||
{
|
||||
coroutine.return_ty().into()
|
||||
} else if tcx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) {
|
||||
} else if cx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) {
|
||||
coroutine.yield_ty().into()
|
||||
} else {
|
||||
panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id())
|
||||
|
|
@ -737,7 +732,7 @@ where
|
|||
),
|
||||
term,
|
||||
}
|
||||
.upcast(tcx),
|
||||
.upcast(cx),
|
||||
// Technically, we need to check that the coroutine type is Sized,
|
||||
// but that's already proven by the coroutine being WF.
|
||||
[],
|
||||
|
|
@ -884,29 +879,29 @@ where
|
|||
impl_trait_ref: rustc_type_ir::TraitRef<I>,
|
||||
target_container_def_id: I::DefId,
|
||||
) -> Result<I::GenericArgs, NoSolution> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
Ok(if target_container_def_id == impl_trait_ref.def_id {
|
||||
// Default value from the trait definition. No need to rebase.
|
||||
goal.predicate.alias.args
|
||||
} else if target_container_def_id == impl_def_id {
|
||||
// Same impl, no need to fully translate, just a rebase from
|
||||
// the trait is sufficient.
|
||||
goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args)
|
||||
goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id, impl_args)
|
||||
} else {
|
||||
let target_args = self.fresh_args_for_item(target_container_def_id);
|
||||
let target_trait_ref =
|
||||
tcx.impl_trait_ref(target_container_def_id).instantiate(tcx, target_args);
|
||||
cx.impl_trait_ref(target_container_def_id).instantiate(cx, target_args);
|
||||
// Relate source impl to target impl by equating trait refs.
|
||||
self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?;
|
||||
// Also add predicates since they may be needed to constrain the
|
||||
// target impl's params.
|
||||
self.add_goals(
|
||||
GoalSource::Misc,
|
||||
tcx.predicates_of(target_container_def_id)
|
||||
.iter_instantiated(tcx, target_args)
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
cx.predicates_of(target_container_def_id)
|
||||
.iter_instantiated(cx, target_args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args)
|
||||
goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id, target_args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, ty::NormalizesTo<I>>,
|
||||
) -> QueryResult<I> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let opaque_ty = goal.predicate.alias;
|
||||
let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ where
|
|||
}
|
||||
(Reveal::All, _) => {
|
||||
// FIXME: Add an assertion that opaque type storage is empty.
|
||||
let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args);
|
||||
let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
|
||||
self.eq(goal.param_env, expected, actual)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ where
|
|||
///
|
||||
/// FIXME: Interner argument is needed to constrain the `I` parameter.
|
||||
pub fn uses_unique_placeholders_ignoring_regions<I: Interner>(
|
||||
_interner: I,
|
||||
_cx: I,
|
||||
args: I::GenericArgs,
|
||||
) -> Result<(), NotUniqueParam<I>> {
|
||||
let mut seen = GrowableBitSet::default();
|
||||
|
|
|
|||
|
|
@ -18,18 +18,18 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, ty::NormalizesTo<I>>,
|
||||
) -> QueryResult<I> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let weak_ty = goal.predicate.alias;
|
||||
|
||||
// Check where clauses
|
||||
self.add_goals(
|
||||
GoalSource::Misc,
|
||||
tcx.predicates_of(weak_ty.def_id)
|
||||
.iter_instantiated(tcx, weak_ty.args)
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
cx.predicates_of(weak_ty.def_id)
|
||||
.iter_instantiated(cx, weak_ty.args)
|
||||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
|
||||
let actual = cx.type_of(weak_ty.def_id).instantiate(cx, weak_ty.args);
|
||||
self.instantiate_normalizes_to_term(goal, actual.into());
|
||||
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ where
|
|||
&mut self,
|
||||
goal: Goal<I, ProjectionPredicate<I>>,
|
||||
) -> QueryResult<I> {
|
||||
let tcx = self.cx();
|
||||
let projection_term = goal.predicate.projection_term.to_term(tcx);
|
||||
let cx = self.cx();
|
||||
let projection_term = goal.predicate.projection_term.to_term(cx);
|
||||
let goal = goal.with(
|
||||
tcx,
|
||||
cx,
|
||||
ty::PredicateKind::AliasRelate(
|
||||
projection_term,
|
||||
goal.predicate.term,
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ impl<I: Interner> SearchGraph<I> {
|
|||
/// the remaining depth of all nested goals to prevent hangs
|
||||
/// in case there is exponential blowup.
|
||||
fn allowed_depth_for_nested(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
stack: &IndexVec<StackDepth, StackEntry<I>>,
|
||||
) -> Option<SolverLimit> {
|
||||
if let Some(last) = stack.raw.last() {
|
||||
|
|
@ -178,18 +178,18 @@ impl<I: Interner> SearchGraph<I> {
|
|||
SolverLimit(last.available_depth.0 - 1)
|
||||
})
|
||||
} else {
|
||||
Some(SolverLimit(tcx.recursion_limit()))
|
||||
Some(SolverLimit(cx.recursion_limit()))
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_coinductive_from(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
stack: &IndexVec<StackDepth, StackEntry<I>>,
|
||||
head: StackDepth,
|
||||
) -> bool {
|
||||
stack.raw[head.index()..]
|
||||
.iter()
|
||||
.all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx))
|
||||
.all(|entry| entry.input.value.goal.predicate.is_coinductive(cx))
|
||||
}
|
||||
|
||||
// When encountering a solver cycle, the result of the current goal
|
||||
|
|
@ -247,8 +247,8 @@ impl<I: Interner> SearchGraph<I> {
|
|||
/// so we use a separate cache. Alternatively we could use
|
||||
/// a single cache and share it between coherence and ordinary
|
||||
/// trait solving.
|
||||
pub(super) fn global_cache(&self, tcx: I) -> I::EvaluationCache {
|
||||
tcx.evaluation_cache(self.mode)
|
||||
pub(super) fn global_cache(&self, cx: I) -> I::EvaluationCache {
|
||||
cx.evaluation_cache(self.mode)
|
||||
}
|
||||
|
||||
/// Probably the most involved method of the whole solver.
|
||||
|
|
@ -257,24 +257,24 @@ impl<I: Interner> SearchGraph<I> {
|
|||
/// handles caching, overflow, and coinductive cycles.
|
||||
pub(super) fn with_new_goal<D: SolverDelegate<Interner = I>>(
|
||||
&mut self,
|
||||
tcx: I,
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
inspect: &mut ProofTreeBuilder<D>,
|
||||
mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<D>) -> QueryResult<I>,
|
||||
) -> QueryResult<I> {
|
||||
self.check_invariants();
|
||||
// Check for overflow.
|
||||
let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
|
||||
let Some(available_depth) = Self::allowed_depth_for_nested(cx, &self.stack) else {
|
||||
if let Some(last) = self.stack.raw.last_mut() {
|
||||
last.encountered_overflow = true;
|
||||
}
|
||||
|
||||
inspect
|
||||
.canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
|
||||
return Self::response_no_constraints(tcx, input, Certainty::overflow(true));
|
||||
return Self::response_no_constraints(cx, input, Certainty::overflow(true));
|
||||
};
|
||||
|
||||
if let Some(result) = self.lookup_global_cache(tcx, input, available_depth, inspect) {
|
||||
if let Some(result) = self.lookup_global_cache(cx, input, available_depth, inspect) {
|
||||
debug!("global cache hit");
|
||||
return result;
|
||||
}
|
||||
|
|
@ -287,12 +287,12 @@ impl<I: Interner> SearchGraph<I> {
|
|||
if let Some(entry) = cache_entry
|
||||
.with_coinductive_stack
|
||||
.as_ref()
|
||||
.filter(|p| Self::stack_coinductive_from(tcx, &self.stack, p.head))
|
||||
.filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head))
|
||||
.or_else(|| {
|
||||
cache_entry
|
||||
.with_inductive_stack
|
||||
.as_ref()
|
||||
.filter(|p| !Self::stack_coinductive_from(tcx, &self.stack, p.head))
|
||||
.filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head))
|
||||
})
|
||||
{
|
||||
debug!("provisional cache hit");
|
||||
|
|
@ -315,7 +315,7 @@ impl<I: Interner> SearchGraph<I> {
|
|||
inspect.canonical_goal_evaluation_kind(
|
||||
inspect::WipCanonicalGoalEvaluationKind::CycleInStack,
|
||||
);
|
||||
let is_coinductive_cycle = Self::stack_coinductive_from(tcx, &self.stack, stack_depth);
|
||||
let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth);
|
||||
let usage_kind = if is_coinductive_cycle {
|
||||
HasBeenUsed::COINDUCTIVE_CYCLE
|
||||
} else {
|
||||
|
|
@ -328,9 +328,9 @@ impl<I: Interner> SearchGraph<I> {
|
|||
return if let Some(result) = self.stack[stack_depth].provisional_result {
|
||||
result
|
||||
} else if is_coinductive_cycle {
|
||||
Self::response_no_constraints(tcx, input, Certainty::Yes)
|
||||
Self::response_no_constraints(cx, input, Certainty::Yes)
|
||||
} else {
|
||||
Self::response_no_constraints(tcx, input, Certainty::overflow(false))
|
||||
Self::response_no_constraints(cx, input, Certainty::overflow(false))
|
||||
};
|
||||
} else {
|
||||
// No entry, we push this goal on the stack and try to prove it.
|
||||
|
|
@ -355,9 +355,9 @@ impl<I: Interner> SearchGraph<I> {
|
|||
// not tracked by the cache key and from outside of this anon task, it
|
||||
// must not be added to the global cache. Notably, this is the case for
|
||||
// trait solver cycles participants.
|
||||
let ((final_entry, result), dep_node) = tcx.with_cached_task(|| {
|
||||
let ((final_entry, result), dep_node) = cx.with_cached_task(|| {
|
||||
for _ in 0..FIXPOINT_STEP_LIMIT {
|
||||
match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) {
|
||||
match self.fixpoint_step_in_task(cx, input, inspect, &mut prove_goal) {
|
||||
StepResult::Done(final_entry, result) => return (final_entry, result),
|
||||
StepResult::HasChanged => debug!("fixpoint changed provisional results"),
|
||||
}
|
||||
|
|
@ -366,17 +366,17 @@ impl<I: Interner> SearchGraph<I> {
|
|||
debug!("canonical cycle overflow");
|
||||
let current_entry = self.pop_stack();
|
||||
debug_assert!(current_entry.has_been_used.is_empty());
|
||||
let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false));
|
||||
let result = Self::response_no_constraints(cx, input, Certainty::overflow(false));
|
||||
(current_entry, result)
|
||||
});
|
||||
|
||||
let proof_tree = inspect.finalize_canonical_goal_evaluation(tcx);
|
||||
let proof_tree = inspect.finalize_canonical_goal_evaluation(cx);
|
||||
|
||||
// We're now done with this goal. In case this goal is involved in a larger cycle
|
||||
// do not remove it from the provisional cache and update its provisional result.
|
||||
// We only add the root of cycles to the global cache.
|
||||
if let Some(head) = final_entry.non_root_cycle_participant {
|
||||
let coinductive_stack = Self::stack_coinductive_from(tcx, &self.stack, head);
|
||||
let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head);
|
||||
|
||||
let entry = self.provisional_cache.get_mut(&input).unwrap();
|
||||
entry.stack_depth = None;
|
||||
|
|
@ -396,8 +396,8 @@ impl<I: Interner> SearchGraph<I> {
|
|||
// participant is on the stack. This is necessary to prevent unstable
|
||||
// results. See the comment of `StackEntry::cycle_participants` for
|
||||
// more details.
|
||||
self.global_cache(tcx).insert(
|
||||
tcx,
|
||||
self.global_cache(cx).insert(
|
||||
cx,
|
||||
input,
|
||||
proof_tree,
|
||||
reached_depth,
|
||||
|
|
@ -418,15 +418,15 @@ impl<I: Interner> SearchGraph<I> {
|
|||
/// this goal.
|
||||
fn lookup_global_cache<D: SolverDelegate<Interner = I>>(
|
||||
&mut self,
|
||||
tcx: I,
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
available_depth: SolverLimit,
|
||||
inspect: &mut ProofTreeBuilder<D>,
|
||||
) -> Option<QueryResult<I>> {
|
||||
let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self
|
||||
.global_cache(tcx)
|
||||
.global_cache(cx)
|
||||
// FIXME: Awkward `Limit -> usize -> Limit`.
|
||||
.get(tcx, input, self.stack.iter().map(|e| e.input), available_depth.0)?;
|
||||
.get(cx, input, self.stack.iter().map(|e| e.input), available_depth.0)?;
|
||||
|
||||
// If we're building a proof tree and the current cache entry does not
|
||||
// contain a proof tree, we do not use the entry but instead recompute
|
||||
|
|
@ -467,7 +467,7 @@ impl<I: Interner> SearchGraph<I> {
|
|||
/// point we are done.
|
||||
fn fixpoint_step_in_task<D, F>(
|
||||
&mut self,
|
||||
tcx: I,
|
||||
cx: I,
|
||||
input: CanonicalInput<I>,
|
||||
inspect: &mut ProofTreeBuilder<D>,
|
||||
prove_goal: &mut F,
|
||||
|
|
@ -506,9 +506,9 @@ impl<I: Interner> SearchGraph<I> {
|
|||
let reached_fixpoint = if let Some(r) = stack_entry.provisional_result {
|
||||
r == result
|
||||
} else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE {
|
||||
Self::response_no_constraints(tcx, input, Certainty::Yes) == result
|
||||
Self::response_no_constraints(cx, input, Certainty::Yes) == result
|
||||
} else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE {
|
||||
Self::response_no_constraints(tcx, input, Certainty::overflow(false)) == result
|
||||
Self::response_no_constraints(cx, input, Certainty::overflow(false)) == result
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
@ -528,11 +528,11 @@ impl<I: Interner> SearchGraph<I> {
|
|||
}
|
||||
|
||||
fn response_no_constraints(
|
||||
tcx: I,
|
||||
cx: I,
|
||||
goal: CanonicalInput<I>,
|
||||
certainty: Certainty,
|
||||
) -> QueryResult<I> {
|
||||
Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
|
||||
Ok(super::response_no_constraints_raw(cx, goal.max_universe, goal.variables, certainty))
|
||||
}
|
||||
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ where
|
|||
self.trait_ref
|
||||
}
|
||||
|
||||
fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
|
||||
self.with_self_ty(tcx, self_ty)
|
||||
fn with_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
|
||||
self.with_self_ty(cx, self_ty)
|
||||
}
|
||||
|
||||
fn trait_def_id(self, _: I) -> I::DefId {
|
||||
|
|
@ -43,18 +43,17 @@ where
|
|||
goal: Goal<I, TraitPredicate<I>>,
|
||||
impl_def_id: I::DefId,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
|
||||
if !tcx
|
||||
.args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
|
||||
let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
|
||||
if !cx.args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
|
||||
{
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// An upper bound of the certainty of this goal, used to lower the certainty
|
||||
// of reservation impl to ambiguous during coherence.
|
||||
let impl_polarity = tcx.impl_polarity(impl_def_id);
|
||||
let impl_polarity = cx.impl_polarity(impl_def_id);
|
||||
let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
|
||||
// In intercrate mode, this is ambiguous. But outside of intercrate,
|
||||
// it's not a real impl.
|
||||
|
|
@ -77,13 +76,13 @@ where
|
|||
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
|
||||
let impl_args = ecx.fresh_args_for_item(impl_def_id);
|
||||
ecx.record_impl_args(impl_args);
|
||||
let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
|
||||
let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
|
||||
|
||||
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
|
||||
let where_clause_bounds = tcx
|
||||
let where_clause_bounds = cx
|
||||
.predicates_of(impl_def_id)
|
||||
.iter_instantiated(tcx, impl_args)
|
||||
.map(|pred| goal.with(tcx, pred));
|
||||
.iter_instantiated(cx, impl_args)
|
||||
.map(|pred| goal.with(cx, pred));
|
||||
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
|
||||
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
||||
|
|
@ -181,13 +180,13 @@ where
|
|||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
|
||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
|
||||
let nested_obligations = tcx
|
||||
let nested_obligations = cx
|
||||
.predicates_of(goal.predicate.def_id())
|
||||
.iter_instantiated(tcx, goal.predicate.trait_ref.args)
|
||||
.map(|p| goal.with(tcx, p));
|
||||
.iter_instantiated(cx, goal.predicate.trait_ref.args)
|
||||
.map(|p| goal.with(cx, p));
|
||||
// FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
|
||||
ecx.add_goals(GoalSource::Misc, nested_obligations);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
|
|
@ -232,13 +231,13 @@ where
|
|||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
// But if there are inference variables, we have to wait until it's resolved.
|
||||
if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() {
|
||||
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
|
||||
}
|
||||
|
||||
if tcx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) {
|
||||
if cx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) {
|
||||
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
|
||||
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
||||
} else {
|
||||
|
|
@ -286,10 +285,10 @@ where
|
|||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
let tupled_inputs_and_output =
|
||||
match structural_traits::extract_tupled_inputs_and_output_from_callable(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
)? {
|
||||
|
|
@ -299,14 +298,14 @@ where
|
|||
}
|
||||
};
|
||||
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
|
||||
ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
|
||||
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
|
||||
});
|
||||
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, _)| {
|
||||
ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
||||
ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
||||
})
|
||||
.upcast(tcx);
|
||||
.upcast(cx);
|
||||
// A built-in `Fn` impl only holds if the output is sized.
|
||||
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
||||
Self::probe_and_consider_implied_clause(
|
||||
|
|
@ -314,7 +313,7 @@ where
|
|||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
goal,
|
||||
pred,
|
||||
[(GoalSource::ImplWhereBound, goal.with(tcx, output_is_sized_pred))],
|
||||
[(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -327,20 +326,20 @@ where
|
|||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tcx = ecx.cx();
|
||||
let cx = ecx.cx();
|
||||
let (tupled_inputs_and_output_and_coroutine, nested_preds) =
|
||||
structural_traits::extract_tupled_inputs_and_output_from_async_callable(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
// This region doesn't matter because we're throwing away the coroutine type
|
||||
Region::new_static(tcx),
|
||||
Region::new_static(cx),
|
||||
)?;
|
||||
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
||||
|AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::Sized),
|
||||
cx,
|
||||
cx.require_lang_item(TraitSolverLangItem::Sized),
|
||||
[output_coroutine_ty],
|
||||
)
|
||||
},
|
||||
|
|
@ -349,12 +348,12 @@ where
|
|||
let pred = tupled_inputs_and_output_and_coroutine
|
||||
.map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
cx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), tupled_inputs_ty],
|
||||
)
|
||||
})
|
||||
.upcast(tcx);
|
||||
.upcast(cx);
|
||||
// A built-in `AsyncFn` impl only holds if the output is sized.
|
||||
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
||||
Self::probe_and_consider_implied_clause(
|
||||
|
|
@ -362,9 +361,9 @@ where
|
|||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
goal,
|
||||
pred,
|
||||
[goal.with(tcx, output_is_sized_pred)]
|
||||
[goal.with(cx, output_is_sized_pred)]
|
||||
.into_iter()
|
||||
.chain(nested_preds.into_iter().map(|pred| goal.with(tcx, pred)))
|
||||
.chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred)))
|
||||
.map(|goal| (GoalSource::ImplWhereBound, goal)),
|
||||
)
|
||||
}
|
||||
|
|
@ -437,8 +436,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not futures unless they come from `async` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_async(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_async(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -463,8 +462,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not iterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_gen(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -489,8 +488,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not iterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_gen(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -513,8 +512,8 @@ where
|
|||
};
|
||||
|
||||
// Coroutines are not iterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.coroutine_is_async_gen(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.coroutine_is_async_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -540,8 +539,8 @@ where
|
|||
};
|
||||
|
||||
// `async`-desugared coroutines do not implement the coroutine trait
|
||||
let tcx = ecx.cx();
|
||||
if !tcx.is_general_coroutine(def_id) {
|
||||
let cx = ecx.cx();
|
||||
if !cx.is_general_coroutine(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -550,8 +549,8 @@ where
|
|||
ecx,
|
||||
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||
goal,
|
||||
ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
|
||||
.upcast(tcx),
|
||||
ty::TraitRef::new(cx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
|
||||
.upcast(cx),
|
||||
// Technically, we need to check that the coroutine types are Sized,
|
||||
// but that's already proven by the coroutine being WF.
|
||||
[],
|
||||
|
|
@ -727,7 +726,7 @@ where
|
|||
b_data: I::BoundExistentialPredicates,
|
||||
b_region: I::Region,
|
||||
) -> Vec<Candidate<I>> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
|
||||
|
||||
let mut responses = vec![];
|
||||
|
|
@ -745,7 +744,7 @@ where
|
|||
));
|
||||
} else if let Some(a_principal) = a_data.principal() {
|
||||
for new_a_principal in
|
||||
D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(tcx, a_ty)).skip(1)
|
||||
D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1)
|
||||
{
|
||||
responses.extend(self.consider_builtin_upcast_to_principal(
|
||||
goal,
|
||||
|
|
@ -755,7 +754,7 @@ where
|
|||
b_data,
|
||||
b_region,
|
||||
Some(new_a_principal.map_bound(|trait_ref| {
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||
ty::ExistentialTraitRef::erase_self_ty(cx, trait_ref)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
|
@ -770,11 +769,11 @@ where
|
|||
b_data: I::BoundExistentialPredicates,
|
||||
b_region: I::Region,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let Goal { predicate: (a_ty, _), .. } = goal;
|
||||
|
||||
// Can only unsize to an object-safe trait.
|
||||
if b_data.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
|
||||
if b_data.principal_def_id().is_some_and(|def_id| !cx.trait_is_object_safe(def_id)) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
|
@ -783,24 +782,20 @@ where
|
|||
// (i.e. the principal, all of the associated types match, and any auto traits)
|
||||
ecx.add_goals(
|
||||
GoalSource::ImplWhereBound,
|
||||
b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
|
||||
b_data.iter().map(|pred| goal.with(cx, pred.with_self_ty(cx, a_ty))),
|
||||
);
|
||||
|
||||
// The type must be `Sized` to be unsized.
|
||||
ecx.add_goal(
|
||||
GoalSource::ImplWhereBound,
|
||||
goal.with(
|
||||
tcx,
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::Sized),
|
||||
[a_ty],
|
||||
),
|
||||
cx,
|
||||
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [a_ty]),
|
||||
),
|
||||
);
|
||||
|
||||
// The type must outlive the lifetime of the `dyn` we're unsizing into.
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region)));
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
|
|
@ -941,28 +936,28 @@ where
|
|||
a_args: I::GenericArgs,
|
||||
b_args: I::GenericArgs,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let Goal { predicate: (_a_ty, b_ty), .. } = goal;
|
||||
|
||||
let unsizing_params = tcx.unsizing_params_for_adt(def.def_id());
|
||||
let unsizing_params = cx.unsizing_params_for_adt(def.def_id());
|
||||
// We must be unsizing some type parameters. This also implies
|
||||
// that the struct has a tail field.
|
||||
if unsizing_params.is_empty() {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let tail_field_ty = def.struct_tail_ty(tcx).unwrap();
|
||||
let tail_field_ty = def.struct_tail_ty(cx).unwrap();
|
||||
|
||||
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
|
||||
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
|
||||
let a_tail_ty = tail_field_ty.instantiate(cx, a_args);
|
||||
let b_tail_ty = tail_field_ty.instantiate(cx, b_args);
|
||||
|
||||
// Instantiate just the unsizing params from B into A. The type after
|
||||
// this instantiation must be equal to B. This is so we don't unsize
|
||||
// unrelated type parameters.
|
||||
let new_a_args = tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
|
||||
let new_a_args = cx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
|
||||
if unsizing_params.contains(i as u32) { b_args.get(i).unwrap() } else { a }
|
||||
}));
|
||||
let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args);
|
||||
let unsized_a_ty = Ty::new_adt(cx, def, new_a_args);
|
||||
|
||||
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
|
||||
// types.
|
||||
|
|
@ -970,10 +965,10 @@ where
|
|||
self.add_goal(
|
||||
GoalSource::ImplWhereBound,
|
||||
goal.with(
|
||||
tcx,
|
||||
cx,
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::Unsize),
|
||||
cx,
|
||||
cx.require_lang_item(TraitSolverLangItem::Unsize),
|
||||
[a_tail_ty, b_tail_ty],
|
||||
),
|
||||
),
|
||||
|
|
@ -998,25 +993,24 @@ where
|
|||
a_tys: I::Tys,
|
||||
b_tys: I::Tys,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let tcx = self.cx();
|
||||
let cx = self.cx();
|
||||
let Goal { predicate: (_a_ty, b_ty), .. } = goal;
|
||||
|
||||
let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
|
||||
let b_last_ty = b_tys.last().unwrap();
|
||||
|
||||
// Instantiate just the tail field of B., and require that they're equal.
|
||||
let unsized_a_ty =
|
||||
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty]));
|
||||
let unsized_a_ty = Ty::new_tup_from_iter(cx, a_rest_tys.iter().copied().chain([b_last_ty]));
|
||||
self.eq(goal.param_env, unsized_a_ty, b_ty)?;
|
||||
|
||||
// Similar to ADTs, require that we can unsize the tail.
|
||||
self.add_goal(
|
||||
GoalSource::ImplWhereBound,
|
||||
goal.with(
|
||||
tcx,
|
||||
cx,
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(TraitSolverLangItem::Unsize),
|
||||
cx,
|
||||
cx.require_lang_item(TraitSolverLangItem::Unsize),
|
||||
[a_last_ty, b_last_ty],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ impl<'a> Parser<'a> {
|
|||
self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span });
|
||||
}
|
||||
// FIXME(gen_blocks): emit a similar error for `gen fn()`
|
||||
let decl_span = span_start.to(self.token.span);
|
||||
let decl_span = span_start.to(self.prev_token.span);
|
||||
Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span })))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
|
||||
fn handle_res(&mut self, res: Res) {
|
||||
match res {
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => {
|
||||
Res::Def(
|
||||
DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
|
||||
def_id,
|
||||
) => {
|
||||
self.check_def_id(def_id);
|
||||
}
|
||||
_ if self.in_pat => {}
|
||||
|
|
@ -399,6 +402,31 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
|
||||
// cause external crate may call such methods to construct values of these types
|
||||
if let Some(local_impl_of) = impl_of.as_local()
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(fn_sig) =
|
||||
self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
|
||||
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
|
||||
&& let TyKind::Path(hir::QPath::Resolved(_, path)) =
|
||||
self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind
|
||||
&& let Res::Def(def_kind, did) = path.res
|
||||
{
|
||||
match def_kind {
|
||||
// for example, #[derive(Default)] pub struct T(i32);
|
||||
// external crate can call T::default() to construct T,
|
||||
// so that don't ignore impl Default for pub Enum and Structs
|
||||
DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
|
||||
return false;
|
||||
}
|
||||
// don't ignore impl Default for Enums,
|
||||
// cause we don't know which variant is constructed
|
||||
DefKind::Enum => return false,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
|
||||
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
|
||||
{
|
||||
|
|
@ -441,7 +469,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
intravisit::walk_item(self, item)
|
||||
}
|
||||
hir::ItemKind::ForeignMod { .. } => {}
|
||||
hir::ItemKind::Trait(..) => {
|
||||
hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => {
|
||||
for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
|
||||
if let Some(local_def_id) = impl_def_id.as_local()
|
||||
&& let ItemKind::Impl(impl_ref) =
|
||||
|
|
@ -454,7 +482,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
|
||||
}
|
||||
}
|
||||
|
||||
// mark assoc ty live if the trait is live
|
||||
for trait_item in trait_item_refs {
|
||||
if let hir::AssocItemKind::Type = trait_item.kind {
|
||||
self.check_def_id(trait_item.id.owner_id.to_def_id());
|
||||
}
|
||||
}
|
||||
intravisit::walk_item(self, item)
|
||||
}
|
||||
_ => intravisit::walk_item(self, item),
|
||||
|
|
@ -471,9 +504,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
&& let ItemKind::Impl(impl_ref) =
|
||||
self.tcx.hir().expect_item(local_impl_id).kind
|
||||
{
|
||||
if !matches!(trait_item.kind, hir::TraitItemKind::Type(..))
|
||||
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
|
||||
.ty_and_all_fields_are_public
|
||||
if !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
|
||||
.ty_and_all_fields_are_public
|
||||
{
|
||||
// skip impl-items of non pure pub ty,
|
||||
// cause we don't know the ty is constructed or not,
|
||||
|
|
@ -812,9 +844,8 @@ fn check_item<'tcx>(
|
|||
// for trait impl blocks,
|
||||
// mark the method live if the self_ty is public,
|
||||
// or the method is public and may construct self
|
||||
if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy)
|
||||
|| tcx.visibility(local_def_id).is_public()
|
||||
&& (ty_and_all_fields_are_public || may_construct_self)
|
||||
if tcx.visibility(local_def_id).is_public()
|
||||
&& (ty_and_all_fields_are_public || may_construct_self)
|
||||
{
|
||||
// if the impl item is public,
|
||||
// and the ty may be constructed or can be constructed in foreign crates,
|
||||
|
|
@ -851,10 +882,13 @@ fn check_trait_item(
|
|||
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
|
||||
id: hir::TraitItemId,
|
||||
) {
|
||||
use hir::TraitItemKind::{Const, Fn};
|
||||
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
|
||||
use hir::TraitItemKind::{Const, Fn, Type};
|
||||
if matches!(
|
||||
tcx.def_kind(id.owner_id),
|
||||
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn
|
||||
) {
|
||||
let trait_item = tcx.hir().trait_item(id);
|
||||
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
|
||||
if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..))
|
||||
&& let Some(comes_from_allow) =
|
||||
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
|
||||
{
|
||||
|
|
@ -896,7 +930,7 @@ fn create_and_seed_worklist(
|
|||
// checks impls, impl-items and pub structs with all public fields later
|
||||
match tcx.def_kind(id) {
|
||||
DefKind::Impl { .. } => false,
|
||||
DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
|
||||
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
|
||||
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
|
||||
_ => true
|
||||
})
|
||||
|
|
@ -1183,6 +1217,7 @@ impl<'tcx> DeadVisitor<'tcx> {
|
|||
}
|
||||
match self.tcx.def_kind(def_id) {
|
||||
DefKind::AssocConst
|
||||
| DefKind::AssocTy
|
||||
| DefKind::AssocFn
|
||||
| DefKind::Fn
|
||||
| DefKind::Static { .. }
|
||||
|
|
@ -1224,15 +1259,14 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
|
|||
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
|
||||
{
|
||||
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
|
||||
// We have diagnosed unused assoc consts and fns in traits
|
||||
// We have diagnosed unused assocs in traits
|
||||
if matches!(def_kind, DefKind::Impl { of_trait: true })
|
||||
&& matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn)
|
||||
&& matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn)
|
||||
// skip unused public inherent methods,
|
||||
// cause we have diagnosed unconstructed struct
|
||||
|| matches!(def_kind, DefKind::Impl { of_trait: false })
|
||||
&& tcx.visibility(def_id).is_public()
|
||||
&& ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
|
||||
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::middle::privacy::{self, Level};
|
||||
use rustc_middle::mir::interpret::{ConstAllocation, ErrorHandled, GlobalAlloc};
|
||||
use rustc_middle::query::Providers;
|
||||
|
|
@ -178,15 +178,7 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
if !self.any_library {
|
||||
// If we are building an executable, only explicitly extern
|
||||
// types need to be exported.
|
||||
let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() {
|
||||
self.tcx.codegen_fn_attrs(search_item)
|
||||
} else {
|
||||
CodegenFnAttrs::EMPTY
|
||||
};
|
||||
let is_extern = codegen_attrs.contains_extern_indicator();
|
||||
let std_internal =
|
||||
codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
|
||||
if is_extern || std_internal {
|
||||
if has_custom_linkage(self.tcx, search_item) {
|
||||
self.reachable_symbols.insert(search_item);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
|
|||
// is the maximum value among visibilities of bindings corresponding to that def id.
|
||||
for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
|
||||
let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
|
||||
if !binding.is_ambiguity() {
|
||||
if !binding.is_ambiguity_recursive() {
|
||||
if let Some(node_id) = import.id() {
|
||||
r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,8 +325,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
}
|
||||
match (old_binding.is_glob_import(), binding.is_glob_import()) {
|
||||
(true, true) => {
|
||||
// FIXME: remove `!binding.is_ambiguity()` after delete the warning ambiguity.
|
||||
if !binding.is_ambiguity()
|
||||
// FIXME: remove `!binding.is_ambiguity_recursive()` after delete the warning ambiguity.
|
||||
if !binding.is_ambiguity_recursive()
|
||||
&& let NameBindingKind::Import { import: old_import, .. } =
|
||||
old_binding.kind
|
||||
&& let NameBindingKind::Import { import, .. } = binding.kind
|
||||
|
|
@ -337,21 +337,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
// imported from the same glob-import statement.
|
||||
resolution.binding = Some(binding);
|
||||
} else if res != old_binding.res() {
|
||||
let binding = if warn_ambiguity {
|
||||
this.warn_ambiguity(AmbiguityKind::GlobVsGlob, old_binding, binding)
|
||||
} else {
|
||||
this.ambiguity(AmbiguityKind::GlobVsGlob, old_binding, binding)
|
||||
};
|
||||
resolution.binding = Some(binding);
|
||||
resolution.binding = Some(this.new_ambiguity_binding(
|
||||
AmbiguityKind::GlobVsGlob,
|
||||
old_binding,
|
||||
binding,
|
||||
warn_ambiguity,
|
||||
));
|
||||
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
|
||||
// We are glob-importing the same item but with greater visibility.
|
||||
resolution.binding = Some(binding);
|
||||
} else if binding.is_ambiguity() {
|
||||
resolution.binding =
|
||||
Some(self.arenas.alloc_name_binding(NameBindingData {
|
||||
warn_ambiguity: true,
|
||||
..(*binding).clone()
|
||||
}));
|
||||
} else if binding.is_ambiguity_recursive() {
|
||||
resolution.binding = Some(this.new_warn_ambiguity_binding(binding));
|
||||
}
|
||||
}
|
||||
(old_glob @ true, false) | (old_glob @ false, true) => {
|
||||
|
|
@ -361,24 +357,26 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
&& nonglob_binding.expansion != LocalExpnId::ROOT
|
||||
&& glob_binding.res() != nonglob_binding.res()
|
||||
{
|
||||
resolution.binding = Some(this.ambiguity(
|
||||
resolution.binding = Some(this.new_ambiguity_binding(
|
||||
AmbiguityKind::GlobVsExpanded,
|
||||
nonglob_binding,
|
||||
glob_binding,
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
resolution.binding = Some(nonglob_binding);
|
||||
}
|
||||
|
||||
if let Some(old_binding) = resolution.shadowed_glob {
|
||||
assert!(old_binding.is_glob_import());
|
||||
if glob_binding.res() != old_binding.res() {
|
||||
resolution.shadowed_glob = Some(this.ambiguity(
|
||||
if let Some(old_shadowed_glob) = resolution.shadowed_glob {
|
||||
assert!(old_shadowed_glob.is_glob_import());
|
||||
if glob_binding.res() != old_shadowed_glob.res() {
|
||||
resolution.shadowed_glob = Some(this.new_ambiguity_binding(
|
||||
AmbiguityKind::GlobVsGlob,
|
||||
old_binding,
|
||||
old_shadowed_glob,
|
||||
glob_binding,
|
||||
false,
|
||||
));
|
||||
} else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
|
||||
} else if !old_shadowed_glob.vis.is_at_least(binding.vis, this.tcx) {
|
||||
resolution.shadowed_glob = Some(glob_binding);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -397,29 +395,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
fn ambiguity(
|
||||
fn new_ambiguity_binding(
|
||||
&self,
|
||||
kind: AmbiguityKind,
|
||||
ambiguity_kind: AmbiguityKind,
|
||||
primary_binding: NameBinding<'a>,
|
||||
secondary_binding: NameBinding<'a>,
|
||||
warn_ambiguity: bool,
|
||||
) -> NameBinding<'a> {
|
||||
self.arenas.alloc_name_binding(NameBindingData {
|
||||
ambiguity: Some((secondary_binding, kind)),
|
||||
..(*primary_binding).clone()
|
||||
})
|
||||
let ambiguity = Some((secondary_binding, ambiguity_kind));
|
||||
let data = NameBindingData { ambiguity, warn_ambiguity, ..*primary_binding };
|
||||
self.arenas.alloc_name_binding(data)
|
||||
}
|
||||
|
||||
fn warn_ambiguity(
|
||||
&self,
|
||||
kind: AmbiguityKind,
|
||||
primary_binding: NameBinding<'a>,
|
||||
secondary_binding: NameBinding<'a>,
|
||||
) -> NameBinding<'a> {
|
||||
self.arenas.alloc_name_binding(NameBindingData {
|
||||
ambiguity: Some((secondary_binding, kind)),
|
||||
warn_ambiguity: true,
|
||||
..(*primary_binding).clone()
|
||||
})
|
||||
fn new_warn_ambiguity_binding(&self, binding: NameBinding<'a>) -> NameBinding<'a> {
|
||||
assert!(binding.is_ambiguity_recursive());
|
||||
self.arenas.alloc_name_binding(NameBindingData { warn_ambiguity: true, ..*binding })
|
||||
}
|
||||
|
||||
// Use `f` to mutate the resolution of the name in the module.
|
||||
|
|
@ -1381,8 +1371,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
target_bindings[ns].get(),
|
||||
) {
|
||||
Ok(other_binding) => {
|
||||
is_redundant =
|
||||
binding.res() == other_binding.res() && !other_binding.is_ambiguity();
|
||||
is_redundant = binding.res() == other_binding.res()
|
||||
&& !other_binding.is_ambiguity_recursive();
|
||||
if is_redundant {
|
||||
redundant_span[ns] =
|
||||
Some((other_binding.span, other_binding.is_import()));
|
||||
|
|
@ -1455,7 +1445,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
.resolution(import.parent_scope.module, key)
|
||||
.borrow()
|
||||
.binding()
|
||||
.is_some_and(|binding| binding.is_warn_ambiguity());
|
||||
.is_some_and(|binding| binding.warn_ambiguity_recursive());
|
||||
let _ = self.try_define(
|
||||
import.parent_scope.module,
|
||||
key,
|
||||
|
|
@ -1480,7 +1470,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
|
||||
module.for_each_child(self, |this, ident, _, binding| {
|
||||
let res = binding.res().expect_non_local();
|
||||
let error_ambiguity = binding.is_ambiguity() && !binding.warn_ambiguity;
|
||||
let error_ambiguity = binding.is_ambiguity_recursive() && !binding.warn_ambiguity;
|
||||
if res != def::Res::Err && !error_ambiguity {
|
||||
let mut reexport_chain = SmallVec::new();
|
||||
let mut next_binding = binding;
|
||||
|
|
|
|||
|
|
@ -3730,7 +3730,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
let ls_binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS)?;
|
||||
let (res, binding) = match ls_binding {
|
||||
LexicalScopeBinding::Item(binding)
|
||||
if is_syntactic_ambiguity && binding.is_ambiguity() =>
|
||||
if is_syntactic_ambiguity && binding.is_ambiguity_recursive() =>
|
||||
{
|
||||
// For ambiguous bindings we don't know all their definitions and cannot check
|
||||
// whether they can be shadowed by fresh bindings or not, so force an error.
|
||||
|
|
|
|||
|
|
@ -694,10 +694,12 @@ impl<'a> fmt::Debug for Module<'a> {
|
|||
}
|
||||
|
||||
/// Records a possibly-private value, type, or module definition.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct NameBindingData<'a> {
|
||||
kind: NameBindingKind<'a>,
|
||||
ambiguity: Option<(NameBinding<'a>, AmbiguityKind)>,
|
||||
/// Produce a warning instead of an error when reporting ambiguities inside this binding.
|
||||
/// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required.
|
||||
warn_ambiguity: bool,
|
||||
expansion: LocalExpnId,
|
||||
span: Span,
|
||||
|
|
@ -718,7 +720,7 @@ impl<'a> ToNameBinding<'a> for NameBinding<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum NameBindingKind<'a> {
|
||||
Res(Res),
|
||||
Module(Module<'a>),
|
||||
|
|
@ -830,18 +832,18 @@ impl<'a> NameBindingData<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_ambiguity(&self) -> bool {
|
||||
fn is_ambiguity_recursive(&self) -> bool {
|
||||
self.ambiguity.is_some()
|
||||
|| match self.kind {
|
||||
NameBindingKind::Import { binding, .. } => binding.is_ambiguity(),
|
||||
NameBindingKind::Import { binding, .. } => binding.is_ambiguity_recursive(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_warn_ambiguity(&self) -> bool {
|
||||
fn warn_ambiguity_recursive(&self) -> bool {
|
||||
self.warn_ambiguity
|
||||
|| match self.kind {
|
||||
NameBindingKind::Import { binding, .. } => binding.is_warn_ambiguity(),
|
||||
NameBindingKind::Import { binding, .. } => binding.warn_ambiguity_recursive(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ fn show_arc() {
|
|||
|
||||
// Make sure deriving works with Arc<T>
|
||||
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)]
|
||||
struct Foo {
|
||||
struct _Foo {
|
||||
inner: Arc<i32>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar;
|
|||
/// ```
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Default")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)]
|
||||
pub trait Default: Sized {
|
||||
/// Returns the "default value" for a type.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -484,7 +484,7 @@ mod sealed_trait {
|
|||
all supported platforms",
|
||||
issue = "44930"
|
||||
)]
|
||||
pub trait VaArgSafe {}
|
||||
pub unsafe trait VaArgSafe {}
|
||||
}
|
||||
|
||||
macro_rules! impl_va_arg_safe {
|
||||
|
|
@ -494,7 +494,7 @@ macro_rules! impl_va_arg_safe {
|
|||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930")]
|
||||
impl sealed_trait::VaArgSafe for $t {}
|
||||
unsafe impl sealed_trait::VaArgSafe for $t {}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
|
@ -509,14 +509,15 @@ impl_va_arg_safe! {f64}
|
|||
all supported platforms",
|
||||
issue = "44930"
|
||||
)]
|
||||
impl<T> sealed_trait::VaArgSafe for *mut T {}
|
||||
unsafe impl<T> sealed_trait::VaArgSafe for *mut T {}
|
||||
|
||||
#[unstable(
|
||||
feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930"
|
||||
)]
|
||||
impl<T> sealed_trait::VaArgSafe for *const T {}
|
||||
unsafe impl<T> sealed_trait::VaArgSafe for *const T {}
|
||||
|
||||
#[unstable(
|
||||
feature = "c_variadic",
|
||||
|
|
|
|||
|
|
@ -2579,7 +2579,7 @@ extern "rust-intrinsic" {
|
|||
/// fn runtime() -> i32 { 1 }
|
||||
/// const fn compiletime() -> i32 { 2 }
|
||||
///
|
||||
// // ⚠ This code violates the required equivalence of `compiletime`
|
||||
/// // ⚠ This code violates the required equivalence of `compiletime`
|
||||
/// // and `runtime`.
|
||||
/// const_eval_select((), compiletime, runtime)
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedF
|
|||
use crate::num::NonZero;
|
||||
use crate::ops::Try;
|
||||
use core::array;
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
/// An iterator that filters the elements of `iter` with `predicate`.
|
||||
|
|
@ -27,6 +27,42 @@ impl<I, P> Filter<I, P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, P> Filter<I, P>
|
||||
where
|
||||
I: Iterator,
|
||||
P: FnMut(&I::Item) -> bool,
|
||||
{
|
||||
#[inline]
|
||||
fn next_chunk_dropless<const N: usize>(
|
||||
&mut self,
|
||||
) -> Result<[I::Item; N], array::IntoIter<I::Item, N>> {
|
||||
let mut array: [MaybeUninit<I::Item>; N] = [const { MaybeUninit::uninit() }; N];
|
||||
let mut initialized = 0;
|
||||
|
||||
let result = self.iter.try_for_each(|element| {
|
||||
let idx = initialized;
|
||||
// branchless index update combined with unconditionally copying the value even when
|
||||
// it is filtered reduces branching and dependencies in the loop.
|
||||
initialized = idx + (self.predicate)(&element) as usize;
|
||||
// SAFETY: Loop conditions ensure the index is in bounds.
|
||||
unsafe { array.get_unchecked_mut(idx) }.write(element);
|
||||
|
||||
if initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
|
||||
});
|
||||
|
||||
match result {
|
||||
ControlFlow::Break(()) => {
|
||||
// SAFETY: The loop above is only explicitly broken when the array has been fully initialized
|
||||
Ok(unsafe { MaybeUninit::array_assume_init(array) })
|
||||
}
|
||||
ControlFlow::Continue(()) => {
|
||||
// SAFETY: The range is in bounds since the loop breaks when reaching N elements.
|
||||
Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "core_impl_debug", since = "1.9.0")]
|
||||
impl<I: fmt::Debug, P> fmt::Debug for Filter<I, P> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
@ -64,52 +100,16 @@ where
|
|||
fn next_chunk<const N: usize>(
|
||||
&mut self,
|
||||
) -> Result<[Self::Item; N], array::IntoIter<Self::Item, N>> {
|
||||
let mut array: [MaybeUninit<Self::Item>; N] = [const { MaybeUninit::uninit() }; N];
|
||||
|
||||
struct Guard<'a, T> {
|
||||
array: &'a mut [MaybeUninit<T>],
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl<T> Drop for Guard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if const { crate::mem::needs_drop::<T>() } {
|
||||
// SAFETY: self.initialized is always <= N, which also is the length of the array.
|
||||
unsafe {
|
||||
core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
|
||||
self.array.get_unchecked_mut(..self.initialized),
|
||||
));
|
||||
}
|
||||
}
|
||||
// avoid codegen for the dead branch
|
||||
let fun = const {
|
||||
if crate::mem::needs_drop::<I::Item>() {
|
||||
array::iter_next_chunk::<I::Item, N>
|
||||
} else {
|
||||
Self::next_chunk_dropless::<N>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut guard = Guard { array: &mut array, initialized: 0 };
|
||||
|
||||
let result = self.iter.try_for_each(|element| {
|
||||
let idx = guard.initialized;
|
||||
guard.initialized = idx + (self.predicate)(&element) as usize;
|
||||
|
||||
// SAFETY: Loop conditions ensure the index is in bounds.
|
||||
unsafe { guard.array.get_unchecked_mut(idx) }.write(element);
|
||||
|
||||
if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
|
||||
});
|
||||
|
||||
let guard = ManuallyDrop::new(guard);
|
||||
|
||||
match result {
|
||||
ControlFlow::Break(()) => {
|
||||
// SAFETY: The loop above is only explicitly broken when the array has been fully initialized
|
||||
Ok(unsafe { MaybeUninit::array_assume_init(array) })
|
||||
}
|
||||
ControlFlow::Continue(()) => {
|
||||
let initialized = guard.initialized;
|
||||
// SAFETY: The range is in bounds since the loop breaks when reaching N elements.
|
||||
Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) })
|
||||
}
|
||||
}
|
||||
fun(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#![unstable(feature = "f128", issue = "116909")]
|
||||
|
||||
use crate::convert::FloatToInt;
|
||||
use crate::mem;
|
||||
|
||||
/// Basic mathematical constants.
|
||||
|
|
@ -220,21 +221,145 @@ impl f128 {
|
|||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub const MAX_10_EXP: i32 = 4_932;
|
||||
|
||||
/// Not a Number (NaN).
|
||||
///
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value;
|
||||
/// a plethora of bit patterns are considered to be NaN.
|
||||
/// Furthermore, the standard makes a difference
|
||||
/// between a "signaling" and a "quiet" NaN,
|
||||
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
|
||||
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
|
||||
/// and the stability of its representation over Rust versions
|
||||
/// and target platforms isn't guaranteed.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[allow(clippy::eq_op)]
|
||||
#[rustc_diagnostic_item = "f128_nan"]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub const NAN: f128 = 0.0_f128 / 0.0_f128;
|
||||
|
||||
/// Infinity (∞).
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub const INFINITY: f128 = 1.0_f128 / 0.0_f128;
|
||||
|
||||
/// Negative infinity (−∞).
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128;
|
||||
|
||||
/// Sign bit
|
||||
#[cfg(not(bootstrap))]
|
||||
pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
|
||||
|
||||
/// Minimum representable positive value (min subnormal)
|
||||
#[cfg(not(bootstrap))]
|
||||
const TINY_BITS: u128 = 0x1;
|
||||
|
||||
/// Minimum representable negative value (min negative subnormal)
|
||||
#[cfg(not(bootstrap))]
|
||||
const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK;
|
||||
|
||||
/// Returns `true` if this value is NaN.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `unordtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let nan = f128::NAN;
|
||||
/// let f = 7.0_f128;
|
||||
///
|
||||
/// assert!(nan.is_nan());
|
||||
/// assert!(!f.is_nan());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :)
|
||||
pub const fn is_nan(self) -> bool {
|
||||
self != self
|
||||
}
|
||||
|
||||
// FIXME(#50145): `abs` is publicly unavailable in core due to
|
||||
// concerns about portability, so this implementation is for
|
||||
// private use internally.
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub(crate) const fn abs_private(self) -> f128 {
|
||||
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
|
||||
unsafe {
|
||||
mem::transmute::<u128, f128>(mem::transmute::<f128, u128>(self) & !Self::SIGN_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this value is positive infinity or negative infinity, and
|
||||
/// `false` otherwise.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let f = 7.0f128;
|
||||
/// let inf = f128::INFINITY;
|
||||
/// let neg_inf = f128::NEG_INFINITY;
|
||||
/// let nan = f128::NAN;
|
||||
///
|
||||
/// assert!(!f.is_infinite());
|
||||
/// assert!(!nan.is_infinite());
|
||||
///
|
||||
/// assert!(inf.is_infinite());
|
||||
/// assert!(neg_inf.is_infinite());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub const fn is_infinite(self) -> bool {
|
||||
(self == f128::INFINITY) | (self == f128::NEG_INFINITY)
|
||||
}
|
||||
|
||||
/// Returns `true` if this number is neither infinite nor NaN.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `lttf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let f = 7.0f128;
|
||||
/// let inf: f128 = f128::INFINITY;
|
||||
/// let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
/// let nan: f128 = f128::NAN;
|
||||
///
|
||||
/// assert!(f.is_finite());
|
||||
///
|
||||
/// assert!(!nan.is_finite());
|
||||
/// assert!(!inf.is_finite());
|
||||
/// assert!(!neg_inf.is_finite());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub const fn is_finite(self) -> bool {
|
||||
// There's no need to handle NaN separately: if self is NaN,
|
||||
// the comparison is not true, exactly as desired.
|
||||
self.abs_private() < Self::INFINITY
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
|
||||
/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
|
||||
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
|
||||
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
|
||||
/// `is_sign_positive` on a NaN might produce an unexpected result in some cases.
|
||||
/// See [explanation of NaN as a special value](f32) for more info.
|
||||
/// See [explanation of NaN as a special value](f128) for more info.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
|
|
@ -257,7 +382,7 @@ impl f128 {
|
|||
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
|
||||
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
|
||||
/// `is_sign_negative` on a NaN might produce an unexpected result in some cases.
|
||||
/// See [explanation of NaN as a special value](f32) for more info.
|
||||
/// See [explanation of NaN as a special value](f128) for more info.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
|
|
@ -278,6 +403,222 @@ impl f128 {
|
|||
(self.to_bits() & (1 << 127)) != 0
|
||||
}
|
||||
|
||||
/// Returns the least number greater than `self`.
|
||||
///
|
||||
/// Let `TINY` be the smallest representable positive `f128`. Then,
|
||||
/// - if `self.is_nan()`, this returns `self`;
|
||||
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
|
||||
/// - if `self` is `-TINY`, this returns -0.0;
|
||||
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
|
||||
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
|
||||
/// - otherwise the unique least value greater than `self` is returned.
|
||||
///
|
||||
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
|
||||
/// is finite `x == x.next_up().next_down()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(f128)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// // f128::EPSILON is the difference between 1.0 and the next number up.
|
||||
/// assert_eq!(1.0f128.next_up(), 1.0 + f128::EPSILON);
|
||||
/// // But not for most numbers.
|
||||
/// assert!(0.1f128.next_up() < 0.1 + f128::EPSILON);
|
||||
/// assert_eq!(4611686018427387904f128.next_up(), 4611686018427387904.000000000000001);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub fn next_up(self) -> Self {
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
Self::TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits + 1
|
||||
} else {
|
||||
bits - 1
|
||||
};
|
||||
Self::from_bits(next_bits)
|
||||
}
|
||||
|
||||
/// Returns the greatest number less than `self`.
|
||||
///
|
||||
/// Let `TINY` be the smallest representable positive `f128`. Then,
|
||||
/// - if `self.is_nan()`, this returns `self`;
|
||||
/// - if `self` is [`INFINITY`], this returns [`MAX`];
|
||||
/// - if `self` is `TINY`, this returns 0.0;
|
||||
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
|
||||
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
|
||||
/// - otherwise the unique greatest value less than `self` is returned.
|
||||
///
|
||||
/// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
|
||||
/// is finite `x == x.next_down().next_up()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(f128)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let x = 1.0f128;
|
||||
/// // Clamp value into range [0, 1).
|
||||
/// let clamped = x.clamp(0.0, 1.0f128.next_down());
|
||||
/// assert!(clamped < 1.0);
|
||||
/// assert_eq!(clamped.next_up(), 1.0);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub fn next_down(self) -> Self {
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
Self::NEG_TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits - 1
|
||||
} else {
|
||||
bits + 1
|
||||
};
|
||||
Self::from_bits(next_bits)
|
||||
}
|
||||
|
||||
/// Takes the reciprocal (inverse) of a number, `1/x`.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let x = 2.0_f128;
|
||||
/// let abs_difference = (x.recip() - (1.0 / x)).abs();
|
||||
///
|
||||
/// assert!(abs_difference <= f128::EPSILON);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn recip(self) -> Self {
|
||||
1.0 / self
|
||||
}
|
||||
|
||||
/// Converts radians to degrees.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let angle = std::f128::consts::PI;
|
||||
///
|
||||
/// let abs_difference = (angle.to_degrees() - 180.0).abs();
|
||||
/// assert!(abs_difference <= f128::EPSILON);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_degrees(self) -> Self {
|
||||
// Use a literal for better precision.
|
||||
const PIS_IN_180: f128 = 57.2957795130823208767981548141051703324054724665643215491602_f128;
|
||||
self * PIS_IN_180
|
||||
}
|
||||
|
||||
/// Converts degrees to radians.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let angle = 180.0f128;
|
||||
///
|
||||
/// let abs_difference = (angle.to_radians() - std::f128::consts::PI).abs();
|
||||
///
|
||||
/// assert!(abs_difference <= 1e-30);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_radians(self) -> f128 {
|
||||
// Use a literal for better precision.
|
||||
const RADS_PER_DEG: f128 =
|
||||
0.0174532925199432957692369076848861271344287188854172545609719_f128;
|
||||
self * RADS_PER_DEG
|
||||
}
|
||||
|
||||
/// Rounds toward zero and converts to any primitive integer type,
|
||||
/// assuming that the value is finite and fits in that type.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `float*itf` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let value = 4.6_f128;
|
||||
/// let rounded = unsafe { value.to_int_unchecked::<u16>() };
|
||||
/// assert_eq!(rounded, 4);
|
||||
///
|
||||
/// let value = -128.9_f128;
|
||||
/// let rounded = unsafe { value.to_int_unchecked::<i8>() };
|
||||
/// assert_eq!(rounded, i8::MIN);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The value must:
|
||||
///
|
||||
/// * Not be `NaN`
|
||||
/// * Not be infinite
|
||||
/// * Be representable in the return type `Int`, after truncating off its fractional part
|
||||
#[inline]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub unsafe fn to_int_unchecked<Int>(self) -> Int
|
||||
where
|
||||
Self: FloatToInt<Int>,
|
||||
{
|
||||
// SAFETY: the caller must uphold the safety contract for
|
||||
// `FloatToInt::to_int_unchecked`.
|
||||
unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
|
||||
}
|
||||
|
||||
/// Raw transmutation to `u128`.
|
||||
///
|
||||
/// This is currently identical to `transmute::<f128, u128>(self)` on all platforms.
|
||||
|
|
@ -287,6 +628,14 @@ impl f128 {
|
|||
///
|
||||
/// Note that this function is distinct from `as` casting, which attempts to
|
||||
/// preserve the *numeric* value, and not the bitwise value.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
///
|
||||
/// # // FIXME(f16_f128): enable this once const casting works
|
||||
/// # // assert_ne!((1f128).to_bits(), 1f128 as u128); // to_bits() is not casting!
|
||||
/// assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
|
|
@ -326,6 +675,16 @@ impl f128 {
|
|||
///
|
||||
/// Note that this function is distinct from `as` casting, which attempts to
|
||||
/// preserve the *numeric* value, and not the bitwise value.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let v = f128::from_bits(0x40029000000000000000000000000000);
|
||||
/// assert_eq!(v, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
|
|
@ -335,4 +694,315 @@ impl f128 {
|
|||
// Stability concerns.
|
||||
unsafe { mem::transmute(v) }
|
||||
}
|
||||
|
||||
/// Return the memory representation of this floating point number as a byte array in
|
||||
/// big-endian (network) byte order.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
///
|
||||
/// let bytes = 12.5f128.to_be_bytes();
|
||||
/// assert_eq!(
|
||||
/// bytes,
|
||||
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_be_bytes(self) -> [u8; 16] {
|
||||
self.to_bits().to_be_bytes()
|
||||
}
|
||||
|
||||
/// Return the memory representation of this floating point number as a byte array in
|
||||
/// little-endian byte order.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
///
|
||||
/// let bytes = 12.5f128.to_le_bytes();
|
||||
/// assert_eq!(
|
||||
/// bytes,
|
||||
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_le_bytes(self) -> [u8; 16] {
|
||||
self.to_bits().to_le_bytes()
|
||||
}
|
||||
|
||||
/// Return the memory representation of this floating point number as a byte array in
|
||||
/// native byte order.
|
||||
///
|
||||
/// As the target platform's native endianness is used, portable code
|
||||
/// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
|
||||
///
|
||||
/// [`to_be_bytes`]: f128::to_be_bytes
|
||||
/// [`to_le_bytes`]: f128::to_le_bytes
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
///
|
||||
/// let bytes = 12.5f128.to_ne_bytes();
|
||||
/// assert_eq!(
|
||||
/// bytes,
|
||||
/// if cfg!(target_endian = "big") {
|
||||
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
/// } else {
|
||||
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_ne_bytes(self) -> [u8; 16] {
|
||||
self.to_bits().to_ne_bytes()
|
||||
}
|
||||
|
||||
/// Create a floating point value from its representation as a byte array in big endian.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let value = f128::from_be_bytes(
|
||||
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
/// );
|
||||
/// assert_eq!(value, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
|
||||
Self::from_bits(u128::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Create a floating point value from its representation as a byte array in little endian.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let value = f128::from_le_bytes(
|
||||
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
|
||||
/// );
|
||||
/// assert_eq!(value, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub fn from_le_bytes(bytes: [u8; 16]) -> Self {
|
||||
Self::from_bits(u128::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Create a floating point value from its representation as a byte array in native endian.
|
||||
///
|
||||
/// As the target platform's native endianness is used, portable code
|
||||
/// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
|
||||
/// appropriate instead.
|
||||
///
|
||||
/// [`from_be_bytes`]: f128::from_be_bytes
|
||||
/// [`from_le_bytes`]: f128::from_le_bytes
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let value = f128::from_ne_bytes(if cfg!(target_endian = "big") {
|
||||
/// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
/// } else {
|
||||
/// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40]
|
||||
/// });
|
||||
/// assert_eq!(value, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub fn from_ne_bytes(bytes: [u8; 16]) -> Self {
|
||||
Self::from_bits(u128::from_ne_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Return the ordering between `self` and `other`.
|
||||
///
|
||||
/// Unlike the standard partial comparison between floating point numbers,
|
||||
/// this comparison always produces an ordering in accordance to
|
||||
/// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision)
|
||||
/// floating point standard. The values are ordered in the following sequence:
|
||||
///
|
||||
/// - negative quiet NaN
|
||||
/// - negative signaling NaN
|
||||
/// - negative infinity
|
||||
/// - negative numbers
|
||||
/// - negative subnormal numbers
|
||||
/// - negative zero
|
||||
/// - positive zero
|
||||
/// - positive subnormal numbers
|
||||
/// - positive numbers
|
||||
/// - positive infinity
|
||||
/// - positive signaling NaN
|
||||
/// - positive quiet NaN.
|
||||
///
|
||||
/// The ordering established by this function does not always agree with the
|
||||
/// [`PartialOrd`] and [`PartialEq`] implementations of `f128`. For example,
|
||||
/// they consider negative and positive zero equal, while `total_cmp`
|
||||
/// doesn't.
|
||||
///
|
||||
/// The interpretation of the signaling NaN bit follows the definition in
|
||||
/// the IEEE 754 standard, which may not match the interpretation by some of
|
||||
/// the older, non-conformant (e.g. MIPS) hardware implementations.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
///
|
||||
/// struct GoodBoy {
|
||||
/// name: &'static str,
|
||||
/// weight: f128,
|
||||
/// }
|
||||
///
|
||||
/// let mut bois = vec![
|
||||
/// GoodBoy { name: "Pucci", weight: 0.1 },
|
||||
/// GoodBoy { name: "Woofer", weight: 99.0 },
|
||||
/// GoodBoy { name: "Yapper", weight: 10.0 },
|
||||
/// GoodBoy { name: "Chonk", weight: f128::INFINITY },
|
||||
/// GoodBoy { name: "Abs. Unit", weight: f128::NAN },
|
||||
/// GoodBoy { name: "Floaty", weight: -5.0 },
|
||||
/// ];
|
||||
///
|
||||
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
|
||||
///
|
||||
/// // `f128::NAN` could be positive or negative, which will affect the sort order.
|
||||
/// if f128::NAN.is_sign_negative() {
|
||||
/// bois.into_iter().map(|b| b.weight)
|
||||
/// .zip([f128::NAN, -5.0, 0.1, 10.0, 99.0, f128::INFINITY].iter())
|
||||
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
|
||||
/// } else {
|
||||
/// bois.into_iter().map(|b| b.weight)
|
||||
/// .zip([-5.0, 0.1, 10.0, 99.0, f128::INFINITY, f128::NAN].iter())
|
||||
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
|
||||
let mut left = self.to_bits() as i128;
|
||||
let mut right = other.to_bits() as i128;
|
||||
|
||||
// In case of negatives, flip all the bits except the sign
|
||||
// to achieve a similar layout as two's complement integers
|
||||
//
|
||||
// Why does this work? IEEE 754 floats consist of three fields:
|
||||
// Sign bit, exponent and mantissa. The set of exponent and mantissa
|
||||
// fields as a whole have the property that their bitwise order is
|
||||
// equal to the numeric magnitude where the magnitude is defined.
|
||||
// The magnitude is not normally defined on NaN values, but
|
||||
// IEEE 754 totalOrder defines the NaN values also to follow the
|
||||
// bitwise order. This leads to order explained in the doc comment.
|
||||
// However, the representation of magnitude is the same for negative
|
||||
// and positive numbers – only the sign bit is different.
|
||||
// To easily compare the floats as signed integers, we need to
|
||||
// flip the exponent and mantissa bits in case of negative numbers.
|
||||
// We effectively convert the numbers to "two's complement" form.
|
||||
//
|
||||
// To do the flipping, we construct a mask and XOR against it.
|
||||
// We branchlessly calculate an "all-ones except for the sign bit"
|
||||
// mask from negative-signed values: right shifting sign-extends
|
||||
// the integer, so we "fill" the mask with sign bits, and then
|
||||
// convert to unsigned to push one more zero bit.
|
||||
// On positive values, the mask is all zeros, so it's a no-op.
|
||||
left ^= (((left >> 127) as u128) >> 1) as i128;
|
||||
right ^= (((right >> 127) as u128) >> 1) as i128;
|
||||
|
||||
left.cmp(&right)
|
||||
}
|
||||
|
||||
/// Restrict a value to a certain interval unless it is NaN.
|
||||
///
|
||||
/// Returns `max` if `self` is greater than `max`, and `min` if `self` is
|
||||
/// less than `min`. Otherwise this returns `self`.
|
||||
///
|
||||
/// Note that this function returns NaN if the initial value was NaN as
|
||||
/// well.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `min > max`, `min` is NaN, or `max` is NaN.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # // FIXME(f16_f128): remove when `{eq,gt,unord}tf` are available
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0);
|
||||
/// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0);
|
||||
/// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0);
|
||||
/// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "method returns a new number and does not mutate the original value"]
|
||||
pub fn clamp(mut self, min: f128, max: f128) -> f128 {
|
||||
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
|
||||
if self < min {
|
||||
self = min;
|
||||
}
|
||||
if self > max {
|
||||
self = max;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#![unstable(feature = "f16", issue = "116909")]
|
||||
|
||||
use crate::convert::FloatToInt;
|
||||
use crate::mem;
|
||||
|
||||
/// Basic mathematical constants.
|
||||
|
|
@ -215,21 +216,140 @@ impl f16 {
|
|||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub const MAX_10_EXP: i32 = 4;
|
||||
|
||||
/// Not a Number (NaN).
|
||||
///
|
||||
/// Note that IEEE 754 doesn't define just a single NaN value;
|
||||
/// a plethora of bit patterns are considered to be NaN.
|
||||
/// Furthermore, the standard makes a difference
|
||||
/// between a "signaling" and a "quiet" NaN,
|
||||
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
|
||||
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
|
||||
/// and the stability of its representation over Rust versions
|
||||
/// and target platforms isn't guaranteed.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[allow(clippy::eq_op)]
|
||||
#[rustc_diagnostic_item = "f16_nan"]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub const NAN: f16 = 0.0_f16 / 0.0_f16;
|
||||
|
||||
/// Infinity (∞).
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub const INFINITY: f16 = 1.0_f16 / 0.0_f16;
|
||||
|
||||
/// Negative infinity (−∞).
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16;
|
||||
|
||||
/// Sign bit
|
||||
#[cfg(not(bootstrap))]
|
||||
const SIGN_MASK: u16 = 0x8000;
|
||||
|
||||
/// Minimum representable positive value (min subnormal)
|
||||
#[cfg(not(bootstrap))]
|
||||
const TINY_BITS: u16 = 0x1;
|
||||
|
||||
/// Minimum representable negative value (min negative subnormal)
|
||||
#[cfg(not(bootstrap))]
|
||||
const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK;
|
||||
|
||||
/// Returns `true` if this value is NaN.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let nan = f16::NAN;
|
||||
/// let f = 7.0_f16;
|
||||
///
|
||||
/// assert!(nan.is_nan());
|
||||
/// assert!(!f.is_nan());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :)
|
||||
pub const fn is_nan(self) -> bool {
|
||||
self != self
|
||||
}
|
||||
|
||||
// FIXMxE(#50145): `abs` is publicly unavailable in core due to
|
||||
// concerns about portability, so this implementation is for
|
||||
// private use internally.
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub(crate) const fn abs_private(self) -> f16 {
|
||||
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
|
||||
unsafe { mem::transmute::<u16, f16>(mem::transmute::<f16, u16>(self) & !Self::SIGN_MASK) }
|
||||
}
|
||||
|
||||
/// Returns `true` if this value is positive infinity or negative infinity, and
|
||||
/// `false` otherwise.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let f = 7.0f16;
|
||||
/// let inf = f16::INFINITY;
|
||||
/// let neg_inf = f16::NEG_INFINITY;
|
||||
/// let nan = f16::NAN;
|
||||
///
|
||||
/// assert!(!f.is_infinite());
|
||||
/// assert!(!nan.is_infinite());
|
||||
///
|
||||
/// assert!(inf.is_infinite());
|
||||
/// assert!(neg_inf.is_infinite());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub const fn is_infinite(self) -> bool {
|
||||
(self == f16::INFINITY) | (self == f16::NEG_INFINITY)
|
||||
}
|
||||
|
||||
/// Returns `true` if this number is neither infinite nor NaN.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let f = 7.0f16;
|
||||
/// let inf: f16 = f16::INFINITY;
|
||||
/// let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
/// let nan: f16 = f16::NAN;
|
||||
///
|
||||
/// assert!(f.is_finite());
|
||||
///
|
||||
/// assert!(!nan.is_finite());
|
||||
/// assert!(!inf.is_finite());
|
||||
/// assert!(!neg_inf.is_finite());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub const fn is_finite(self) -> bool {
|
||||
// There's no need to handle NaN separately: if self is NaN,
|
||||
// the comparison is not true, exactly as desired.
|
||||
self.abs_private() < Self::INFINITY
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
|
||||
/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
|
||||
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
|
||||
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
|
||||
/// `is_sign_positive` on a NaN might produce an unexpected result in some cases.
|
||||
/// See [explanation of NaN as a special value](f32) for more info.
|
||||
/// See [explanation of NaN as a special value](f16) for more info.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
|
|
@ -252,7 +372,7 @@ impl f16 {
|
|||
/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
|
||||
/// the bit pattern of NaNs are conserved over arithmetic operations, the result of
|
||||
/// `is_sign_negative` on a NaN might produce an unexpected result in some cases.
|
||||
/// See [explanation of NaN as a special value](f32) for more info.
|
||||
/// See [explanation of NaN as a special value](f16) for more info.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
|
|
@ -273,6 +393,220 @@ impl f16 {
|
|||
(self.to_bits() & (1 << 15)) != 0
|
||||
}
|
||||
|
||||
/// Returns the least number greater than `self`.
|
||||
///
|
||||
/// Let `TINY` be the smallest representable positive `f16`. Then,
|
||||
/// - if `self.is_nan()`, this returns `self`;
|
||||
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
|
||||
/// - if `self` is `-TINY`, this returns -0.0;
|
||||
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
|
||||
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
|
||||
/// - otherwise the unique least value greater than `self` is returned.
|
||||
///
|
||||
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
|
||||
/// is finite `x == x.next_up().next_down()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(f16)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): ABI issues on MSVC
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// // f16::EPSILON is the difference between 1.0 and the next number up.
|
||||
/// assert_eq!(1.0f16.next_up(), 1.0 + f16::EPSILON);
|
||||
/// // But not for most numbers.
|
||||
/// assert!(0.1f16.next_up() < 0.1 + f16::EPSILON);
|
||||
/// assert_eq!(4356f16.next_up(), 4360.0);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub fn next_up(self) -> Self {
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
Self::TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits + 1
|
||||
} else {
|
||||
bits - 1
|
||||
};
|
||||
Self::from_bits(next_bits)
|
||||
}
|
||||
|
||||
/// Returns the greatest number less than `self`.
|
||||
///
|
||||
/// Let `TINY` be the smallest representable positive `f16`. Then,
|
||||
/// - if `self.is_nan()`, this returns `self`;
|
||||
/// - if `self` is [`INFINITY`], this returns [`MAX`];
|
||||
/// - if `self` is `TINY`, this returns 0.0;
|
||||
/// - if `self` is -0.0 or +0.0, this returns `-TINY`;
|
||||
/// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`];
|
||||
/// - otherwise the unique greatest value less than `self` is returned.
|
||||
///
|
||||
/// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x`
|
||||
/// is finite `x == x.next_down().next_up()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(f16)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): ABI issues on MSVC
|
||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||
///
|
||||
/// let x = 1.0f16;
|
||||
/// // Clamp value into range [0, 1).
|
||||
/// let clamped = x.clamp(0.0, 1.0f16.next_down());
|
||||
/// assert!(clamped < 1.0);
|
||||
/// assert_eq!(clamped.next_up(), 1.0);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub fn next_down(self) -> Self {
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
Self::NEG_TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits - 1
|
||||
} else {
|
||||
bits + 1
|
||||
};
|
||||
Self::from_bits(next_bits)
|
||||
}
|
||||
|
||||
/// Takes the reciprocal (inverse) of a number, `1/x`.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
|
||||
/// # #[cfg(target_os = "linux")] {
|
||||
///
|
||||
/// let x = 2.0_f16;
|
||||
/// let abs_difference = (x.recip() - (1.0 / x)).abs();
|
||||
///
|
||||
/// assert!(abs_difference <= f16::EPSILON);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn recip(self) -> Self {
|
||||
1.0 / self
|
||||
}
|
||||
|
||||
/// Converts radians to degrees.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
|
||||
/// # #[cfg(target_os = "linux")] {
|
||||
///
|
||||
/// let angle = std::f16::consts::PI;
|
||||
///
|
||||
/// let abs_difference = (angle.to_degrees() - 180.0).abs();
|
||||
/// assert!(abs_difference <= 0.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_degrees(self) -> Self {
|
||||
// Use a literal for better precision.
|
||||
const PIS_IN_180: f16 = 57.2957795130823208767981548141051703_f16;
|
||||
self * PIS_IN_180
|
||||
}
|
||||
|
||||
/// Converts degrees to radians.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available
|
||||
/// # #[cfg(target_os = "linux")] {
|
||||
///
|
||||
/// let angle = 180.0f16;
|
||||
///
|
||||
/// let abs_difference = (angle.to_radians() - std::f16::consts::PI).abs();
|
||||
///
|
||||
/// assert!(abs_difference <= 0.01);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_radians(self) -> f16 {
|
||||
// Use a literal for better precision.
|
||||
const RADS_PER_DEG: f16 = 0.017453292519943295769236907684886_f16;
|
||||
self * RADS_PER_DEG
|
||||
}
|
||||
|
||||
/// Rounds toward zero and converts to any primitive integer type,
|
||||
/// assuming that the value is finite and fits in that type.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let value = 4.6_f16;
|
||||
/// let rounded = unsafe { value.to_int_unchecked::<u16>() };
|
||||
/// assert_eq!(rounded, 4);
|
||||
///
|
||||
/// let value = -128.9_f16;
|
||||
/// let rounded = unsafe { value.to_int_unchecked::<i8>() };
|
||||
/// assert_eq!(rounded, i8::MIN);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The value must:
|
||||
///
|
||||
/// * Not be `NaN`
|
||||
/// * Not be infinite
|
||||
/// * Be representable in the return type `Int`, after truncating off its fractional part
|
||||
#[inline]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub unsafe fn to_int_unchecked<Int>(self) -> Int
|
||||
where
|
||||
Self: FloatToInt<Int>,
|
||||
{
|
||||
// SAFETY: the caller must uphold the safety contract for
|
||||
// `FloatToInt::to_int_unchecked`.
|
||||
unsafe { FloatToInt::<Int>::to_int_unchecked(self) }
|
||||
}
|
||||
|
||||
/// Raw transmutation to `u16`.
|
||||
///
|
||||
/// This is currently identical to `transmute::<f16, u16>(self)` on all platforms.
|
||||
|
|
@ -282,6 +616,16 @@ impl f16 {
|
|||
///
|
||||
/// Note that this function is distinct from `as` casting, which attempts to
|
||||
/// preserve the *numeric* value, and not the bitwise value.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// # // FIXME(f16_f128): enable this once const casting works
|
||||
/// # // assert_ne!((1f16).to_bits(), 1f16 as u128); // to_bits() is not casting!
|
||||
/// assert_eq!((12.5f16).to_bits(), 0x4a40);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
|
|
@ -321,6 +665,15 @@ impl f16 {
|
|||
///
|
||||
/// Note that this function is distinct from `as` casting, which attempts to
|
||||
/// preserve the *numeric* value, and not the bitwise value.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let v = f16::from_bits(0x4a40);
|
||||
/// assert_eq!(v, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
|
|
@ -330,4 +683,293 @@ impl f16 {
|
|||
// Stability concerns.
|
||||
unsafe { mem::transmute(v) }
|
||||
}
|
||||
|
||||
/// Return the memory representation of this floating point number as a byte array in
|
||||
/// big-endian (network) byte order.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
///
|
||||
/// let bytes = 12.5f16.to_be_bytes();
|
||||
/// assert_eq!(bytes, [0x4a, 0x40]);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_be_bytes(self) -> [u8; 2] {
|
||||
self.to_bits().to_be_bytes()
|
||||
}
|
||||
|
||||
/// Return the memory representation of this floating point number as a byte array in
|
||||
/// little-endian byte order.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
///
|
||||
/// let bytes = 12.5f16.to_le_bytes();
|
||||
/// assert_eq!(bytes, [0x40, 0x4a]);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_le_bytes(self) -> [u8; 2] {
|
||||
self.to_bits().to_le_bytes()
|
||||
}
|
||||
|
||||
/// Return the memory representation of this floating point number as a byte array in
|
||||
/// native byte order.
|
||||
///
|
||||
/// As the target platform's native endianness is used, portable code
|
||||
/// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
|
||||
///
|
||||
/// [`to_be_bytes`]: f16::to_be_bytes
|
||||
/// [`to_le_bytes`]: f16::to_le_bytes
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
///
|
||||
/// let bytes = 12.5f16.to_ne_bytes();
|
||||
/// assert_eq!(
|
||||
/// bytes,
|
||||
/// if cfg!(target_endian = "big") {
|
||||
/// [0x4a, 0x40]
|
||||
/// } else {
|
||||
/// [0x40, 0x4a]
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "this returns the result of the operation, without modifying the original"]
|
||||
pub fn to_ne_bytes(self) -> [u8; 2] {
|
||||
self.to_bits().to_ne_bytes()
|
||||
}
|
||||
|
||||
/// Create a floating point value from its representation as a byte array in big endian.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let value = f16::from_be_bytes([0x4a, 0x40]);
|
||||
/// assert_eq!(value, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
|
||||
Self::from_bits(u16::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Create a floating point value from its representation as a byte array in little endian.
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let value = f16::from_le_bytes([0x40, 0x4a]);
|
||||
/// assert_eq!(value, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub fn from_le_bytes(bytes: [u8; 2]) -> Self {
|
||||
Self::from_bits(u16::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Create a floating point value from its representation as a byte array in native endian.
|
||||
///
|
||||
/// As the target platform's native endianness is used, portable code
|
||||
/// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
|
||||
/// appropriate instead.
|
||||
///
|
||||
/// [`from_be_bytes`]: f16::from_be_bytes
|
||||
/// [`from_le_bytes`]: f16::from_le_bytes
|
||||
///
|
||||
/// See [`from_bits`](Self::from_bits) for some discussion of the
|
||||
/// portability of this operation (there are almost no issues).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") {
|
||||
/// [0x4a, 0x40]
|
||||
/// } else {
|
||||
/// [0x40, 0x4a]
|
||||
/// });
|
||||
/// assert_eq!(value, 12.5);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub fn from_ne_bytes(bytes: [u8; 2]) -> Self {
|
||||
Self::from_bits(u16::from_ne_bytes(bytes))
|
||||
}
|
||||
|
||||
/// Return the ordering between `self` and `other`.
|
||||
///
|
||||
/// Unlike the standard partial comparison between floating point numbers,
|
||||
/// this comparison always produces an ordering in accordance to
|
||||
/// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision)
|
||||
/// floating point standard. The values are ordered in the following sequence:
|
||||
///
|
||||
/// - negative quiet NaN
|
||||
/// - negative signaling NaN
|
||||
/// - negative infinity
|
||||
/// - negative numbers
|
||||
/// - negative subnormal numbers
|
||||
/// - negative zero
|
||||
/// - positive zero
|
||||
/// - positive subnormal numbers
|
||||
/// - positive numbers
|
||||
/// - positive infinity
|
||||
/// - positive signaling NaN
|
||||
/// - positive quiet NaN.
|
||||
///
|
||||
/// The ordering established by this function does not always agree with the
|
||||
/// [`PartialOrd`] and [`PartialEq`] implementations of `f16`. For example,
|
||||
/// they consider negative and positive zero equal, while `total_cmp`
|
||||
/// doesn't.
|
||||
///
|
||||
/// The interpretation of the signaling NaN bit follows the definition in
|
||||
/// the IEEE 754 standard, which may not match the interpretation by some of
|
||||
/// the older, non-conformant (e.g. MIPS) hardware implementations.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
///
|
||||
/// struct GoodBoy {
|
||||
/// name: &'static str,
|
||||
/// weight: f16,
|
||||
/// }
|
||||
///
|
||||
/// let mut bois = vec![
|
||||
/// GoodBoy { name: "Pucci", weight: 0.1 },
|
||||
/// GoodBoy { name: "Woofer", weight: 99.0 },
|
||||
/// GoodBoy { name: "Yapper", weight: 10.0 },
|
||||
/// GoodBoy { name: "Chonk", weight: f16::INFINITY },
|
||||
/// GoodBoy { name: "Abs. Unit", weight: f16::NAN },
|
||||
/// GoodBoy { name: "Floaty", weight: -5.0 },
|
||||
/// ];
|
||||
///
|
||||
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
|
||||
///
|
||||
/// // `f16::NAN` could be positive or negative, which will affect the sort order.
|
||||
/// if f16::NAN.is_sign_negative() {
|
||||
/// bois.into_iter().map(|b| b.weight)
|
||||
/// .zip([f16::NAN, -5.0, 0.1, 10.0, 99.0, f16::INFINITY].iter())
|
||||
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
|
||||
/// } else {
|
||||
/// bois.into_iter().map(|b| b.weight)
|
||||
/// .zip([-5.0, 0.1, 10.0, 99.0, f16::INFINITY, f16::NAN].iter())
|
||||
/// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits()))
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
|
||||
let mut left = self.to_bits() as i16;
|
||||
let mut right = other.to_bits() as i16;
|
||||
|
||||
// In case of negatives, flip all the bits except the sign
|
||||
// to achieve a similar layout as two's complement integers
|
||||
//
|
||||
// Why does this work? IEEE 754 floats consist of three fields:
|
||||
// Sign bit, exponent and mantissa. The set of exponent and mantissa
|
||||
// fields as a whole have the property that their bitwise order is
|
||||
// equal to the numeric magnitude where the magnitude is defined.
|
||||
// The magnitude is not normally defined on NaN values, but
|
||||
// IEEE 754 totalOrder defines the NaN values also to follow the
|
||||
// bitwise order. This leads to order explained in the doc comment.
|
||||
// However, the representation of magnitude is the same for negative
|
||||
// and positive numbers – only the sign bit is different.
|
||||
// To easily compare the floats as signed integers, we need to
|
||||
// flip the exponent and mantissa bits in case of negative numbers.
|
||||
// We effectively convert the numbers to "two's complement" form.
|
||||
//
|
||||
// To do the flipping, we construct a mask and XOR against it.
|
||||
// We branchlessly calculate an "all-ones except for the sign bit"
|
||||
// mask from negative-signed values: right shifting sign-extends
|
||||
// the integer, so we "fill" the mask with sign bits, and then
|
||||
// convert to unsigned to push one more zero bit.
|
||||
// On positive values, the mask is all zeros, so it's a no-op.
|
||||
left ^= (((left >> 15) as u16) >> 1) as i16;
|
||||
right ^= (((right >> 15) as u16) >> 1) as i16;
|
||||
|
||||
left.cmp(&right)
|
||||
}
|
||||
|
||||
/// Restrict a value to a certain interval unless it is NaN.
|
||||
///
|
||||
/// Returns `max` if `self` is greater than `max`, and `min` if `self` is
|
||||
/// less than `min`. Otherwise this returns `self`.
|
||||
///
|
||||
/// Note that this function returns NaN if the initial value was NaN as
|
||||
/// well.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `min > max`, `min` is NaN, or `max` is NaN.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
|
||||
///
|
||||
/// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0);
|
||||
/// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0);
|
||||
/// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0);
|
||||
/// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "method returns a new number and does not mutate the original value"]
|
||||
pub fn clamp(mut self, min: f16, max: f16) -> f16 {
|
||||
assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}");
|
||||
if self < min {
|
||||
self = min;
|
||||
}
|
||||
if self > max {
|
||||
self = max;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -490,6 +490,21 @@ impl f32 {
|
|||
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
|
||||
pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;
|
||||
|
||||
/// Sign bit
|
||||
const SIGN_MASK: u32 = 0x8000_0000;
|
||||
|
||||
/// Exponent mask
|
||||
const EXP_MASK: u32 = 0x7f80_0000;
|
||||
|
||||
/// Mantissa mask
|
||||
const MAN_MASK: u32 = 0x007f_ffff;
|
||||
|
||||
/// Minimum representable positive value (min subnormal)
|
||||
const TINY_BITS: u32 = 0x1;
|
||||
|
||||
/// Minimum representable negative value (min negative subnormal)
|
||||
const NEG_TINY_BITS: u32 = Self::TINY_BITS | Self::SIGN_MASK;
|
||||
|
||||
/// Returns `true` if this value is NaN.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -515,7 +530,7 @@ impl f32 {
|
|||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub(crate) const fn abs_private(self) -> f32 {
|
||||
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
|
||||
unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & 0x7fff_ffff) }
|
||||
unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
|
||||
}
|
||||
|
||||
/// Returns `true` if this value is positive infinity or negative infinity, and
|
||||
|
|
@ -682,12 +697,9 @@ impl f32 {
|
|||
// runtime-deviating logic which may or may not be acceptable.
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
const unsafe fn partial_classify(self) -> FpCategory {
|
||||
const EXP_MASK: u32 = 0x7f800000;
|
||||
const MAN_MASK: u32 = 0x007fffff;
|
||||
|
||||
// SAFETY: The caller is not asking questions for which this will tell lies.
|
||||
let b = unsafe { mem::transmute::<f32, u32>(self) };
|
||||
match (b & MAN_MASK, b & EXP_MASK) {
|
||||
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
|
||||
(0, 0) => FpCategory::Zero,
|
||||
(_, 0) => FpCategory::Subnormal,
|
||||
_ => FpCategory::Normal,
|
||||
|
|
@ -699,12 +711,9 @@ impl f32 {
|
|||
// plus a transmute. We do not live in a just world, but we can make it more so.
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
const fn classify_bits(b: u32) -> FpCategory {
|
||||
const EXP_MASK: u32 = 0x7f800000;
|
||||
const MAN_MASK: u32 = 0x007fffff;
|
||||
|
||||
match (b & MAN_MASK, b & EXP_MASK) {
|
||||
(0, EXP_MASK) => FpCategory::Infinite,
|
||||
(_, EXP_MASK) => FpCategory::Nan,
|
||||
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
|
||||
(0, Self::EXP_MASK) => FpCategory::Infinite,
|
||||
(_, Self::EXP_MASK) => FpCategory::Nan,
|
||||
(0, 0) => FpCategory::Zero,
|
||||
(_, 0) => FpCategory::Subnormal,
|
||||
_ => FpCategory::Normal,
|
||||
|
|
@ -787,19 +796,17 @@ impl f32 {
|
|||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_up(self) -> Self {
|
||||
// We must use strictly integer arithmetic to prevent denormals from
|
||||
// flushing to zero after an arithmetic operation on some platforms.
|
||||
const TINY_BITS: u32 = 0x1; // Smallest positive f32.
|
||||
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
|
||||
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & CLEAR_SIGN_MASK;
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
TINY_BITS
|
||||
Self::TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits + 1
|
||||
} else {
|
||||
|
|
@ -837,19 +844,17 @@ impl f32 {
|
|||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_down(self) -> Self {
|
||||
// We must use strictly integer arithmetic to prevent denormals from
|
||||
// flushing to zero after an arithmetic operation on some platforms.
|
||||
const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32.
|
||||
const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
|
||||
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & CLEAR_SIGN_MASK;
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
NEG_TINY_BITS
|
||||
Self::NEG_TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits - 1
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -489,6 +489,21 @@ impl f64 {
|
|||
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
|
||||
pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64;
|
||||
|
||||
/// Sign bit
|
||||
const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
|
||||
|
||||
/// Exponent mask
|
||||
const EXP_MASK: u64 = 0x7ff0_0000_0000_0000;
|
||||
|
||||
/// Mantissa mask
|
||||
const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff;
|
||||
|
||||
/// Minimum representable positive value (min subnormal)
|
||||
const TINY_BITS: u64 = 0x1;
|
||||
|
||||
/// Minimum representable negative value (min negative subnormal)
|
||||
const NEG_TINY_BITS: u64 = Self::TINY_BITS | Self::SIGN_MASK;
|
||||
|
||||
/// Returns `true` if this value is NaN.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -514,9 +529,7 @@ impl f64 {
|
|||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
pub(crate) const fn abs_private(self) -> f64 {
|
||||
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
|
||||
unsafe {
|
||||
mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & 0x7fff_ffff_ffff_ffff)
|
||||
}
|
||||
unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
|
||||
}
|
||||
|
||||
/// Returns `true` if this value is positive infinity or negative infinity, and
|
||||
|
|
@ -673,13 +686,10 @@ impl f64 {
|
|||
// and some normal floating point numbers truncated from an x87 FPU.
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
const unsafe fn partial_classify(self) -> FpCategory {
|
||||
const EXP_MASK: u64 = 0x7ff0000000000000;
|
||||
const MAN_MASK: u64 = 0x000fffffffffffff;
|
||||
|
||||
// SAFETY: The caller is not asking questions for which this will tell lies.
|
||||
let b = unsafe { mem::transmute::<f64, u64>(self) };
|
||||
match (b & MAN_MASK, b & EXP_MASK) {
|
||||
(0, EXP_MASK) => FpCategory::Infinite,
|
||||
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
|
||||
(0, Self::EXP_MASK) => FpCategory::Infinite,
|
||||
(0, 0) => FpCategory::Zero,
|
||||
(_, 0) => FpCategory::Subnormal,
|
||||
_ => FpCategory::Normal,
|
||||
|
|
@ -691,12 +701,9 @@ impl f64 {
|
|||
// plus a transmute. We do not live in a just world, but we can make it more so.
|
||||
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
|
||||
const fn classify_bits(b: u64) -> FpCategory {
|
||||
const EXP_MASK: u64 = 0x7ff0000000000000;
|
||||
const MAN_MASK: u64 = 0x000fffffffffffff;
|
||||
|
||||
match (b & MAN_MASK, b & EXP_MASK) {
|
||||
(0, EXP_MASK) => FpCategory::Infinite,
|
||||
(_, EXP_MASK) => FpCategory::Nan,
|
||||
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
|
||||
(0, Self::EXP_MASK) => FpCategory::Infinite,
|
||||
(_, Self::EXP_MASK) => FpCategory::Nan,
|
||||
(0, 0) => FpCategory::Zero,
|
||||
(_, 0) => FpCategory::Subnormal,
|
||||
_ => FpCategory::Normal,
|
||||
|
|
@ -756,7 +763,7 @@ impl f64 {
|
|||
// IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
|
||||
// applies to zeros and NaNs as well.
|
||||
// SAFETY: This is just transmuting to get the sign bit, it's fine.
|
||||
unsafe { mem::transmute::<f64, u64>(self) & 0x8000_0000_0000_0000 != 0 }
|
||||
unsafe { mem::transmute::<f64, u64>(self) & Self::SIGN_MASK != 0 }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -797,19 +804,17 @@ impl f64 {
|
|||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_up(self) -> Self {
|
||||
// We must use strictly integer arithmetic to prevent denormals from
|
||||
// flushing to zero after an arithmetic operation on some platforms.
|
||||
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
|
||||
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
|
||||
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & CLEAR_SIGN_MASK;
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
TINY_BITS
|
||||
Self::TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits + 1
|
||||
} else {
|
||||
|
|
@ -847,19 +852,17 @@ impl f64 {
|
|||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_down(self) -> Self {
|
||||
// We must use strictly integer arithmetic to prevent denormals from
|
||||
// flushing to zero after an arithmetic operation on some platforms.
|
||||
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
|
||||
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
|
||||
|
||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||
// denormals to zero. This is in general unsound and unsupported, but here
|
||||
// we do our best to still produce the correct result on such targets.
|
||||
let bits = self.to_bits();
|
||||
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let abs = bits & CLEAR_SIGN_MASK;
|
||||
let abs = bits & !Self::SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
NEG_TINY_BITS
|
||||
Self::NEG_TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits - 1
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use core::iter::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn test_iterator_filter_count() {
|
||||
|
|
@ -50,3 +51,15 @@ fn test_double_ended_filter() {
|
|||
assert_eq!(it.next().unwrap(), &2);
|
||||
assert_eq!(it.next_back(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_chunk_does_not_leak() {
|
||||
let drop_witness: [_; 5] = std::array::from_fn(|_| Rc::new(()));
|
||||
|
||||
let v = (0..5).map(|i| drop_witness[i].clone()).collect::<Vec<_>>();
|
||||
let _ = v.into_iter().filter(|_| false).next_chunk::<1>();
|
||||
|
||||
for ref w in drop_witness {
|
||||
assert_eq!(Rc::strong_count(w), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ fn main() {
|
|||
let target_vendor =
|
||||
env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set");
|
||||
let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set");
|
||||
let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
|
||||
.expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set")
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rustc-check-cfg=cfg(netbsd10)");
|
||||
if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() {
|
||||
|
|
@ -70,4 +74,62 @@ fn main() {
|
|||
println!("cargo:rustc-cfg=backtrace_in_libstd");
|
||||
|
||||
println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap());
|
||||
|
||||
// Emit these on platforms that have no known ABI bugs, LLVM selection bugs, lowering bugs,
|
||||
// missing symbols, or other problems, to determine when tests get run.
|
||||
// If more broken platforms are found, please update the tracking issue at
|
||||
// <https://github.com/rust-lang/rust/issues/116909>
|
||||
//
|
||||
// Some of these match arms are redundant; the goal is to separate reasons that the type is
|
||||
// unreliable, even when multiple reasons might fail the same platform.
|
||||
println!("cargo:rustc-check-cfg=cfg(reliable_f16)");
|
||||
println!("cargo:rustc-check-cfg=cfg(reliable_f128)");
|
||||
|
||||
let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) {
|
||||
// Selection failure until recent LLVM <https://github.com/llvm/llvm-project/issues/93894>
|
||||
// FIXME(llvm19): can probably be removed at the version bump
|
||||
("loongarch64", _) => false,
|
||||
// Selection failure <https://github.com/llvm/llvm-project/issues/50374>
|
||||
("s390x", _) => false,
|
||||
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
|
||||
("arm64ec", _) => false,
|
||||
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
|
||||
("x86", "windows") => false,
|
||||
// x86 has ABI bugs that show up with optimizations. This should be partially fixed with
|
||||
// the compiler-builtins update. <https://github.com/rust-lang/rust/issues/123885>
|
||||
("x86" | "x86_64", _) => false,
|
||||
// Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee`
|
||||
("powerpc" | "powerpc64" | "powerpc64le", _) => false,
|
||||
// Missing `__extendhfsf` and `__truncsfhf`
|
||||
("riscv32" | "riscv64", _) => false,
|
||||
// Most OSs are missing `__extendhfsf` and `__truncsfhf`
|
||||
(_, "linux" | "macos") => true,
|
||||
// Almost all OSs besides Linux and MacOS are missing symbols until compiler-builtins can
|
||||
// be updated. <https://github.com/rust-lang/rust/pull/125016> will get some of these, the
|
||||
// next CB update should get the rest.
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) {
|
||||
// Unsupported <https://github.com/llvm/llvm-project/issues/94434>
|
||||
("arm64ec", _) => false,
|
||||
// ABI and precision bugs <https://github.com/rust-lang/rust/issues/125109>
|
||||
// <https://github.com/rust-lang/rust/issues/125102>
|
||||
("powerpc" | "powerpc64", _) => false,
|
||||
// Selection bug <https://github.com/llvm/llvm-project/issues/95471>
|
||||
("nvptx64", _) => false,
|
||||
// ABI unsupported <https://github.com/llvm/llvm-project/issues/41838>
|
||||
("sparc", _) => false,
|
||||
// 64-bit Linux is about the only platform to have f128 symbols by default
|
||||
(_, "linux") if target_pointer_width == 64 => true,
|
||||
// Same as for f16, except MacOS is also missing f128 symbols.
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if has_reliable_f16 {
|
||||
println!("cargo:rustc-cfg=reliable_f16");
|
||||
}
|
||||
if has_reliable_f128 {
|
||||
println!("cargo:rustc-cfg=reliable_f128");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,34 @@ impl f128 {
|
|||
pub fn powi(self, n: i32) -> f128 {
|
||||
unsafe { intrinsics::powif128(self, n) }
|
||||
}
|
||||
|
||||
/// Computes the absolute value of `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f128)]
|
||||
/// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128
|
||||
///
|
||||
/// let x = 3.5_f128;
|
||||
/// let y = -3.5_f128;
|
||||
///
|
||||
/// assert_eq!(x.abs(), x);
|
||||
/// assert_eq!(y.abs(), -y);
|
||||
///
|
||||
/// assert!(f128::NAN.abs().is_nan());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[rustc_allow_incoherent_impl]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
#[must_use = "method returns a new number and does not mutate the original value"]
|
||||
pub fn abs(self) -> Self {
|
||||
// FIXME(f16_f128): replace with `intrinsics::fabsf128` when available
|
||||
// We don't do this now because LLVM has lowering bugs for f128 math.
|
||||
Self::from_bits(self.to_bits() & !(1 << 127))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,31 @@
|
|||
#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used
|
||||
#![cfg(not(bootstrap))]
|
||||
// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
|
||||
#![cfg(reliable_f128)]
|
||||
|
||||
use crate::f128::consts;
|
||||
use crate::num::*;
|
||||
|
||||
/// Smallest number
|
||||
const TINY_BITS: u128 = 0x1;
|
||||
|
||||
/// Next smallest number
|
||||
const TINY_UP_BITS: u128 = 0x2;
|
||||
|
||||
/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
|
||||
const MAX_DOWN_BITS: u128 = 0x7ffeffffffffffffffffffffffffffff;
|
||||
const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe;
|
||||
|
||||
/// Zeroed exponent, full significant
|
||||
const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff;
|
||||
|
||||
/// Exponent = 0b1, zeroed significand
|
||||
const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000;
|
||||
|
||||
/// First pattern over the mantissa
|
||||
const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa;
|
||||
|
||||
/// Second pattern over the mantissa
|
||||
const NAN_MASK2: u128 = 0x00005555555555555555555555555555;
|
||||
|
||||
/// Compare by value
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f128_eq {
|
||||
($a:expr, $b:expr) => {
|
||||
let (l, r): (&f128, &f128) = (&$a, &$b);
|
||||
assert_eq!(*l, *r, "\na: {:#0130x}\nb: {:#0130x}", l.to_bits(), r.to_bits())
|
||||
};
|
||||
}
|
||||
|
||||
/// Compare by representation
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f128_biteq {
|
||||
|
|
@ -31,10 +33,503 @@ macro_rules! assert_f128_biteq {
|
|||
let (l, r): (&f128, &f128) = (&$a, &$b);
|
||||
let lb = l.to_bits();
|
||||
let rb = r.to_bits();
|
||||
assert_eq!(
|
||||
lb, rb,
|
||||
"float {:?} is not bitequal to {:?}.\na: {:#0130x}\nb: {:#0130x}",
|
||||
*l, *r, lb, rb
|
||||
);
|
||||
assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_f128() {
|
||||
test_num(10f128, 2f128);
|
||||
}
|
||||
|
||||
// FIXME(f16_f128): add min and max tests when available
|
||||
|
||||
#[test]
|
||||
fn test_nan() {
|
||||
let nan: f128 = f128::NAN;
|
||||
assert!(nan.is_nan());
|
||||
assert!(!nan.is_infinite());
|
||||
assert!(!nan.is_finite());
|
||||
assert!(nan.is_sign_positive());
|
||||
assert!(!nan.is_sign_negative());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!nan.is_normal());
|
||||
// assert_eq!(Fp::Nan, nan.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_infinity() {
|
||||
let inf: f128 = f128::INFINITY;
|
||||
assert!(inf.is_infinite());
|
||||
assert!(!inf.is_finite());
|
||||
assert!(inf.is_sign_positive());
|
||||
assert!(!inf.is_sign_negative());
|
||||
assert!(!inf.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!inf.is_normal());
|
||||
// assert_eq!(Fp::Infinite, inf.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_infinity() {
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert!(neg_inf.is_infinite());
|
||||
assert!(!neg_inf.is_finite());
|
||||
assert!(!neg_inf.is_sign_positive());
|
||||
assert!(neg_inf.is_sign_negative());
|
||||
assert!(!neg_inf.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!neg_inf.is_normal());
|
||||
// assert_eq!(Fp::Infinite, neg_inf.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
let zero: f128 = 0.0f128;
|
||||
assert_eq!(0.0, zero);
|
||||
assert!(!zero.is_infinite());
|
||||
assert!(zero.is_finite());
|
||||
assert!(zero.is_sign_positive());
|
||||
assert!(!zero.is_sign_negative());
|
||||
assert!(!zero.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!zero.is_normal());
|
||||
// assert_eq!(Fp::Zero, zero.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_zero() {
|
||||
let neg_zero: f128 = -0.0;
|
||||
assert_eq!(0.0, neg_zero);
|
||||
assert!(!neg_zero.is_infinite());
|
||||
assert!(neg_zero.is_finite());
|
||||
assert!(!neg_zero.is_sign_positive());
|
||||
assert!(neg_zero.is_sign_negative());
|
||||
assert!(!neg_zero.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!neg_zero.is_normal());
|
||||
// assert_eq!(Fp::Zero, neg_zero.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one() {
|
||||
let one: f128 = 1.0f128;
|
||||
assert_eq!(1.0, one);
|
||||
assert!(!one.is_infinite());
|
||||
assert!(one.is_finite());
|
||||
assert!(one.is_sign_positive());
|
||||
assert!(!one.is_sign_negative());
|
||||
assert!(!one.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(one.is_normal());
|
||||
// assert_eq!(Fp::Normal, one.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_nan() {
|
||||
let nan: f128 = f128::NAN;
|
||||
let inf: f128 = f128::INFINITY;
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert!(nan.is_nan());
|
||||
assert!(!0.0f128.is_nan());
|
||||
assert!(!5.3f128.is_nan());
|
||||
assert!(!(-10.732f128).is_nan());
|
||||
assert!(!inf.is_nan());
|
||||
assert!(!neg_inf.is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_infinite() {
|
||||
let nan: f128 = f128::NAN;
|
||||
let inf: f128 = f128::INFINITY;
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert!(!nan.is_infinite());
|
||||
assert!(inf.is_infinite());
|
||||
assert!(neg_inf.is_infinite());
|
||||
assert!(!0.0f128.is_infinite());
|
||||
assert!(!42.8f128.is_infinite());
|
||||
assert!(!(-109.2f128).is_infinite());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_finite() {
|
||||
let nan: f128 = f128::NAN;
|
||||
let inf: f128 = f128::INFINITY;
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert!(!nan.is_finite());
|
||||
assert!(!inf.is_finite());
|
||||
assert!(!neg_inf.is_finite());
|
||||
assert!(0.0f128.is_finite());
|
||||
assert!(42.8f128.is_finite());
|
||||
assert!((-109.2f128).is_finite());
|
||||
}
|
||||
|
||||
// FIXME(f16_f128): add `test_is_normal` and `test_classify` when classify is working
|
||||
// FIXME(f16_f128): add missing math functions when available
|
||||
|
||||
#[test]
|
||||
fn test_abs() {
|
||||
assert_eq!(f128::INFINITY.abs(), f128::INFINITY);
|
||||
assert_eq!(1f128.abs(), 1f128);
|
||||
assert_eq!(0f128.abs(), 0f128);
|
||||
assert_eq!((-0f128).abs(), 0f128);
|
||||
assert_eq!((-1f128).abs(), 1f128);
|
||||
assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY);
|
||||
assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128);
|
||||
assert!(f128::NAN.abs().is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_sign_positive() {
|
||||
assert!(f128::INFINITY.is_sign_positive());
|
||||
assert!(1f128.is_sign_positive());
|
||||
assert!(0f128.is_sign_positive());
|
||||
assert!(!(-0f128).is_sign_positive());
|
||||
assert!(!(-1f128).is_sign_positive());
|
||||
assert!(!f128::NEG_INFINITY.is_sign_positive());
|
||||
assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive());
|
||||
assert!(f128::NAN.is_sign_positive());
|
||||
assert!(!(-f128::NAN).is_sign_positive());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_sign_negative() {
|
||||
assert!(!f128::INFINITY.is_sign_negative());
|
||||
assert!(!1f128.is_sign_negative());
|
||||
assert!(!0f128.is_sign_negative());
|
||||
assert!((-0f128).is_sign_negative());
|
||||
assert!((-1f128).is_sign_negative());
|
||||
assert!(f128::NEG_INFINITY.is_sign_negative());
|
||||
assert!((1f128 / f128::NEG_INFINITY).is_sign_negative());
|
||||
assert!(!f128::NAN.is_sign_negative());
|
||||
assert!((-f128::NAN).is_sign_negative());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_up() {
|
||||
let tiny = f128::from_bits(TINY_BITS);
|
||||
let tiny_up = f128::from_bits(TINY_UP_BITS);
|
||||
let max_down = f128::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN);
|
||||
assert_f128_biteq!(f128::MIN.next_up(), -max_down);
|
||||
assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0);
|
||||
assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal);
|
||||
assert_f128_biteq!((-tiny_up).next_up(), -tiny);
|
||||
assert_f128_biteq!((-tiny).next_up(), -0.0f128);
|
||||
assert_f128_biteq!((-0.0f128).next_up(), tiny);
|
||||
assert_f128_biteq!(0.0f128.next_up(), tiny);
|
||||
assert_f128_biteq!(tiny.next_up(), tiny_up);
|
||||
assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal);
|
||||
assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON);
|
||||
assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY);
|
||||
assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY);
|
||||
|
||||
// Check that NaNs roundtrip.
|
||||
let nan0 = f128::NAN;
|
||||
let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa);
|
||||
let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555);
|
||||
assert_f128_biteq!(nan0.next_up(), nan0);
|
||||
assert_f128_biteq!(nan1.next_up(), nan1);
|
||||
assert_f128_biteq!(nan2.next_up(), nan2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_down() {
|
||||
let tiny = f128::from_bits(TINY_BITS);
|
||||
let tiny_up = f128::from_bits(TINY_UP_BITS);
|
||||
let max_down = f128::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY);
|
||||
assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY);
|
||||
assert_f128_biteq!((-max_down).next_down(), f128::MIN);
|
||||
assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON);
|
||||
assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal);
|
||||
assert_f128_biteq!((-tiny).next_down(), -tiny_up);
|
||||
assert_f128_biteq!((-0.0f128).next_down(), -tiny);
|
||||
assert_f128_biteq!((0.0f128).next_down(), -tiny);
|
||||
assert_f128_biteq!(tiny.next_down(), 0.0f128);
|
||||
assert_f128_biteq!(tiny_up.next_down(), tiny);
|
||||
assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal);
|
||||
assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128);
|
||||
assert_f128_biteq!(f128::MAX.next_down(), max_down);
|
||||
assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX);
|
||||
|
||||
// Check that NaNs roundtrip.
|
||||
let nan0 = f128::NAN;
|
||||
let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa);
|
||||
let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555);
|
||||
assert_f128_biteq!(nan0.next_down(), nan0);
|
||||
assert_f128_biteq!(nan1.next_down(), nan1);
|
||||
assert_f128_biteq!(nan2.next_down(), nan2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recip() {
|
||||
let nan: f128 = f128::NAN;
|
||||
let inf: f128 = f128::INFINITY;
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert_eq!(1.0f128.recip(), 1.0);
|
||||
assert_eq!(2.0f128.recip(), 0.5);
|
||||
assert_eq!((-0.4f128).recip(), -2.5);
|
||||
assert_eq!(0.0f128.recip(), inf);
|
||||
assert!(nan.recip().is_nan());
|
||||
assert_eq!(inf.recip(), 0.0);
|
||||
assert_eq!(neg_inf.recip(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_degrees() {
|
||||
let pi: f128 = consts::PI;
|
||||
let nan: f128 = f128::NAN;
|
||||
let inf: f128 = f128::INFINITY;
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert_eq!(0.0f128.to_degrees(), 0.0);
|
||||
assert_approx_eq!((-5.8f128).to_degrees(), -332.315521);
|
||||
assert_eq!(pi.to_degrees(), 180.0);
|
||||
assert!(nan.to_degrees().is_nan());
|
||||
assert_eq!(inf.to_degrees(), inf);
|
||||
assert_eq!(neg_inf.to_degrees(), neg_inf);
|
||||
assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_radians() {
|
||||
let pi: f128 = consts::PI;
|
||||
let nan: f128 = f128::NAN;
|
||||
let inf: f128 = f128::INFINITY;
|
||||
let neg_inf: f128 = f128::NEG_INFINITY;
|
||||
assert_eq!(0.0f128.to_radians(), 0.0);
|
||||
assert_approx_eq!(154.6f128.to_radians(), 2.698279);
|
||||
assert_approx_eq!((-332.31f128).to_radians(), -5.799903);
|
||||
// check approx rather than exact because round trip for pi doesn't fall on an exactly
|
||||
// representable value (unlike `f32` and `f64`).
|
||||
assert_approx_eq!(180.0f128.to_radians(), pi);
|
||||
assert!(nan.to_radians().is_nan());
|
||||
assert_eq!(inf.to_radians(), inf);
|
||||
assert_eq!(neg_inf.to_radians(), neg_inf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_real_consts() {
|
||||
// FIXME(f16_f128): add math tests when available
|
||||
use super::consts;
|
||||
|
||||
let pi: f128 = consts::PI;
|
||||
let frac_pi_2: f128 = consts::FRAC_PI_2;
|
||||
let frac_pi_3: f128 = consts::FRAC_PI_3;
|
||||
let frac_pi_4: f128 = consts::FRAC_PI_4;
|
||||
let frac_pi_6: f128 = consts::FRAC_PI_6;
|
||||
let frac_pi_8: f128 = consts::FRAC_PI_8;
|
||||
let frac_1_pi: f128 = consts::FRAC_1_PI;
|
||||
let frac_2_pi: f128 = consts::FRAC_2_PI;
|
||||
// let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI;
|
||||
// let sqrt2: f128 = consts::SQRT_2;
|
||||
// let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2;
|
||||
// let e: f128 = consts::E;
|
||||
// let log2_e: f128 = consts::LOG2_E;
|
||||
// let log10_e: f128 = consts::LOG10_E;
|
||||
// let ln_2: f128 = consts::LN_2;
|
||||
// let ln_10: f128 = consts::LN_10;
|
||||
|
||||
assert_approx_eq!(frac_pi_2, pi / 2f128);
|
||||
assert_approx_eq!(frac_pi_3, pi / 3f128);
|
||||
assert_approx_eq!(frac_pi_4, pi / 4f128);
|
||||
assert_approx_eq!(frac_pi_6, pi / 6f128);
|
||||
assert_approx_eq!(frac_pi_8, pi / 8f128);
|
||||
assert_approx_eq!(frac_1_pi, 1f128 / pi);
|
||||
assert_approx_eq!(frac_2_pi, 2f128 / pi);
|
||||
// assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt());
|
||||
// assert_approx_eq!(sqrt2, 2f128.sqrt());
|
||||
// assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt());
|
||||
// assert_approx_eq!(log2_e, e.log2());
|
||||
// assert_approx_eq!(log10_e, e.log10());
|
||||
// assert_approx_eq!(ln_2, 2f128.ln());
|
||||
// assert_approx_eq!(ln_10, 10f128.ln());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_float_bits_conv() {
|
||||
assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000);
|
||||
assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000);
|
||||
assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000);
|
||||
assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000);
|
||||
assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0);
|
||||
assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5);
|
||||
assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0);
|
||||
assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25);
|
||||
|
||||
// Check that NaNs roundtrip their bits regardless of signaling-ness
|
||||
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
|
||||
let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1;
|
||||
let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2;
|
||||
assert!(f128::from_bits(masked_nan1).is_nan());
|
||||
assert!(f128::from_bits(masked_nan2).is_nan());
|
||||
|
||||
assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1);
|
||||
assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_clamp_min_greater_than_max() {
|
||||
let _ = 1.0f128.clamp(3.0, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_clamp_min_is_nan() {
|
||||
let _ = 1.0f128.clamp(f128::NAN, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_clamp_max_is_nan() {
|
||||
let _ = 1.0f128.clamp(3.0, f128::NAN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_cmp() {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
fn quiet_bit_mask() -> u128 {
|
||||
1 << (f128::MANTISSA_DIGITS - 2)
|
||||
}
|
||||
|
||||
// FIXME(f16_f128): test subnormals when powf is available
|
||||
// fn min_subnorm() -> f128 {
|
||||
// f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0)
|
||||
// }
|
||||
|
||||
// fn max_subnorm() -> f128 {
|
||||
// f128::MIN_POSITIVE - min_subnorm()
|
||||
// }
|
||||
|
||||
fn q_nan() -> f128 {
|
||||
f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask())
|
||||
}
|
||||
|
||||
fn s_nan() -> f128 {
|
||||
f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42)
|
||||
}
|
||||
|
||||
assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY));
|
||||
assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX));
|
||||
assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX));
|
||||
assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY));
|
||||
assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX));
|
||||
assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX));
|
||||
assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY));
|
||||
assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY));
|
||||
assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX));
|
||||
assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5));
|
||||
// assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0));
|
||||
// assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX));
|
||||
assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY));
|
||||
assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,33 @@ impl f16 {
|
|||
pub fn powi(self, n: i32) -> f16 {
|
||||
unsafe { intrinsics::powif16(self, n) }
|
||||
}
|
||||
|
||||
/// Computes the absolute value of `self`.
|
||||
///
|
||||
/// This function always returns the precise result.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(f16)]
|
||||
/// # #[cfg(reliable_f16)] {
|
||||
///
|
||||
/// let x = 3.5_f16;
|
||||
/// let y = -3.5_f16;
|
||||
///
|
||||
/// assert_eq!(x.abs(), x);
|
||||
/// assert_eq!(y.abs(), -y);
|
||||
///
|
||||
/// assert!(f16::NAN.abs().is_nan());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[rustc_allow_incoherent_impl]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
#[must_use = "method returns a new number and does not mutate the original value"]
|
||||
pub fn abs(self) -> Self {
|
||||
// FIXME(f16_f128): replace with `intrinsics::fabsf16` when available
|
||||
Self::from_bits(self.to_bits() & !(1 << 15))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,37 @@
|
|||
#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used
|
||||
#![cfg(not(bootstrap))]
|
||||
// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
|
||||
#![cfg(reliable_f16)]
|
||||
|
||||
use crate::f16::consts;
|
||||
use crate::num::*;
|
||||
|
||||
// We run out of precision pretty quickly with f16
|
||||
const F16_APPROX_L1: f16 = 0.001;
|
||||
// const F16_APPROX_L1: f16 = 0.001;
|
||||
const F16_APPROX_L2: f16 = 0.01;
|
||||
const F16_APPROX_L3: f16 = 0.1;
|
||||
// const F16_APPROX_L3: f16 = 0.1;
|
||||
const F16_APPROX_L4: f16 = 0.5;
|
||||
|
||||
/// Smallest number
|
||||
const TINY_BITS: u16 = 0x1;
|
||||
|
||||
/// Next smallest number
|
||||
const TINY_UP_BITS: u16 = 0x2;
|
||||
|
||||
/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
|
||||
const MAX_DOWN_BITS: u16 = 0x7bfe;
|
||||
|
||||
/// Zeroed exponent, full significant
|
||||
const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff;
|
||||
|
||||
/// Exponent = 0b1, zeroed significand
|
||||
const SMALLEST_NORMAL_BITS: u16 = 0x0400;
|
||||
|
||||
/// First pattern over the mantissa
|
||||
const NAN_MASK1: u16 = 0x02aa;
|
||||
|
||||
/// Second pattern over the mantissa
|
||||
const NAN_MASK2: u16 = 0x0155;
|
||||
|
||||
/// Compare by value
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f16_eq {
|
||||
($a:expr, $b:expr) => {
|
||||
let (l, r): (&f16, &f16) = (&$a, &$b);
|
||||
assert_eq!(*l, *r, "\na: {:#018x}\nb: {:#018x}", l.to_bits(), r.to_bits())
|
||||
};
|
||||
}
|
||||
|
||||
/// Compare by representation
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f16_biteq {
|
||||
|
|
@ -37,10 +39,500 @@ macro_rules! assert_f16_biteq {
|
|||
let (l, r): (&f16, &f16) = (&$a, &$b);
|
||||
let lb = l.to_bits();
|
||||
let rb = r.to_bits();
|
||||
assert_eq!(
|
||||
lb, rb,
|
||||
"float {:?} is not bitequal to {:?}.\na: {:#018x}\nb: {:#018x}",
|
||||
*l, *r, lb, rb
|
||||
);
|
||||
assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_f16() {
|
||||
test_num(10f16, 2f16);
|
||||
}
|
||||
|
||||
// FIXME(f16_f128): add min and max tests when available
|
||||
|
||||
#[test]
|
||||
fn test_nan() {
|
||||
let nan: f16 = f16::NAN;
|
||||
assert!(nan.is_nan());
|
||||
assert!(!nan.is_infinite());
|
||||
assert!(!nan.is_finite());
|
||||
assert!(nan.is_sign_positive());
|
||||
assert!(!nan.is_sign_negative());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!nan.is_normal());
|
||||
// assert_eq!(Fp::Nan, nan.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_infinity() {
|
||||
let inf: f16 = f16::INFINITY;
|
||||
assert!(inf.is_infinite());
|
||||
assert!(!inf.is_finite());
|
||||
assert!(inf.is_sign_positive());
|
||||
assert!(!inf.is_sign_negative());
|
||||
assert!(!inf.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!inf.is_normal());
|
||||
// assert_eq!(Fp::Infinite, inf.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_infinity() {
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert!(neg_inf.is_infinite());
|
||||
assert!(!neg_inf.is_finite());
|
||||
assert!(!neg_inf.is_sign_positive());
|
||||
assert!(neg_inf.is_sign_negative());
|
||||
assert!(!neg_inf.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!neg_inf.is_normal());
|
||||
// assert_eq!(Fp::Infinite, neg_inf.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
let zero: f16 = 0.0f16;
|
||||
assert_eq!(0.0, zero);
|
||||
assert!(!zero.is_infinite());
|
||||
assert!(zero.is_finite());
|
||||
assert!(zero.is_sign_positive());
|
||||
assert!(!zero.is_sign_negative());
|
||||
assert!(!zero.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!zero.is_normal());
|
||||
// assert_eq!(Fp::Zero, zero.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg_zero() {
|
||||
let neg_zero: f16 = -0.0;
|
||||
assert_eq!(0.0, neg_zero);
|
||||
assert!(!neg_zero.is_infinite());
|
||||
assert!(neg_zero.is_finite());
|
||||
assert!(!neg_zero.is_sign_positive());
|
||||
assert!(neg_zero.is_sign_negative());
|
||||
assert!(!neg_zero.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(!neg_zero.is_normal());
|
||||
// assert_eq!(Fp::Zero, neg_zero.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one() {
|
||||
let one: f16 = 1.0f16;
|
||||
assert_eq!(1.0, one);
|
||||
assert!(!one.is_infinite());
|
||||
assert!(one.is_finite());
|
||||
assert!(one.is_sign_positive());
|
||||
assert!(!one.is_sign_negative());
|
||||
assert!(!one.is_nan());
|
||||
// FIXME(f16_f128): classify
|
||||
// assert!(one.is_normal());
|
||||
// assert_eq!(Fp::Normal, one.classify());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_nan() {
|
||||
let nan: f16 = f16::NAN;
|
||||
let inf: f16 = f16::INFINITY;
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert!(nan.is_nan());
|
||||
assert!(!0.0f16.is_nan());
|
||||
assert!(!5.3f16.is_nan());
|
||||
assert!(!(-10.732f16).is_nan());
|
||||
assert!(!inf.is_nan());
|
||||
assert!(!neg_inf.is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_infinite() {
|
||||
let nan: f16 = f16::NAN;
|
||||
let inf: f16 = f16::INFINITY;
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert!(!nan.is_infinite());
|
||||
assert!(inf.is_infinite());
|
||||
assert!(neg_inf.is_infinite());
|
||||
assert!(!0.0f16.is_infinite());
|
||||
assert!(!42.8f16.is_infinite());
|
||||
assert!(!(-109.2f16).is_infinite());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_finite() {
|
||||
let nan: f16 = f16::NAN;
|
||||
let inf: f16 = f16::INFINITY;
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert!(!nan.is_finite());
|
||||
assert!(!inf.is_finite());
|
||||
assert!(!neg_inf.is_finite());
|
||||
assert!(0.0f16.is_finite());
|
||||
assert!(42.8f16.is_finite());
|
||||
assert!((-109.2f16).is_finite());
|
||||
}
|
||||
|
||||
// FIXME(f16_f128): add `test_is_normal` and `test_classify` when classify is working
|
||||
// FIXME(f16_f128): add missing math functions when available
|
||||
|
||||
#[test]
|
||||
fn test_abs() {
|
||||
assert_eq!(f16::INFINITY.abs(), f16::INFINITY);
|
||||
assert_eq!(1f16.abs(), 1f16);
|
||||
assert_eq!(0f16.abs(), 0f16);
|
||||
assert_eq!((-0f16).abs(), 0f16);
|
||||
assert_eq!((-1f16).abs(), 1f16);
|
||||
assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY);
|
||||
assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16);
|
||||
assert!(f16::NAN.abs().is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_sign_positive() {
|
||||
assert!(f16::INFINITY.is_sign_positive());
|
||||
assert!(1f16.is_sign_positive());
|
||||
assert!(0f16.is_sign_positive());
|
||||
assert!(!(-0f16).is_sign_positive());
|
||||
assert!(!(-1f16).is_sign_positive());
|
||||
assert!(!f16::NEG_INFINITY.is_sign_positive());
|
||||
assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive());
|
||||
assert!(f16::NAN.is_sign_positive());
|
||||
assert!(!(-f16::NAN).is_sign_positive());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_sign_negative() {
|
||||
assert!(!f16::INFINITY.is_sign_negative());
|
||||
assert!(!1f16.is_sign_negative());
|
||||
assert!(!0f16.is_sign_negative());
|
||||
assert!((-0f16).is_sign_negative());
|
||||
assert!((-1f16).is_sign_negative());
|
||||
assert!(f16::NEG_INFINITY.is_sign_negative());
|
||||
assert!((1f16 / f16::NEG_INFINITY).is_sign_negative());
|
||||
assert!(!f16::NAN.is_sign_negative());
|
||||
assert!((-f16::NAN).is_sign_negative());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_up() {
|
||||
let tiny = f16::from_bits(TINY_BITS);
|
||||
let tiny_up = f16::from_bits(TINY_UP_BITS);
|
||||
let max_down = f16::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN);
|
||||
assert_f16_biteq!(f16::MIN.next_up(), -max_down);
|
||||
assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0);
|
||||
assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal);
|
||||
assert_f16_biteq!((-tiny_up).next_up(), -tiny);
|
||||
assert_f16_biteq!((-tiny).next_up(), -0.0f16);
|
||||
assert_f16_biteq!((-0.0f16).next_up(), tiny);
|
||||
assert_f16_biteq!(0.0f16.next_up(), tiny);
|
||||
assert_f16_biteq!(tiny.next_up(), tiny_up);
|
||||
assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal);
|
||||
assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON);
|
||||
assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY);
|
||||
assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY);
|
||||
|
||||
// Check that NaNs roundtrip.
|
||||
let nan0 = f16::NAN;
|
||||
let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1);
|
||||
let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2);
|
||||
assert_f16_biteq!(nan0.next_up(), nan0);
|
||||
assert_f16_biteq!(nan1.next_up(), nan1);
|
||||
assert_f16_biteq!(nan2.next_up(), nan2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_down() {
|
||||
let tiny = f16::from_bits(TINY_BITS);
|
||||
let tiny_up = f16::from_bits(TINY_UP_BITS);
|
||||
let max_down = f16::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY);
|
||||
assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY);
|
||||
assert_f16_biteq!((-max_down).next_down(), f16::MIN);
|
||||
assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON);
|
||||
assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal);
|
||||
assert_f16_biteq!((-tiny).next_down(), -tiny_up);
|
||||
assert_f16_biteq!((-0.0f16).next_down(), -tiny);
|
||||
assert_f16_biteq!((0.0f16).next_down(), -tiny);
|
||||
assert_f16_biteq!(tiny.next_down(), 0.0f16);
|
||||
assert_f16_biteq!(tiny_up.next_down(), tiny);
|
||||
assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal);
|
||||
assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16);
|
||||
assert_f16_biteq!(f16::MAX.next_down(), max_down);
|
||||
assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX);
|
||||
|
||||
// Check that NaNs roundtrip.
|
||||
let nan0 = f16::NAN;
|
||||
let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1);
|
||||
let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2);
|
||||
assert_f16_biteq!(nan0.next_down(), nan0);
|
||||
assert_f16_biteq!(nan1.next_down(), nan1);
|
||||
assert_f16_biteq!(nan2.next_down(), nan2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recip() {
|
||||
let nan: f16 = f16::NAN;
|
||||
let inf: f16 = f16::INFINITY;
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert_eq!(1.0f16.recip(), 1.0);
|
||||
assert_eq!(2.0f16.recip(), 0.5);
|
||||
assert_eq!((-0.4f16).recip(), -2.5);
|
||||
assert_eq!(0.0f16.recip(), inf);
|
||||
assert!(nan.recip().is_nan());
|
||||
assert_eq!(inf.recip(), 0.0);
|
||||
assert_eq!(neg_inf.recip(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_degrees() {
|
||||
let pi: f16 = consts::PI;
|
||||
let nan: f16 = f16::NAN;
|
||||
let inf: f16 = f16::INFINITY;
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert_eq!(0.0f16.to_degrees(), 0.0);
|
||||
assert_approx_eq!((-5.8f16).to_degrees(), -332.315521);
|
||||
assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4);
|
||||
assert!(nan.to_degrees().is_nan());
|
||||
assert_eq!(inf.to_degrees(), inf);
|
||||
assert_eq!(neg_inf.to_degrees(), neg_inf);
|
||||
assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_radians() {
|
||||
let pi: f16 = consts::PI;
|
||||
let nan: f16 = f16::NAN;
|
||||
let inf: f16 = f16::INFINITY;
|
||||
let neg_inf: f16 = f16::NEG_INFINITY;
|
||||
assert_eq!(0.0f16.to_radians(), 0.0);
|
||||
assert_approx_eq!(154.6f16.to_radians(), 2.698279);
|
||||
assert_approx_eq!((-332.31f16).to_radians(), -5.799903);
|
||||
assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2);
|
||||
assert!(nan.to_radians().is_nan());
|
||||
assert_eq!(inf.to_radians(), inf);
|
||||
assert_eq!(neg_inf.to_radians(), neg_inf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_real_consts() {
|
||||
// FIXME(f16_f128): add math tests when available
|
||||
use super::consts;
|
||||
|
||||
let pi: f16 = consts::PI;
|
||||
let frac_pi_2: f16 = consts::FRAC_PI_2;
|
||||
let frac_pi_3: f16 = consts::FRAC_PI_3;
|
||||
let frac_pi_4: f16 = consts::FRAC_PI_4;
|
||||
let frac_pi_6: f16 = consts::FRAC_PI_6;
|
||||
let frac_pi_8: f16 = consts::FRAC_PI_8;
|
||||
let frac_1_pi: f16 = consts::FRAC_1_PI;
|
||||
let frac_2_pi: f16 = consts::FRAC_2_PI;
|
||||
// let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI;
|
||||
// let sqrt2: f16 = consts::SQRT_2;
|
||||
// let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2;
|
||||
// let e: f16 = consts::E;
|
||||
// let log2_e: f16 = consts::LOG2_E;
|
||||
// let log10_e: f16 = consts::LOG10_E;
|
||||
// let ln_2: f16 = consts::LN_2;
|
||||
// let ln_10: f16 = consts::LN_10;
|
||||
|
||||
assert_approx_eq!(frac_pi_2, pi / 2f16);
|
||||
assert_approx_eq!(frac_pi_3, pi / 3f16);
|
||||
assert_approx_eq!(frac_pi_4, pi / 4f16);
|
||||
assert_approx_eq!(frac_pi_6, pi / 6f16);
|
||||
assert_approx_eq!(frac_pi_8, pi / 8f16);
|
||||
assert_approx_eq!(frac_1_pi, 1f16 / pi);
|
||||
assert_approx_eq!(frac_2_pi, 2f16 / pi);
|
||||
// assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt());
|
||||
// assert_approx_eq!(sqrt2, 2f16.sqrt());
|
||||
// assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt());
|
||||
// assert_approx_eq!(log2_e, e.log2());
|
||||
// assert_approx_eq!(log10_e, e.log10());
|
||||
// assert_approx_eq!(ln_2, 2f16.ln());
|
||||
// assert_approx_eq!(ln_10, 10f16.ln());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_float_bits_conv() {
|
||||
assert_eq!((1f16).to_bits(), 0x3c00);
|
||||
assert_eq!((12.5f16).to_bits(), 0x4a40);
|
||||
assert_eq!((1337f16).to_bits(), 0x6539);
|
||||
assert_eq!((-14.25f16).to_bits(), 0xcb20);
|
||||
assert_approx_eq!(f16::from_bits(0x3c00), 1.0);
|
||||
assert_approx_eq!(f16::from_bits(0x4a40), 12.5);
|
||||
assert_approx_eq!(f16::from_bits(0x6539), 1337.0);
|
||||
assert_approx_eq!(f16::from_bits(0xcb20), -14.25);
|
||||
|
||||
// Check that NaNs roundtrip their bits regardless of signaling-ness
|
||||
let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1;
|
||||
let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2;
|
||||
assert!(f16::from_bits(masked_nan1).is_nan());
|
||||
assert!(f16::from_bits(masked_nan2).is_nan());
|
||||
|
||||
assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1);
|
||||
assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_clamp_min_greater_than_max() {
|
||||
let _ = 1.0f16.clamp(3.0, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_clamp_min_is_nan() {
|
||||
let _ = 1.0f16.clamp(f16::NAN, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_clamp_max_is_nan() {
|
||||
let _ = 1.0f16.clamp(3.0, f16::NAN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_cmp() {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
fn quiet_bit_mask() -> u16 {
|
||||
1 << (f16::MANTISSA_DIGITS - 2)
|
||||
}
|
||||
|
||||
// FIXME(f16_f128): test subnormals when powf is available
|
||||
// fn min_subnorm() -> f16 {
|
||||
// f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0)
|
||||
// }
|
||||
|
||||
// fn max_subnorm() -> f16 {
|
||||
// f16::MIN_POSITIVE - min_subnorm()
|
||||
// }
|
||||
|
||||
fn q_nan() -> f16 {
|
||||
f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask())
|
||||
}
|
||||
|
||||
fn s_nan() -> f16 {
|
||||
f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42)
|
||||
}
|
||||
|
||||
assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY));
|
||||
assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX));
|
||||
assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX));
|
||||
assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY));
|
||||
assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX));
|
||||
assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX));
|
||||
assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY));
|
||||
assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan()));
|
||||
assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
|
||||
|
||||
assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY));
|
||||
assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX));
|
||||
assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5));
|
||||
// assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0));
|
||||
// assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX));
|
||||
assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY));
|
||||
assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
|
||||
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
|
||||
// assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY));
|
||||
assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,45 @@ use crate::f32::consts;
|
|||
use crate::num::FpCategory as Fp;
|
||||
use crate::num::*;
|
||||
|
||||
/// Smallest number
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const TINY_BITS: u32 = 0x1;
|
||||
|
||||
/// Next smallest number
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const TINY_UP_BITS: u32 = 0x2;
|
||||
|
||||
/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const MAX_DOWN_BITS: u32 = 0x7f7f_fffe;
|
||||
|
||||
/// Zeroed exponent, full significant
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff;
|
||||
|
||||
/// Exponent = 0b1, zeroed significand
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000;
|
||||
|
||||
/// First pattern over the mantissa
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const NAN_MASK1: u32 = 0x002a_aaaa;
|
||||
|
||||
/// Second pattern over the mantissa
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const NAN_MASK2: u32 = 0x0055_5555;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f32_biteq {
|
||||
($left : expr, $right : expr) => {
|
||||
let l: &f32 = &$left;
|
||||
let r: &f32 = &$right;
|
||||
let lb = l.to_bits();
|
||||
let rb = r.to_bits();
|
||||
assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_f32() {
|
||||
test_num(10f32, 2f32);
|
||||
|
|
@ -315,27 +354,16 @@ fn test_is_sign_negative() {
|
|||
assert!((-f32::NAN).is_sign_negative());
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f32_biteq {
|
||||
($left : expr, $right : expr) => {
|
||||
let l: &f32 = &$left;
|
||||
let r: &f32 = &$right;
|
||||
let lb = l.to_bits();
|
||||
let rb = r.to_bits();
|
||||
assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
|
||||
};
|
||||
}
|
||||
|
||||
// Ignore test on x87 floating point, these platforms do not guarantee NaN
|
||||
// payloads are preserved and flush denormals to zero, failing the tests.
|
||||
#[cfg(not(target_arch = "x86"))]
|
||||
#[test]
|
||||
fn test_next_up() {
|
||||
let tiny = f32::from_bits(1);
|
||||
let tiny_up = f32::from_bits(2);
|
||||
let max_down = f32::from_bits(0x7f7f_fffe);
|
||||
let largest_subnormal = f32::from_bits(0x007f_ffff);
|
||||
let smallest_normal = f32::from_bits(0x0080_0000);
|
||||
let tiny = f32::from_bits(TINY_BITS);
|
||||
let tiny_up = f32::from_bits(TINY_UP_BITS);
|
||||
let max_down = f32::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
|
||||
assert_f32_biteq!(f32::MIN.next_up(), -max_down);
|
||||
assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);
|
||||
|
|
@ -352,8 +380,8 @@ fn test_next_up() {
|
|||
|
||||
// Check that NaNs roundtrip.
|
||||
let nan0 = f32::NAN;
|
||||
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
|
||||
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
|
||||
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
|
||||
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
|
||||
assert_f32_biteq!(nan0.next_up(), nan0);
|
||||
assert_f32_biteq!(nan1.next_up(), nan1);
|
||||
assert_f32_biteq!(nan2.next_up(), nan2);
|
||||
|
|
@ -364,11 +392,11 @@ fn test_next_up() {
|
|||
#[cfg(not(target_arch = "x86"))]
|
||||
#[test]
|
||||
fn test_next_down() {
|
||||
let tiny = f32::from_bits(1);
|
||||
let tiny_up = f32::from_bits(2);
|
||||
let max_down = f32::from_bits(0x7f7f_fffe);
|
||||
let largest_subnormal = f32::from_bits(0x007f_ffff);
|
||||
let smallest_normal = f32::from_bits(0x0080_0000);
|
||||
let tiny = f32::from_bits(TINY_BITS);
|
||||
let tiny_up = f32::from_bits(TINY_UP_BITS);
|
||||
let max_down = f32::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY);
|
||||
assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY);
|
||||
assert_f32_biteq!((-max_down).next_down(), f32::MIN);
|
||||
|
|
@ -386,8 +414,8 @@ fn test_next_down() {
|
|||
|
||||
// Check that NaNs roundtrip.
|
||||
let nan0 = f32::NAN;
|
||||
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa);
|
||||
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555);
|
||||
let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1);
|
||||
let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2);
|
||||
assert_f32_biteq!(nan0.next_down(), nan0);
|
||||
assert_f32_biteq!(nan1.next_down(), nan1);
|
||||
assert_f32_biteq!(nan2.next_down(), nan2);
|
||||
|
|
@ -734,8 +762,8 @@ fn test_float_bits_conv() {
|
|||
|
||||
// Check that NaNs roundtrip their bits regardless of signaling-ness
|
||||
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
|
||||
let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA;
|
||||
let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555;
|
||||
let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1;
|
||||
let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2;
|
||||
assert!(f32::from_bits(masked_nan1).is_nan());
|
||||
assert!(f32::from_bits(masked_nan2).is_nan());
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,45 @@ use crate::f64::consts;
|
|||
use crate::num::FpCategory as Fp;
|
||||
use crate::num::*;
|
||||
|
||||
/// Smallest number
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const TINY_BITS: u64 = 0x1;
|
||||
|
||||
/// Next smallest number
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const TINY_UP_BITS: u64 = 0x2;
|
||||
|
||||
/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe;
|
||||
|
||||
/// Zeroed exponent, full significant
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff;
|
||||
|
||||
/// Exponent = 0b1, zeroed significand
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000;
|
||||
|
||||
/// First pattern over the mantissa
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa;
|
||||
|
||||
/// Second pattern over the mantissa
|
||||
#[allow(dead_code)] // unused on x86
|
||||
const NAN_MASK2: u64 = 0x0005_5555_5555_5555;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f64_biteq {
|
||||
($left : expr, $right : expr) => {
|
||||
let l: &f64 = &$left;
|
||||
let r: &f64 = &$right;
|
||||
let lb = l.to_bits();
|
||||
let rb = r.to_bits();
|
||||
assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_f64() {
|
||||
test_num(10f64, 2f64);
|
||||
|
|
@ -305,27 +344,16 @@ fn test_is_sign_negative() {
|
|||
assert!((-f64::NAN).is_sign_negative());
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_f64_biteq {
|
||||
($left : expr, $right : expr) => {
|
||||
let l: &f64 = &$left;
|
||||
let r: &f64 = &$right;
|
||||
let lb = l.to_bits();
|
||||
let rb = r.to_bits();
|
||||
assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb);
|
||||
};
|
||||
}
|
||||
|
||||
// Ignore test on x87 floating point, these platforms do not guarantee NaN
|
||||
// payloads are preserved and flush denormals to zero, failing the tests.
|
||||
#[cfg(not(target_arch = "x86"))]
|
||||
#[test]
|
||||
fn test_next_up() {
|
||||
let tiny = f64::from_bits(1);
|
||||
let tiny_up = f64::from_bits(2);
|
||||
let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
|
||||
let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
|
||||
let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
|
||||
let tiny = f64::from_bits(TINY_BITS);
|
||||
let tiny_up = f64::from_bits(TINY_UP_BITS);
|
||||
let max_down = f64::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN);
|
||||
assert_f64_biteq!(f64::MIN.next_up(), -max_down);
|
||||
assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0);
|
||||
|
|
@ -341,8 +369,8 @@ fn test_next_up() {
|
|||
assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY);
|
||||
|
||||
let nan0 = f64::NAN;
|
||||
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
|
||||
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
|
||||
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1);
|
||||
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2);
|
||||
assert_f64_biteq!(nan0.next_up(), nan0);
|
||||
assert_f64_biteq!(nan1.next_up(), nan1);
|
||||
assert_f64_biteq!(nan2.next_up(), nan2);
|
||||
|
|
@ -353,11 +381,11 @@ fn test_next_up() {
|
|||
#[cfg(not(target_arch = "x86"))]
|
||||
#[test]
|
||||
fn test_next_down() {
|
||||
let tiny = f64::from_bits(1);
|
||||
let tiny_up = f64::from_bits(2);
|
||||
let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe);
|
||||
let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff);
|
||||
let smallest_normal = f64::from_bits(0x0010_0000_0000_0000);
|
||||
let tiny = f64::from_bits(TINY_BITS);
|
||||
let tiny_up = f64::from_bits(TINY_UP_BITS);
|
||||
let max_down = f64::from_bits(MAX_DOWN_BITS);
|
||||
let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS);
|
||||
let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS);
|
||||
assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY);
|
||||
assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY);
|
||||
assert_f64_biteq!((-max_down).next_down(), f64::MIN);
|
||||
|
|
@ -374,8 +402,8 @@ fn test_next_down() {
|
|||
assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX);
|
||||
|
||||
let nan0 = f64::NAN;
|
||||
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa);
|
||||
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555);
|
||||
let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1);
|
||||
let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2);
|
||||
assert_f64_biteq!(nan0.next_down(), nan0);
|
||||
assert_f64_biteq!(nan1.next_down(), nan1);
|
||||
assert_f64_biteq!(nan2.next_down(), nan2);
|
||||
|
|
@ -715,9 +743,8 @@ fn test_float_bits_conv() {
|
|||
assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25);
|
||||
|
||||
// Check that NaNs roundtrip their bits regardless of signaling-ness
|
||||
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
|
||||
let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
|
||||
let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
|
||||
let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1;
|
||||
let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2;
|
||||
assert!(f64::from_bits(masked_nan1).is_nan());
|
||||
assert!(f64::from_bits(masked_nan2).is_nan());
|
||||
|
||||
|
|
|
|||
|
|
@ -552,10 +552,20 @@ impl OsString {
|
|||
OsStr::from_inner_mut(self.inner.leak())
|
||||
}
|
||||
|
||||
/// Part of a hack to make PathBuf::push/pop more efficient.
|
||||
/// Provides plumbing to core `Vec::truncate`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
|
||||
self.inner.as_mut_vec_for_path_buf()
|
||||
pub(crate) fn truncate(&mut self, len: usize) {
|
||||
self.inner.truncate(len);
|
||||
}
|
||||
|
||||
/// Provides plumbing to core `Vec::extend_from_slice`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
|
||||
self.inner.extend_from_slice(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -373,10 +373,17 @@ macro_rules! dbg {
|
|||
};
|
||||
}
|
||||
|
||||
/// Verify that floats are within a tolerance of each other, 1.0e-6 by default.
|
||||
#[cfg(test)]
|
||||
macro_rules! assert_approx_eq {
|
||||
($a:expr, $b:expr) => {{
|
||||
($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }};
|
||||
($a:expr, $b:expr, $lim:expr) => {{
|
||||
let (a, b) = (&$a, &$b);
|
||||
assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
|
||||
let diff = (*a - *b).abs();
|
||||
assert!(
|
||||
diff < $lim,
|
||||
"{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})",
|
||||
lim = $lim
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1163,11 +1163,6 @@ pub struct PathBuf {
|
|||
}
|
||||
|
||||
impl PathBuf {
|
||||
#[inline]
|
||||
fn as_mut_vec(&mut self) -> &mut Vec<u8> {
|
||||
self.inner.as_mut_vec_for_path_buf()
|
||||
}
|
||||
|
||||
/// Allocates an empty `PathBuf`.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -1290,7 +1285,8 @@ impl PathBuf {
|
|||
|
||||
fn _push(&mut self, path: &Path) {
|
||||
// in general, a separator is needed if the rightmost byte is not a separator
|
||||
let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
|
||||
let buf = self.inner.as_encoded_bytes();
|
||||
let mut need_sep = buf.last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
|
||||
|
||||
// in the special case of `C:` on Windows, do *not* add a separator
|
||||
let comps = self.components();
|
||||
|
|
@ -1304,7 +1300,7 @@ impl PathBuf {
|
|||
|
||||
// absolute `path` replaces `self`
|
||||
if path.is_absolute() || path.prefix().is_some() {
|
||||
self.as_mut_vec().truncate(0);
|
||||
self.inner.truncate(0);
|
||||
|
||||
// verbatim paths need . and .. removed
|
||||
} else if comps.prefix_verbatim() && !path.inner.is_empty() {
|
||||
|
|
@ -1349,7 +1345,7 @@ impl PathBuf {
|
|||
// `path` has a root but no prefix, e.g., `\windows` (Windows only)
|
||||
} else if path.has_root() {
|
||||
let prefix_len = self.components().prefix_remaining();
|
||||
self.as_mut_vec().truncate(prefix_len);
|
||||
self.inner.truncate(prefix_len);
|
||||
|
||||
// `path` is a pure relative path
|
||||
} else if need_sep {
|
||||
|
|
@ -1382,7 +1378,7 @@ impl PathBuf {
|
|||
pub fn pop(&mut self) -> bool {
|
||||
match self.parent().map(|p| p.as_u8_slice().len()) {
|
||||
Some(len) => {
|
||||
self.as_mut_vec().truncate(len);
|
||||
self.inner.truncate(len);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
|
|
@ -1510,15 +1506,14 @@ impl PathBuf {
|
|||
// truncate until right after the file stem
|
||||
let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr();
|
||||
let start = self.inner.as_encoded_bytes().as_ptr().addr();
|
||||
let v = self.as_mut_vec();
|
||||
v.truncate(end_file_stem.wrapping_sub(start));
|
||||
self.inner.truncate(end_file_stem.wrapping_sub(start));
|
||||
|
||||
// add the new extension, if any
|
||||
let new = extension.as_encoded_bytes();
|
||||
let new = extension;
|
||||
if !new.is_empty() {
|
||||
v.reserve_exact(new.len() + 1);
|
||||
v.push(b'.');
|
||||
v.extend_from_slice(new);
|
||||
self.inner.reserve_exact(new.len() + 1);
|
||||
self.inner.push(OsStr::new("."));
|
||||
self.inner.push(new);
|
||||
}
|
||||
|
||||
true
|
||||
|
|
@ -2645,18 +2640,18 @@ impl Path {
|
|||
None => {
|
||||
// Enough capacity for the extension and the dot
|
||||
let capacity = self_len + extension.len() + 1;
|
||||
let whole_path = self_bytes.iter();
|
||||
let whole_path = self_bytes;
|
||||
(capacity, whole_path)
|
||||
}
|
||||
Some(previous_extension) => {
|
||||
let capacity = self_len + extension.len() - previous_extension.len();
|
||||
let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter();
|
||||
let path_till_dot = &self_bytes[..self_len - previous_extension.len()];
|
||||
(capacity, path_till_dot)
|
||||
}
|
||||
};
|
||||
|
||||
let mut new_path = PathBuf::with_capacity(new_capacity);
|
||||
new_path.as_mut_vec().extend(slice_to_copy);
|
||||
new_path.inner.extend_from_slice(slice_to_copy);
|
||||
new_path.set_extension(extension);
|
||||
new_path
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,10 +202,20 @@ impl Buf {
|
|||
self.as_slice().into_rc()
|
||||
}
|
||||
|
||||
/// Part of a hack to make PathBuf::push/pop more efficient.
|
||||
/// Provides plumbing to core `Vec::truncate`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.inner
|
||||
pub(crate) fn truncate(&mut self, len: usize) {
|
||||
self.inner.truncate(len);
|
||||
}
|
||||
|
||||
/// Provides plumbing to core `Vec::extend_from_slice`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
|
||||
self.inner.extend_from_slice(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -165,10 +165,20 @@ impl Buf {
|
|||
self.as_slice().into_rc()
|
||||
}
|
||||
|
||||
/// Part of a hack to make PathBuf::push/pop more efficient.
|
||||
/// Provides plumbing to core `Vec::truncate`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
|
||||
self.inner.as_mut_vec_for_path_buf()
|
||||
pub(crate) fn truncate(&mut self, len: usize) {
|
||||
self.inner.truncate(len);
|
||||
}
|
||||
|
||||
/// Provides plumbing to core `Vec::extend_from_slice`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
|
||||
self.inner.extend_from_slice(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -474,13 +474,13 @@ impl Wtf8Buf {
|
|||
Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false }
|
||||
}
|
||||
|
||||
/// Part of a hack to make PathBuf::push/pop more efficient.
|
||||
/// Provides plumbing to core `Vec::extend_from_slice`.
|
||||
/// More well behaving alternative to allowing outer types
|
||||
/// full mutable access to the core `Vec`.
|
||||
#[inline]
|
||||
pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
|
||||
// FIXME: this function should not even exist, as it implies violating Wtf8Buf invariants
|
||||
// For now, simply assume that is about to happen.
|
||||
self.is_known_utf8 = false;
|
||||
&mut self.bytes
|
||||
pub(crate) fn extend_from_slice(&mut self, other: &[u8]) {
|
||||
self.bytes.extend_from_slice(other);
|
||||
self.is_known_utf8 = self.is_known_utf8 || self.next_surrogate(0).is_none();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,13 @@ pub fn prepare_tool_cargo(
|
|||
// See https://github.com/rust-lang/rust/issues/116538
|
||||
cargo.rustflag("-Zunstable-options");
|
||||
|
||||
// `-Zon-broken-pipe=kill` breaks cargo tests
|
||||
if !path.ends_with("cargo") {
|
||||
// If the output is piped to e.g. `head -n1` we want the process to be killed,
|
||||
// rather than having an error bubble up and cause a panic.
|
||||
cargo.rustflag("-Zon-broken-pipe=kill");
|
||||
}
|
||||
|
||||
cargo
|
||||
}
|
||||
|
||||
|
|
@ -575,7 +582,8 @@ impl Step for Rustdoc {
|
|||
features.push("jemalloc".to_string());
|
||||
}
|
||||
|
||||
let mut cargo = prepare_tool_cargo(
|
||||
// NOTE: Never modify the rustflags here, it breaks the build cache for other tools!
|
||||
let cargo = prepare_tool_cargo(
|
||||
builder,
|
||||
build_compiler,
|
||||
Mode::ToolRustc,
|
||||
|
|
@ -586,11 +594,6 @@ impl Step for Rustdoc {
|
|||
features.as_slice(),
|
||||
);
|
||||
|
||||
// If the rustdoc output is piped to e.g. `head -n1` we want the process
|
||||
// to be killed, rather than having an error bubble up and cause a
|
||||
// panic.
|
||||
cargo.rustflag("-Zon-broken-pipe=kill");
|
||||
|
||||
let _guard = builder.msg_tool(
|
||||
Kind::Build,
|
||||
Mode::ToolRustc,
|
||||
|
|
|
|||
|
|
@ -35,4 +35,4 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
|
|||
# NOTE: intentionally uses python2 for x.py so we can test it still works.
|
||||
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
|
||||
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
|
||||
--stage 0 src/tools/tidy tidyselftest --extra-checks=py:lint
|
||||
--stage 0 src/tools/tidy tidyselftest --extra-checks=py:lint,cpp:fmt
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
# Lint Levels
|
||||
|
||||
In `rustc`, lints are divided into five *levels*:
|
||||
In `rustc`, lints are divided into six *levels*:
|
||||
|
||||
1. allow
|
||||
2. warn
|
||||
3. force-warn
|
||||
4. deny
|
||||
5. forbid
|
||||
2. expect
|
||||
3. warn
|
||||
4. force-warn
|
||||
5. deny
|
||||
6. forbid
|
||||
|
||||
Each lint has a default level (explained in the lint listing later in this
|
||||
chapter), and the compiler has a default warning level. First, let's explain
|
||||
|
|
@ -33,6 +34,40 @@ But this code violates the `missing_docs` lint.
|
|||
These lints exist mostly to be manually turned on via configuration, as we'll
|
||||
talk about later in this section.
|
||||
|
||||
## expect
|
||||
|
||||
Sometimes, it can be helpful to suppress lints, but at the same time ensure that
|
||||
the code in question still emits them. The 'expect' level does exactly this. If
|
||||
the lint in question is not emitted, the `unfulfilled_lint_expectation` lint
|
||||
triggers on the `expect` attribute, notifying you that the expectation is no
|
||||
longer fulfilled.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
#[expect(unused_variables)]
|
||||
let unused = "Everyone ignores me";
|
||||
|
||||
#[expect(unused_variables)] // `unused_variables` lint is not emitted
|
||||
let used = "I'm useful"; // the expectation is therefore unfulfilled
|
||||
println!("The `used` value is equal to: {:?}", used);
|
||||
}
|
||||
```
|
||||
|
||||
This will produce the following warning:
|
||||
|
||||
```txt
|
||||
warning: this lint expectation is unfulfilled
|
||||
--> src/main.rs:7:14
|
||||
|
|
||||
7 | #[expect(unused_variables)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
|
||||
```
|
||||
|
||||
This level can only be defined via the `#[expect]` attribute, there is no equivalent
|
||||
flag. Lints with the special 'force-warn' level will still be emitted as usual.
|
||||
|
||||
## warn
|
||||
|
||||
The 'warn' lint level will produce a warning if you violate the lint. For example,
|
||||
|
|
@ -240,6 +275,21 @@ And use multiple attributes together:
|
|||
pub fn foo() {}
|
||||
```
|
||||
|
||||
All lint attributes support an additional `reason` parameter, to give context why
|
||||
a certain attribute was added. This reason will be displayed as part of the lint
|
||||
message, if the lint is emitted at the defined level.
|
||||
|
||||
```rust
|
||||
use std::path::PathBuf;
|
||||
pub fn get_path() -> PathBuf {
|
||||
#[allow(unused_mut, reason = "this is only modified on some platforms")]
|
||||
let mut file_name = PathBuf::from("git");
|
||||
#[cfg(target_os = "windows")]
|
||||
file_name.set_extension("exe");
|
||||
file_name
|
||||
}
|
||||
```
|
||||
|
||||
### Capping lints
|
||||
|
||||
`rustc` supports a flag, `--cap-lints LEVEL` that sets the "lint cap level."
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit bc89bffa5987d4af8f71011c7557119b39e44a65
|
||||
Subproject commit 4ed7bee47f7dd4416b36fada1909e9a62c546246
|
||||
|
|
@ -1943,7 +1943,7 @@ Released 2022-05-19
|
|||
[#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
|
||||
* [`needless_match`]
|
||||
[#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
|
||||
* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
|
||||
* [`allow_attributes_without_reason`]
|
||||
[#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
|
||||
* [`print_in_format_impl`]
|
||||
[#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
|
||||
|
|
|
|||
|
|
@ -669,6 +669,8 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes)
|
||||
* [`allow_attributes_without_reason`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason)
|
||||
* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
|
||||
* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
|
||||
* [`assigning_clones`](https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones)
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ define_Conf! {
|
|||
///
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
(avoid_breaking_exported_api: bool = true),
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS.
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON.
|
||||
///
|
||||
/// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
|
||||
#[default_text = ""]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ macro_rules! msrv_aliases {
|
|||
|
||||
// names may refer to stabilized feature flags or library items
|
||||
msrv_aliases! {
|
||||
1,81,0 { LINT_REASONS_STABILIZATION }
|
||||
1,77,0 { C_STR_LITERALS }
|
||||
1,76,0 { PTR_FROM_REF }
|
||||
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
use ast::{AttrStyle, Attribute};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// The expect attribute is still unstable and requires the `lint_reasons`
|
||||
/// on nightly. It can be enabled by adding `#![feature(lint_reasons)]` to
|
||||
/// the crate root.
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// `#[allow]` attributes can linger after their reason for existence is gone.
|
||||
/// `#[expect]` attributes suppress the lint emission, but emit a warning if
|
||||
/// the expectation is unfulfilled. This can be useful to be notified when the
|
||||
/// lint is no longer triggered, which may indicate the attribute can be removed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #![feature(lint_reasons)]
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
|
||||
|
||||
impl LateLintPass<'_> for AllowAttribute {
|
||||
// Separate each crate's features.
|
||||
fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) {
|
||||
if !in_external_macro(cx.sess(), attr.span)
|
||||
&& cx.tcx.features().lint_reasons
|
||||
&& let AttrStyle::Outer = attr.style
|
||||
&& let Some(ident) = attr.ident()
|
||||
&& ident.name == rustc_span::symbol::sym::allow
|
||||
&& !is_from_proc_macro(cx, &attr)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ALLOW_ATTRIBUTES,
|
||||
ident.span,
|
||||
"#[allow] attribute found",
|
||||
"replace it with",
|
||||
"expect".into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs
Normal file
26
src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_ast::{AttrStyle, Attribute};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use super::ALLOW_ATTRIBUTES;
|
||||
|
||||
// Separate each crate's features.
|
||||
pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) {
|
||||
if !in_external_macro(cx.sess(), attr.span)
|
||||
&& let AttrStyle::Outer = attr.style
|
||||
&& let Some(ident) = attr.ident()
|
||||
&& !is_from_proc_macro(cx, &attr)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ALLOW_ATTRIBUTES,
|
||||
ident.span,
|
||||
"#[allow] attribute found",
|
||||
"replace it with",
|
||||
"expect".into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,11 +8,6 @@ use rustc_span::sym;
|
|||
use rustc_span::symbol::Symbol;
|
||||
|
||||
pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
|
||||
// Check for the feature
|
||||
if !cx.tcx.features().lint_reasons {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the reason is present
|
||||
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
|
||||
&& let MetaItemKind::NameValue(_) = &item.kind
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! checks for attributes
|
||||
|
||||
mod allow_attributes_without_reason;
|
||||
mod allow_attributes;
|
||||
mod blanket_clippy_restriction_lints;
|
||||
mod deprecated_cfg_attr;
|
||||
mod deprecated_semver;
|
||||
|
|
@ -14,11 +15,11 @@ mod unnecessary_clippy_cfg;
|
|||
mod useless_attribute;
|
||||
mod utils;
|
||||
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, impl_lint_pass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait};
|
||||
|
||||
|
|
@ -272,23 +273,17 @@ declare_clippy_lint! {
|
|||
/// ### What it does
|
||||
/// Checks for attributes that allow lints without a reason.
|
||||
///
|
||||
/// (This requires the `lint_reasons` feature)
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Justifying each `allow` helps readers understand the reasoning,
|
||||
/// and may allow removing `allow` attributes if their purpose is obsolete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![feature(lint_reasons)]
|
||||
///
|
||||
/// #![allow(clippy::some_lint)]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![feature(lint_reasons)]
|
||||
///
|
||||
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
|
|
@ -297,6 +292,41 @@ declare_clippy_lint! {
|
|||
"ensures that all `allow` and `expect` attributes have a reason"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
|
||||
/// the expectation is unfulfilled. This can be useful to be notified when the
|
||||
/// lint is no longer triggered.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[should_panic]` attributes without specifying the expected panic message.
|
||||
|
|
@ -469,7 +499,12 @@ declare_clippy_lint! {
|
|||
"duplicated attribute"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Attributes => [
|
||||
#[derive(Clone)]
|
||||
pub struct Attributes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Attributes => [
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
INLINE_ALWAYS,
|
||||
DEPRECATED_SEMVER,
|
||||
|
|
@ -480,6 +515,13 @@ declare_lint_pass!(Attributes => [
|
|||
DUPLICATED_ATTRIBUTES,
|
||||
]);
|
||||
|
||||
impl Attributes {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
blanket_clippy_restriction_lints::check_command_line(cx);
|
||||
|
|
@ -492,8 +534,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
if is_lint_level(ident.name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, ident.name, items);
|
||||
}
|
||||
if matches!(ident.name, sym::allow) {
|
||||
if self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes::check(cx, attr);
|
||||
}
|
||||
}
|
||||
if matches!(ident.name, sym::allow | sym::expect) {
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
if self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
}
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
|
|
@ -537,6 +586,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
pub struct EarlyAttributes {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO,
|
||||
crate::absolute_paths::ABSOLUTE_PATHS_INFO,
|
||||
crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
|
||||
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
|
||||
crate::approx_const::APPROX_CONSTANT_INFO,
|
||||
crate::arc_with_non_send_sync::ARC_WITH_NON_SEND_SYNC_INFO,
|
||||
|
|
@ -49,6 +48,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO,
|
||||
crate::assigning_clones::ASSIGNING_CLONES_INFO,
|
||||
crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO,
|
||||
crate::attrs::ALLOW_ATTRIBUTES_INFO,
|
||||
crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
|
||||
crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
|
||||
crate::attrs::DEPRECATED_CFG_ATTR_INFO,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use clippy_utils::{
|
|||
expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
|
||||
};
|
||||
use core::mem;
|
||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||
use rustc_ast::util::parser::{PREC_UNAMBIGUOUS, PREC_PREFIX};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
|
|
@ -1013,7 +1013,7 @@ fn report<'tcx>(
|
|||
let (precedence, calls_field) = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
|
||||
Node::Expr(e) => match e.kind {
|
||||
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
|
||||
ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
|
||||
ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))),
|
||||
_ => (e.precedence().order(), false),
|
||||
},
|
||||
_ => (0, false),
|
||||
|
|
@ -1160,7 +1160,7 @@ impl<'tcx> Dereferencing<'tcx> {
|
|||
},
|
||||
Some(parent) if !parent.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
if parent.precedence().order() == PREC_POSTFIX {
|
||||
if parent.precedence().order() == PREC_UNAMBIGUOUS {
|
||||
// Parentheses would be needed here, don't lint.
|
||||
*outer_pat = None;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(lint_reasons)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
|
@ -73,7 +73,6 @@ mod renamed_lints;
|
|||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
mod absolute_paths;
|
||||
mod allow_attributes;
|
||||
mod almost_complete_range;
|
||||
mod approx_const;
|
||||
mod arc_with_non_send_sync;
|
||||
|
|
@ -699,7 +698,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
|
||||
store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default());
|
||||
store.register_late_pass(|_| Box::new(len_zero::LenZero));
|
||||
store.register_late_pass(|_| Box::new(attrs::Attributes));
|
||||
store.register_late_pass(move |_| Box::new(attrs::Attributes::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions));
|
||||
store.register_late_pass(|_| Box::new(unicode::Unicode));
|
||||
store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
|
||||
|
|
@ -1065,7 +1064,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
|
||||
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
|
||||
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
|
||||
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
|
||||
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
|
||||
store.register_late_pass(move |_| {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use clippy_utils::{
|
|||
can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
|
||||
peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
|
|
@ -117,7 +117,7 @@ where
|
|||
// it's being passed by value.
|
||||
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
||||
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
|
||||
let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_POSTFIX {
|
||||
let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_UNAMBIGUOUS {
|
||||
format!("({scrutinee_str})")
|
||||
} else {
|
||||
scrutinee_str.into()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(lint_reasons)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(assert_matches)]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(lint_reasons)]
|
||||
#![cfg_attr(bootstrap, feature(lint_reasons))]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![feature(lint_reasons)]
|
||||
|
||||
mod a;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue