Auto merge of #104246 - Manishearth:rollup-9o3txc7, r=Manishearth
Rollup of 9 pull requests Successful merges: - #101939 (Add loongarch64 abi support) - #103863 (Use `TraitEngine` in more places, restrict visibility of `FulfillmentCtxt` constructor) - #104036 (Suggest `is_some` when we've found `Option` but expected `bool`) - #104060 (Make `Hash`, `Hasher` and `BuildHasher` `#[const_trait]` and make `Sip` const `Hasher`) - #104077 (Use aapcs for efiapi calling convention on arm) - #104186 (Tighten the 'introduce new binding' suggestion) - #104194 (`EarlyBinder` docs) - #104233 (Don't ICE when encountering `ConstKind::Error` in `RequiredConstsVisitor`) - #104235 (Use `const_error_with_guaranteed` more) Failed merges: - #104078 (Print "Checking/Building ..." message even when --dry-run is passed) - #104169 (Migrate `:target` rules to use CSS variables) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
a3c0a02361
38 changed files with 827 additions and 104 deletions
|
@ -62,6 +62,7 @@ use rustc_span::{self, BytePos, DesugaringKind, Span};
|
|||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
||||
use rustc_trait_selection::traits::TraitEngineExt as _;
|
||||
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
@ -1038,7 +1039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let Ok(ok) = coerce.coerce(source, target) else {
|
||||
return false;
|
||||
};
|
||||
let mut fcx = traits::FulfillmentContext::new_in_snapshot();
|
||||
let mut fcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(self.tcx);
|
||||
fcx.register_predicate_obligations(self, ok.obligations);
|
||||
fcx.select_where_possible(&self).is_empty()
|
||||
})
|
||||
|
|
|
@ -42,7 +42,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|
||||
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|
||||
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|
||||
|| self.suggest_into(err, expr, expr_ty, expected);
|
||||
|| self.suggest_into(err, expr, expr_ty, expected)
|
||||
|| self.suggest_option_to_bool(err, expr, expr_ty, expected);
|
||||
|
||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||
|
|
|
@ -103,8 +103,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
|
||||
let expr = expr.peel_drop_temps();
|
||||
self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None);
|
||||
// FIXME(compiler-errors): We probably should fold some of the
|
||||
// `suggest_` functions from `emit_coerce_suggestions` into here,
|
||||
// since some of those aren't necessarily just coerce suggestions.
|
||||
let _ = self.suggest_deref_ref_or_into(
|
||||
&mut err,
|
||||
expr.peel_drop_temps(),
|
||||
expected_ty,
|
||||
ty,
|
||||
None,
|
||||
) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty);
|
||||
extend_err(&mut err);
|
||||
err.emit();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_hir_analysis::astconv::AstConv;
|
|||
use rustc_infer::infer::{self, TyCtxtInferExt};
|
||||
use rustc_infer::traits::{self, StatementAsExpression};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
|
||||
use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty};
|
||||
use rustc_session::errors::ExprParenthesesNeeded;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
@ -1116,6 +1116,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
/// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()`
|
||||
pub(crate) fn suggest_option_to_bool(
|
||||
&self,
|
||||
diag: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr_ty: Ty<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if !expected_ty.is_bool() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; };
|
||||
if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| {
|
||||
matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
|
||||
}).next();
|
||||
// Don't suggest:
|
||||
// `let Some(_) = a.is_some() && b`
|
||||
// ++++++++++
|
||||
// since the user probably just misunderstood how `let else`
|
||||
// and `&&` work together.
|
||||
if let Some((_, hir::Node::Local(local))) = cond_parent
|
||||
&& let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
|
||||
&& let hir::QPath::Resolved(None, path) = qpath
|
||||
&& let Some(did) = path.res.opt_def_id()
|
||||
.and_then(|did| self.tcx.opt_parent(did))
|
||||
.and_then(|did| self.tcx.opt_parent(did))
|
||||
&& self.tcx.is_diagnostic_item(sym::Option, did)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
expr.span.shrink_to_hi(),
|
||||
"use `Option::is_some` to test if the `Option` has a value",
|
||||
".is_some()",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Suggest wrapping the block in square brackets instead of curly braces
|
||||
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
|
||||
pub(crate) fn suggest_block_to_brackets(
|
||||
|
|
|
@ -106,6 +106,7 @@ use rustc_ast::LitKind;
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::{HashMapExt, Lock};
|
||||
use rustc_data_structures::tiny_list::TinyList;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
@ -176,7 +177,7 @@ pub enum LitToConstError {
|
|||
/// This is used for graceful error handling (`delay_span_bug`) in
|
||||
/// type checking (`Const::from_anon_const`).
|
||||
TypeError,
|
||||
Reported,
|
||||
Reported(ErrorGuaranteed),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
|
|
|
@ -2251,7 +2251,9 @@ impl<'tcx> ConstantKind<'tcx> {
|
|||
match tcx.const_eval_resolve(param_env, uneval, None) {
|
||||
Ok(val) => Self::Val(val, ty),
|
||||
Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => self,
|
||||
Err(_) => Self::Ty(tcx.const_error(ty)),
|
||||
Err(ErrorHandled::Reported(guar)) => {
|
||||
Self::Ty(tcx.const_error_with_guaranteed(ty, guar))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::mir::interpret::LitToConstInput;
|
|||
use crate::mir::ConstantKind;
|
||||
use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_macros::HashStable;
|
||||
|
@ -225,7 +224,7 @@ impl<'tcx> Const<'tcx> {
|
|||
if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) {
|
||||
match val {
|
||||
Ok(val) => Const::from_value(tcx, val, self.ty()),
|
||||
Err(ErrorGuaranteed { .. }) => tcx.const_error(self.ty()),
|
||||
Err(guar) => tcx.const_error_with_guaranteed(self.ty(), guar),
|
||||
}
|
||||
} else {
|
||||
// Either the constant isn't evaluatable or ValTree creation failed.
|
||||
|
@ -240,7 +239,7 @@ impl<'tcx> Const<'tcx> {
|
|||
if let Some(val) = self.kind().try_eval_for_mir(tcx, param_env) {
|
||||
match val {
|
||||
Ok(const_val) => ConstantKind::from_value(const_val, self.ty()),
|
||||
Err(ErrorGuaranteed { .. }) => ConstantKind::Ty(tcx.const_error(self.ty())),
|
||||
Err(guar) => ConstantKind::Ty(tcx.const_error_with_guaranteed(self.ty(), guar)),
|
||||
}
|
||||
} else {
|
||||
ConstantKind::Ty(self)
|
||||
|
|
|
@ -506,6 +506,9 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &'tcx ty::List<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
|
||||
/// needs `T` substituted immediately. This type primarily exists to avoid forgetting to call
|
||||
/// `subst`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[derive(Encodable, Decodable, HashStable)]
|
||||
pub struct EarlyBinder<T>(pub T);
|
||||
|
|
|
@ -9,6 +9,7 @@ use rustc_middle::mir::interpret::{
|
|||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, TyCtxt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
@ -26,7 +27,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let literal =
|
||||
match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
|
||||
Ok(c) => c,
|
||||
Err(LitToConstError::Reported) => ConstantKind::Ty(tcx.const_error(ty)),
|
||||
Err(LitToConstError::Reported(guar)) => {
|
||||
ConstantKind::Ty(tcx.const_error_with_guaranteed(ty, guar))
|
||||
}
|
||||
Err(LitToConstError::TypeError) => {
|
||||
bug!("encountered type error in `lit_to_mir_constant")
|
||||
}
|
||||
|
@ -105,7 +108,15 @@ pub(crate) fn lit_to_mir_constant<'tcx>(
|
|||
let LitToConstInput { lit, ty, neg } = lit_input;
|
||||
let trunc = |n| {
|
||||
let param_ty = ty::ParamEnv::reveal_all().and(ty);
|
||||
let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
|
||||
let width = tcx
|
||||
.layout_of(param_ty)
|
||||
.map_err(|_| {
|
||||
LitToConstError::Reported(tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
format!("couldn't compute width of literal: {:?}", lit_input.lit),
|
||||
))
|
||||
})?
|
||||
.size;
|
||||
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
|
||||
let result = width.truncate(n);
|
||||
trace!("trunc result: {}", result);
|
||||
|
@ -136,12 +147,20 @@ pub(crate) fn lit_to_mir_constant<'tcx>(
|
|||
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
|
||||
trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?
|
||||
}
|
||||
(ast::LitKind::Float(n, _), ty::Float(fty)) => {
|
||||
parse_float_into_constval(*n, *fty, neg).ok_or(LitToConstError::Reported)?
|
||||
}
|
||||
(ast::LitKind::Float(n, _), ty::Float(fty)) => parse_float_into_constval(*n, *fty, neg)
|
||||
.ok_or_else(|| {
|
||||
LitToConstError::Reported(tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
format!("couldn't parse float literal: {:?}", lit_input.lit),
|
||||
))
|
||||
})?,
|
||||
(ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)),
|
||||
(ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)),
|
||||
(ast::LitKind::Err, _) => return Err(LitToConstError::Reported),
|
||||
(ast::LitKind::Err, _) => {
|
||||
return Err(LitToConstError::Reported(
|
||||
tcx.sess.delay_span_bug(DUMMY_SP, "encountered LitKind::Err during mir build"),
|
||||
));
|
||||
}
|
||||
_ => return Err(LitToConstError::TypeError),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use rustc_ast as ast;
|
||||
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
|
||||
use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
pub(crate) fn lit_to_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -10,7 +11,15 @@ pub(crate) fn lit_to_const<'tcx>(
|
|||
|
||||
let trunc = |n| {
|
||||
let param_ty = ParamEnv::reveal_all().and(ty);
|
||||
let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
|
||||
let width = tcx
|
||||
.layout_of(param_ty)
|
||||
.map_err(|_| {
|
||||
LitToConstError::Reported(tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
format!("couldn't compute width of literal: {:?}", lit_input.lit),
|
||||
))
|
||||
})?
|
||||
.size;
|
||||
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
|
||||
let result = width.truncate(n);
|
||||
trace!("trunc result: {}", result);
|
||||
|
@ -44,7 +53,11 @@ pub(crate) fn lit_to_const<'tcx>(
|
|||
}
|
||||
(ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
|
||||
(ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
|
||||
(ast::LitKind::Err, _) => return Err(LitToConstError::Reported),
|
||||
(ast::LitKind::Err, _) => {
|
||||
return Err(LitToConstError::Reported(
|
||||
tcx.sess.delay_span_bug(DUMMY_SP, "encountered LitKind::Err during mir build"),
|
||||
));
|
||||
}
|
||||
_ => return Err(LitToConstError::TypeError),
|
||||
};
|
||||
|
||||
|
|
|
@ -614,7 +614,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
|
||||
match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) {
|
||||
Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind,
|
||||
Err(LitToConstError::Reported) => PatKind::Wild,
|
||||
Err(LitToConstError::Reported(_)) => PatKind::Wild,
|
||||
Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
|
|||
let literal = constant.literal;
|
||||
match literal {
|
||||
ConstantKind::Ty(c) => match c.kind() {
|
||||
ConstKind::Param(_) => {}
|
||||
ConstKind::Param(_) | ConstKind::Error(_) => {}
|
||||
_ => bug!("only ConstKind::Param should be encountered here, got {:#?}", c),
|
||||
},
|
||||
ConstantKind::Unevaluated(..) => self.required_consts.push(*constant),
|
||||
|
|
|
@ -527,6 +527,7 @@ struct DiagnosticMetadata<'ast> {
|
|||
|
||||
/// Used to detect possible new binding written without `let` and to provide structured suggestion.
|
||||
in_assignment: Option<&'ast Expr>,
|
||||
is_assign_rhs: bool,
|
||||
|
||||
/// If we are currently in a trait object definition. Used to point at the bounds when
|
||||
/// encountering a struct or enum.
|
||||
|
@ -3963,10 +3964,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
self.resolve_expr(elem, Some(expr));
|
||||
self.visit_expr(idx);
|
||||
}
|
||||
ExprKind::Assign(..) => {
|
||||
let old = self.diagnostic_metadata.in_assignment.replace(expr);
|
||||
visit::walk_expr(self, expr);
|
||||
self.diagnostic_metadata.in_assignment = old;
|
||||
ExprKind::Assign(ref lhs, ref rhs, _) => {
|
||||
if !self.diagnostic_metadata.is_assign_rhs {
|
||||
self.diagnostic_metadata.in_assignment = Some(expr);
|
||||
}
|
||||
self.visit_expr(lhs);
|
||||
self.diagnostic_metadata.is_assign_rhs = true;
|
||||
self.diagnostic_metadata.in_assignment = None;
|
||||
self.visit_expr(rhs);
|
||||
self.diagnostic_metadata.is_assign_rhs = false;
|
||||
}
|
||||
_ => {
|
||||
visit::walk_expr(self, expr);
|
||||
|
|
|
@ -1810,29 +1810,22 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
false
|
||||
}
|
||||
|
||||
fn let_binding_suggestion(&self, err: &mut Diagnostic, ident_span: Span) -> bool {
|
||||
// try to give a suggestion for this pattern: `name = 1`, which is common in other languages
|
||||
let mut added_suggestion = false;
|
||||
if let Some(Expr { kind: ExprKind::Assign(lhs, _rhs, _), .. }) = self.diagnostic_metadata.in_assignment &&
|
||||
// try to give a suggestion for this pattern: `name = blah`, which is common in other languages
|
||||
// suggest `let name = blah` to introduce a new binding
|
||||
fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool {
|
||||
if let Some(Expr { kind: ExprKind::Assign(lhs, .. ), .. }) = self.diagnostic_metadata.in_assignment &&
|
||||
let ast::ExprKind::Path(None, _) = lhs.kind {
|
||||
let sm = self.r.session.source_map();
|
||||
let line_span = sm.span_extend_to_line(ident_span);
|
||||
let ident_name = sm.span_to_snippet(ident_span).unwrap();
|
||||
// HACK(chenyukang): make sure ident_name is at the starting of the line to protect against macros
|
||||
if sm
|
||||
.span_to_snippet(line_span)
|
||||
.map_or(false, |s| s.trim().starts_with(&ident_name))
|
||||
{
|
||||
if !ident_span.from_expansion() {
|
||||
err.span_suggestion_verbose(
|
||||
ident_span.shrink_to_lo(),
|
||||
"you might have meant to introduce a new binding",
|
||||
"let ".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
added_suggestion = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
added_suggestion
|
||||
false
|
||||
}
|
||||
|
||||
fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> {
|
||||
|
|
342
compiler/rustc_target/src/abi/call/loongarch.rs
Normal file
342
compiler/rustc_target/src/abi/call/loongarch.rs
Normal file
|
@ -0,0 +1,342 @@
|
|||
use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
|
||||
use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
|
||||
use crate::spec::HasTargetSpec;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum RegPassKind {
|
||||
Float(Reg),
|
||||
Integer(Reg),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum FloatConv {
|
||||
FloatPair(Reg, Reg),
|
||||
Float(Reg),
|
||||
MixedPair(Reg, Reg),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct CannotUseFpConv;
|
||||
|
||||
fn is_loongarch_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool {
|
||||
match arg.layout.abi {
|
||||
Abi::Vector { .. } => true,
|
||||
_ => arg.layout.is_aggregate(),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_use_fp_conv_helper<'a, Ty, C>(
|
||||
cx: &C,
|
||||
arg_layout: &TyAndLayout<'a, Ty>,
|
||||
xlen: u64,
|
||||
flen: u64,
|
||||
field1_kind: &mut RegPassKind,
|
||||
field2_kind: &mut RegPassKind,
|
||||
) -> Result<(), CannotUseFpConv>
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
match arg_layout.abi {
|
||||
Abi::Scalar(scalar) => match scalar.primitive() {
|
||||
abi::Int(..) | abi::Pointer => {
|
||||
if arg_layout.size.bits() > xlen {
|
||||
return Err(CannotUseFpConv);
|
||||
}
|
||||
match (*field1_kind, *field2_kind) {
|
||||
(RegPassKind::Unknown, _) => {
|
||||
*field1_kind = RegPassKind::Integer(Reg {
|
||||
kind: RegKind::Integer,
|
||||
size: arg_layout.size,
|
||||
});
|
||||
}
|
||||
(RegPassKind::Float(_), RegPassKind::Unknown) => {
|
||||
*field2_kind = RegPassKind::Integer(Reg {
|
||||
kind: RegKind::Integer,
|
||||
size: arg_layout.size,
|
||||
});
|
||||
}
|
||||
_ => return Err(CannotUseFpConv),
|
||||
}
|
||||
}
|
||||
abi::F32 | abi::F64 => {
|
||||
if arg_layout.size.bits() > flen {
|
||||
return Err(CannotUseFpConv);
|
||||
}
|
||||
match (*field1_kind, *field2_kind) {
|
||||
(RegPassKind::Unknown, _) => {
|
||||
*field1_kind =
|
||||
RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
|
||||
}
|
||||
(_, RegPassKind::Unknown) => {
|
||||
*field2_kind =
|
||||
RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
|
||||
}
|
||||
_ => return Err(CannotUseFpConv),
|
||||
}
|
||||
}
|
||||
},
|
||||
Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv),
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields {
|
||||
FieldsShape::Primitive => {
|
||||
unreachable!("aggregates can't have `FieldsShape::Primitive`")
|
||||
}
|
||||
FieldsShape::Union(_) => {
|
||||
if !arg_layout.is_zst() {
|
||||
return Err(CannotUseFpConv);
|
||||
}
|
||||
}
|
||||
FieldsShape::Array { count, .. } => {
|
||||
for _ in 0..count {
|
||||
let elem_layout = arg_layout.field(cx, 0);
|
||||
should_use_fp_conv_helper(
|
||||
cx,
|
||||
&elem_layout,
|
||||
xlen,
|
||||
flen,
|
||||
field1_kind,
|
||||
field2_kind,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
FieldsShape::Arbitrary { .. } => {
|
||||
match arg_layout.variants {
|
||||
abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
|
||||
abi::Variants::Single { .. } => (),
|
||||
}
|
||||
for i in arg_layout.fields.index_by_increasing_offset() {
|
||||
let field = arg_layout.field(cx, i);
|
||||
should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn should_use_fp_conv<'a, Ty, C>(
|
||||
cx: &C,
|
||||
arg: &TyAndLayout<'a, Ty>,
|
||||
xlen: u64,
|
||||
flen: u64,
|
||||
) -> Option<FloatConv>
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
let mut field1_kind = RegPassKind::Unknown;
|
||||
let mut field2_kind = RegPassKind::Unknown;
|
||||
if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() {
|
||||
return None;
|
||||
}
|
||||
match (field1_kind, field2_kind) {
|
||||
(RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)),
|
||||
(RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)),
|
||||
(RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)),
|
||||
(RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) {
|
||||
match conv {
|
||||
FloatConv::Float(f) => {
|
||||
arg.cast_to(f);
|
||||
}
|
||||
FloatConv::FloatPair(l, r) => {
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
}
|
||||
FloatConv::MixedPair(l, r) => {
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let total = arg.layout.size;
|
||||
|
||||
// "Scalars wider than 2✕XLEN are passed by reference and are replaced in
|
||||
// the argument list with the address."
|
||||
// "Aggregates larger than 2✕XLEN bits are passed by reference and are
|
||||
// replaced in the argument list with the address, as are C++ aggregates
|
||||
// with nontrivial copy constructors, destructors, or vtables."
|
||||
if total.bits() > 2 * xlen {
|
||||
// We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN.
|
||||
if is_loongarch_aggregate(arg) {
|
||||
arg.make_indirect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let xlen_reg = match xlen {
|
||||
32 => Reg::i32(),
|
||||
64 => Reg::i64(),
|
||||
_ => unreachable!("Unsupported XLEN: {}", xlen),
|
||||
};
|
||||
if is_loongarch_aggregate(arg) {
|
||||
if total.bits() <= xlen {
|
||||
arg.cast_to(xlen_reg);
|
||||
} else {
|
||||
arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// "When passed in registers, scalars narrower than XLEN bits are widened
|
||||
// according to the sign of their type up to 32 bits, then sign-extended to
|
||||
// XLEN bits."
|
||||
extend_integer_width(arg, xlen);
|
||||
false
|
||||
}
|
||||
|
||||
fn classify_arg<'a, Ty, C>(
|
||||
cx: &C,
|
||||
arg: &mut ArgAbi<'a, Ty>,
|
||||
xlen: u64,
|
||||
flen: u64,
|
||||
is_vararg: bool,
|
||||
avail_gprs: &mut u64,
|
||||
avail_fprs: &mut u64,
|
||||
) where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
if !is_vararg {
|
||||
match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
|
||||
Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {
|
||||
*avail_fprs -= 1;
|
||||
arg.cast_to(f);
|
||||
return;
|
||||
}
|
||||
Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => {
|
||||
*avail_fprs -= 2;
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
return;
|
||||
}
|
||||
Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => {
|
||||
*avail_gprs -= 1;
|
||||
*avail_fprs -= 1;
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let total = arg.layout.size;
|
||||
let align = arg.layout.align.abi.bits();
|
||||
|
||||
// "Scalars wider than 2✕XLEN are passed by reference and are replaced in
|
||||
// the argument list with the address."
|
||||
// "Aggregates larger than 2✕XLEN bits are passed by reference and are
|
||||
// replaced in the argument list with the address, as are C++ aggregates
|
||||
// with nontrivial copy constructors, destructors, or vtables."
|
||||
if total.bits() > 2 * xlen {
|
||||
// We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN.
|
||||
if is_loongarch_aggregate(arg) {
|
||||
arg.make_indirect();
|
||||
}
|
||||
if *avail_gprs >= 1 {
|
||||
*avail_gprs -= 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let double_xlen_reg = match xlen {
|
||||
32 => Reg::i64(),
|
||||
64 => Reg::i128(),
|
||||
_ => unreachable!("Unsupported XLEN: {}", xlen),
|
||||
};
|
||||
|
||||
let xlen_reg = match xlen {
|
||||
32 => Reg::i32(),
|
||||
64 => Reg::i64(),
|
||||
_ => unreachable!("Unsupported XLEN: {}", xlen),
|
||||
};
|
||||
|
||||
if total.bits() > xlen {
|
||||
let align_regs = align > xlen;
|
||||
if is_loongarch_aggregate(arg) {
|
||||
arg.cast_to(Uniform {
|
||||
unit: if align_regs { double_xlen_reg } else { xlen_reg },
|
||||
total: Size::from_bits(xlen * 2),
|
||||
});
|
||||
}
|
||||
if align_regs && is_vararg {
|
||||
*avail_gprs -= *avail_gprs % 2;
|
||||
}
|
||||
if *avail_gprs >= 2 {
|
||||
*avail_gprs -= 2;
|
||||
} else {
|
||||
*avail_gprs = 0;
|
||||
}
|
||||
return;
|
||||
} else if is_loongarch_aggregate(arg) {
|
||||
arg.cast_to(xlen_reg);
|
||||
if *avail_gprs >= 1 {
|
||||
*avail_gprs -= 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// "When passed in registers, scalars narrower than XLEN bits are widened
|
||||
// according to the sign of their type up to 32 bits, then sign-extended to
|
||||
// XLEN bits."
|
||||
if *avail_gprs >= 1 {
|
||||
extend_integer_width(arg, xlen);
|
||||
*avail_gprs -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) {
|
||||
if let Abi::Scalar(scalar) = arg.layout.abi {
|
||||
if let abi::Int(i, _) = scalar.primitive() {
|
||||
// 32-bit integers are always sign-extended
|
||||
if i.size().bits() == 32 && xlen > 32 {
|
||||
if let PassMode::Direct(ref mut attrs) = arg.mode {
|
||||
attrs.ext(ArgExtension::Sext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arg.extend_integer_width_to(xlen);
|
||||
}
|
||||
|
||||
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout + HasTargetSpec,
|
||||
{
|
||||
let xlen = cx.data_layout().pointer_size.bits();
|
||||
let flen = match &cx.target_spec().llvm_abiname[..] {
|
||||
"ilp32f" | "lp64f" => 32,
|
||||
"ilp32d" | "lp64d" => 64,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let mut avail_gprs = 8;
|
||||
let mut avail_fprs = 8;
|
||||
|
||||
if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) {
|
||||
avail_gprs -= 1;
|
||||
}
|
||||
|
||||
for (i, arg) in fn_abi.args.iter_mut().enumerate() {
|
||||
if arg.is_ignore() {
|
||||
continue;
|
||||
}
|
||||
classify_arg(
|
||||
cx,
|
||||
arg,
|
||||
xlen,
|
||||
flen,
|
||||
i >= fn_abi.fixed_count as usize,
|
||||
&mut avail_gprs,
|
||||
&mut avail_fprs,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ mod arm;
|
|||
mod avr;
|
||||
mod bpf;
|
||||
mod hexagon;
|
||||
mod loongarch;
|
||||
mod m68k;
|
||||
mod mips;
|
||||
mod mips64;
|
||||
|
@ -696,6 +697,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
|
|||
"amdgpu" => amdgpu::compute_abi_info(cx, self),
|
||||
"arm" => arm::compute_abi_info(cx, self),
|
||||
"avr" => avr::compute_abi_info(self),
|
||||
"loongarch64" => loongarch::compute_abi_info(cx, self),
|
||||
"m68k" => m68k::compute_abi_info(self),
|
||||
"mips" => mips::compute_abi_info(cx, self),
|
||||
"mips64" => mips64::compute_abi_info(cx, self),
|
||||
|
|
|
@ -1911,6 +1911,7 @@ impl Target {
|
|||
Abi::Stdcall { unwind }
|
||||
}
|
||||
Abi::System { unwind } => Abi::C { unwind },
|
||||
Abi::EfiApi if self.arch == "arm" => Abi::Aapcs { unwind: false },
|
||||
Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false },
|
||||
Abi::EfiApi => Abi::C { unwind: false },
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::errors::AutoDerefReachedRecursionLimit;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use crate::traits::{self, TraitEngine};
|
||||
use crate::traits::{self, TraitEngine, TraitEngineExt};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt};
|
||||
|
@ -139,7 +139,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
|
||||
let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
|
||||
let normalized_ty = fulfillcx.normalize_projection_type(
|
||||
&self.infcx,
|
||||
self.param_env,
|
||||
|
|
|
@ -19,7 +19,7 @@ pub struct FulfillmentContext<'tcx> {
|
|||
}
|
||||
|
||||
impl FulfillmentContext<'_> {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(super) fn new() -> Self {
|
||||
FulfillmentContext {
|
||||
obligations: FxIndexSet::default(),
|
||||
relationships: FxHashMap::default(),
|
||||
|
|
|
@ -3,13 +3,14 @@ pub mod on_unimplemented;
|
|||
pub mod suggestions;
|
||||
|
||||
use super::{
|
||||
FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
|
||||
Obligation, ObligationCause, ObligationCauseCode, OutputTypeParameterMismatch, Overflow,
|
||||
PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
|
||||
FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause,
|
||||
ObligationCauseCode, OutputTypeParameterMismatch, Overflow, PredicateObligation,
|
||||
SelectionContext, SelectionError, TraitNotObjectSafe,
|
||||
};
|
||||
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
||||
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use crate::infer::{self, InferCtxt, TyCtxtInferExt};
|
||||
use crate::traits::engine::TraitEngineExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use crate::traits::query::normalize::AtExt as _;
|
||||
use crate::traits::specialize::to_pretty_impl_header;
|
||||
|
@ -352,7 +353,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
|||
})
|
||||
.to_predicate(self.tcx),
|
||||
);
|
||||
let mut fulfill_cx = FulfillmentContext::new_in_snapshot();
|
||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(self.tcx);
|
||||
fulfill_cx.register_predicate_obligation(self, obligation);
|
||||
if fulfill_cx.select_all_or_error(self).is_empty() {
|
||||
return Ok((
|
||||
|
|
|
@ -85,7 +85,7 @@ static_assert_size!(PendingPredicateObligation<'_>, 72);
|
|||
|
||||
impl<'a, 'tcx> FulfillmentContext<'tcx> {
|
||||
/// Creates a new fulfillment context.
|
||||
pub fn new() -> FulfillmentContext<'tcx> {
|
||||
pub(super) fn new() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
relationships: FxHashMap::default(),
|
||||
|
@ -93,7 +93,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_in_snapshot() -> FulfillmentContext<'tcx> {
|
||||
pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
relationships: FxHashMap::default(),
|
||||
|
|
|
@ -235,7 +235,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
neg,
|
||||
}) {
|
||||
Ok(c) => c,
|
||||
Err(LitToConstError::Reported) => self.tcx.const_error(node.ty),
|
||||
Err(LitToConstError::Reported(guar)) => {
|
||||
self.tcx.const_error_with_guaranteed(node.ty, guar)
|
||||
}
|
||||
Err(LitToConstError::TypeError) => {
|
||||
bug!("encountered type error in lit_to_const")
|
||||
}
|
||||
|
|
|
@ -86,7 +86,8 @@
|
|||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::fmt;
|
||||
use crate::marker;
|
||||
use crate::intrinsics::const_eval_select;
|
||||
use crate::marker::{self, Destruct};
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
|
@ -183,6 +184,7 @@ mod sip;
|
|||
/// [impl]: ../../std/primitive.str.html#impl-Hash-for-str
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "Hash"]
|
||||
#[const_trait]
|
||||
pub trait Hash {
|
||||
/// Feeds this value into the given [`Hasher`].
|
||||
///
|
||||
|
@ -234,14 +236,26 @@ pub trait Hash {
|
|||
/// [`hash`]: Hash::hash
|
||||
/// [`hash_slice`]: Hash::hash_slice
|
||||
#[stable(feature = "hash_slice", since = "1.3.0")]
|
||||
fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
|
||||
fn hash_slice<H: ~const Hasher>(data: &[Self], state: &mut H)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
//FIXME(const_trait_impl): revert to only a for loop
|
||||
fn rt<T: Hash, H: Hasher>(data: &[T], state: &mut H) {
|
||||
for piece in data {
|
||||
piece.hash(state);
|
||||
piece.hash(state)
|
||||
}
|
||||
}
|
||||
const fn ct<T: ~const Hash, H: ~const Hasher>(data: &[T], state: &mut H) {
|
||||
let mut i = 0;
|
||||
while i < data.len() {
|
||||
data[i].hash(state);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
// SAFETY: same behavior, CT just uses while instead of for
|
||||
unsafe { const_eval_select((data, state), ct, rt) };
|
||||
}
|
||||
}
|
||||
|
||||
// Separate module to reexport the macro `Hash` from prelude without the trait `Hash`.
|
||||
|
@ -313,6 +327,7 @@ pub use macros::Hash;
|
|||
/// [`write_u8`]: Hasher::write_u8
|
||||
/// [`write_u32`]: Hasher::write_u32
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[const_trait]
|
||||
pub trait Hasher {
|
||||
/// Returns the hash value for the values written so far.
|
||||
///
|
||||
|
@ -558,7 +573,8 @@ pub trait Hasher {
|
|||
}
|
||||
|
||||
#[stable(feature = "indirect_hasher_impl", since = "1.22.0")]
|
||||
impl<H: Hasher + ?Sized> Hasher for &mut H {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl<H: ~const Hasher + ?Sized> const Hasher for &mut H {
|
||||
fn finish(&self) -> u64 {
|
||||
(**self).finish()
|
||||
}
|
||||
|
@ -638,6 +654,7 @@ impl<H: Hasher + ?Sized> Hasher for &mut H {
|
|||
/// [`build_hasher`]: BuildHasher::build_hasher
|
||||
/// [`HashMap`]: ../../std/collections/struct.HashMap.html
|
||||
#[stable(since = "1.7.0", feature = "build_hasher")]
|
||||
#[const_trait]
|
||||
pub trait BuildHasher {
|
||||
/// Type of the hasher that will be created.
|
||||
#[stable(since = "1.7.0", feature = "build_hasher")]
|
||||
|
@ -698,9 +715,10 @@ pub trait BuildHasher {
|
|||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "build_hasher_simple_hash_one", issue = "86161")]
|
||||
fn hash_one<T: Hash>(&self, x: T) -> u64
|
||||
fn hash_one<T: ~const Hash + ~const Destruct>(&self, x: T) -> u64
|
||||
where
|
||||
Self: Sized,
|
||||
Self::Hasher: ~const Hasher + ~const Destruct,
|
||||
{
|
||||
let mut hasher = self.build_hasher();
|
||||
x.hash(&mut hasher);
|
||||
|
@ -764,7 +782,8 @@ impl<H> fmt::Debug for BuildHasherDefault<H> {
|
|||
}
|
||||
|
||||
#[stable(since = "1.7.0", feature = "build_hasher")]
|
||||
impl<H: Default + Hasher> BuildHasher for BuildHasherDefault<H> {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl<H: ~const Default + Hasher> const BuildHasher for BuildHasherDefault<H> {
|
||||
type Hasher = H;
|
||||
|
||||
fn build_hasher(&self) -> H {
|
||||
|
@ -806,14 +825,15 @@ mod impls {
|
|||
macro_rules! impl_write {
|
||||
($(($ty:ident, $meth:ident),)*) => {$(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Hash for $ty {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hash for $ty {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
state.$meth(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hash_slice<H: Hasher>(data: &[$ty], state: &mut H) {
|
||||
fn hash_slice<H: ~const Hasher>(data: &[$ty], state: &mut H) {
|
||||
let newlen = data.len() * mem::size_of::<$ty>();
|
||||
let ptr = data.as_ptr() as *const u8;
|
||||
// SAFETY: `ptr` is valid and aligned, as this macro is only used
|
||||
|
@ -842,33 +862,37 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Hash for bool {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hash for bool {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
state.write_u8(*self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Hash for char {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hash for char {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
state.write_u32(*self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Hash for str {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hash for str {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
state.write_str(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "never_hash", since = "1.29.0")]
|
||||
impl Hash for ! {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hash for ! {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, _: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, _: &mut H) {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
@ -876,9 +900,10 @@ mod impls {
|
|||
macro_rules! impl_hash_tuple {
|
||||
() => (
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Hash for () {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hash for () {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
fn hash<H: ~const Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -886,10 +911,11 @@ mod impls {
|
|||
maybe_tuple_doc! {
|
||||
$($name)+ @
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl<$($name: ~const Hash),+> const Hash for ($($name,)+) where last_type!($($name,)+): ?Sized {
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn hash<S: Hasher>(&self, state: &mut S) {
|
||||
fn hash<S: ~const Hasher>(&self, state: &mut S) {
|
||||
let ($(ref $name,)+) = *self;
|
||||
$($name.hash(state);)+
|
||||
}
|
||||
|
@ -932,24 +958,27 @@ mod impls {
|
|||
impl_hash_tuple! { T B C D E F G H I J K L }
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: Hash> Hash for [T] {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl<T: ~const Hash> const Hash for [T] {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
state.write_length_prefix(self.len());
|
||||
Hash::hash_slice(self, state)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized + Hash> Hash for &T {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl<T: ?Sized + ~const Hash> const Hash for &T {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized + Hash> Hash for &mut T {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl<T: ?Sized + ~const Hash> const Hash for &mut T {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state);
|
||||
|
|
|
@ -118,7 +118,7 @@ macro_rules! load_int_le {
|
|||
/// Safety: this performs unchecked indexing of `buf` at `start..start+len`, so
|
||||
/// that must be in-bounds.
|
||||
#[inline]
|
||||
unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
|
||||
const unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
|
||||
debug_assert!(len < 8);
|
||||
let mut i = 0; // current byte index (from LSB) in the output u64
|
||||
let mut out = 0;
|
||||
|
@ -138,7 +138,8 @@ unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
|
|||
out |= (unsafe { *buf.get_unchecked(start + i) } as u64) << (i * 8);
|
||||
i += 1;
|
||||
}
|
||||
debug_assert_eq!(i, len);
|
||||
//FIXME(fee1-dead): use debug_assert_eq
|
||||
debug_assert!(i == len);
|
||||
out
|
||||
}
|
||||
|
||||
|
@ -150,8 +151,9 @@ impl SipHasher {
|
|||
since = "1.13.0",
|
||||
note = "use `std::collections::hash_map::DefaultHasher` instead"
|
||||
)]
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
#[must_use]
|
||||
pub fn new() -> SipHasher {
|
||||
pub const fn new() -> SipHasher {
|
||||
SipHasher::new_with_keys(0, 0)
|
||||
}
|
||||
|
||||
|
@ -162,8 +164,9 @@ impl SipHasher {
|
|||
since = "1.13.0",
|
||||
note = "use `std::collections::hash_map::DefaultHasher` instead"
|
||||
)]
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
#[must_use]
|
||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher {
|
||||
pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher {
|
||||
SipHasher(SipHasher24 { hasher: Hasher::new_with_keys(key0, key1) })
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +179,8 @@ impl SipHasher13 {
|
|||
since = "1.13.0",
|
||||
note = "use `std::collections::hash_map::DefaultHasher` instead"
|
||||
)]
|
||||
pub fn new() -> SipHasher13 {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
pub const fn new() -> SipHasher13 {
|
||||
SipHasher13::new_with_keys(0, 0)
|
||||
}
|
||||
|
||||
|
@ -187,14 +191,15 @@ impl SipHasher13 {
|
|||
since = "1.13.0",
|
||||
note = "use `std::collections::hash_map::DefaultHasher` instead"
|
||||
)]
|
||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 {
|
||||
SipHasher13 { hasher: Hasher::new_with_keys(key0, key1) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Sip> Hasher<S> {
|
||||
#[inline]
|
||||
fn new_with_keys(key0: u64, key1: u64) -> Hasher<S> {
|
||||
const fn new_with_keys(key0: u64, key1: u64) -> Hasher<S> {
|
||||
let mut state = Hasher {
|
||||
k0: key0,
|
||||
k1: key1,
|
||||
|
@ -209,7 +214,7 @@ impl<S: Sip> Hasher<S> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn reset(&mut self) {
|
||||
const fn reset(&mut self) {
|
||||
self.length = 0;
|
||||
self.state.v0 = self.k0 ^ 0x736f6d6570736575;
|
||||
self.state.v1 = self.k1 ^ 0x646f72616e646f6d;
|
||||
|
@ -220,7 +225,8 @@ impl<S: Sip> Hasher<S> {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl super::Hasher for SipHasher {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const super::Hasher for SipHasher {
|
||||
#[inline]
|
||||
fn write(&mut self, msg: &[u8]) {
|
||||
self.0.hasher.write(msg)
|
||||
|
@ -238,7 +244,8 @@ impl super::Hasher for SipHasher {
|
|||
}
|
||||
|
||||
#[unstable(feature = "hashmap_internals", issue = "none")]
|
||||
impl super::Hasher for SipHasher13 {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const super::Hasher for SipHasher13 {
|
||||
#[inline]
|
||||
fn write(&mut self, msg: &[u8]) {
|
||||
self.hasher.write(msg)
|
||||
|
@ -255,7 +262,7 @@ impl super::Hasher for SipHasher13 {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: Sip> super::Hasher for Hasher<S> {
|
||||
impl<S: ~const Sip> const super::Hasher for Hasher<S> {
|
||||
// Note: no integer hashing methods (`write_u*`, `write_i*`) are defined
|
||||
// for this type. We could add them, copy the `short_write` implementation
|
||||
// in librustc_data_structures/sip128.rs, and add `write_u*`/`write_i*`
|
||||
|
@ -335,7 +342,7 @@ impl<S: Sip> super::Hasher for Hasher<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: Sip> Clone for Hasher<S> {
|
||||
impl<S: Sip> const Clone for Hasher<S> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Hasher<S> {
|
||||
Hasher {
|
||||
|
@ -359,6 +366,7 @@ impl<S: Sip> Default for Hasher<S> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[const_trait]
|
||||
trait Sip {
|
||||
fn c_rounds(_: &mut State);
|
||||
fn d_rounds(_: &mut State);
|
||||
|
@ -367,7 +375,7 @@ trait Sip {
|
|||
#[derive(Debug, Clone, Default)]
|
||||
struct Sip13Rounds;
|
||||
|
||||
impl Sip for Sip13Rounds {
|
||||
impl const Sip for Sip13Rounds {
|
||||
#[inline]
|
||||
fn c_rounds(state: &mut State) {
|
||||
compress!(state);
|
||||
|
@ -384,7 +392,7 @@ impl Sip for Sip13Rounds {
|
|||
#[derive(Debug, Clone, Default)]
|
||||
struct Sip24Rounds;
|
||||
|
||||
impl Sip for Sip24Rounds {
|
||||
impl const Sip for Sip24Rounds {
|
||||
#[inline]
|
||||
fn c_rounds(state: &mut State) {
|
||||
compress!(state);
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
#![feature(const_float_bits_conv)]
|
||||
#![feature(const_float_classify)]
|
||||
#![feature(const_fmt_arguments_new)]
|
||||
#![feature(const_hash)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_convert)]
|
||||
#![feature(const_index_range_slice_index)]
|
||||
|
|
|
@ -9,16 +9,19 @@ struct MyHasher {
|
|||
hash: u64,
|
||||
}
|
||||
|
||||
impl Default for MyHasher {
|
||||
impl const Default for MyHasher {
|
||||
fn default() -> MyHasher {
|
||||
MyHasher { hash: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hasher for MyHasher {
|
||||
impl const Hasher for MyHasher {
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
for byte in buf {
|
||||
self.hash += *byte as u64;
|
||||
// FIXME(const_trait_impl): change to for loop
|
||||
let mut i = 0;
|
||||
while i < buf.len() {
|
||||
self.hash += buf[i] as u64;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
fn write_str(&mut self, s: &str) {
|
||||
|
@ -32,12 +35,25 @@ impl Hasher for MyHasher {
|
|||
|
||||
#[test]
|
||||
fn test_writer_hasher() {
|
||||
fn hash<T: Hash>(t: &T) -> u64 {
|
||||
const fn hash<T: ~const Hash>(t: &T) -> u64 {
|
||||
let mut s = MyHasher { hash: 0 };
|
||||
t.hash(&mut s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
const {
|
||||
// FIXME(fee1-dead): assert_eq
|
||||
assert!(hash(&()) == 0);
|
||||
assert!(hash(&5_u8) == 5);
|
||||
assert!(hash(&5_u16) == 5);
|
||||
assert!(hash(&5_u32) == 5);
|
||||
|
||||
assert!(hash(&'a') == 97);
|
||||
|
||||
let s: &str = "a";
|
||||
assert!(hash(&s) == 97 + 0xFF);
|
||||
};
|
||||
|
||||
assert_eq!(hash(&()), 0);
|
||||
|
||||
assert_eq!(hash(&5_u8), 5);
|
||||
|
@ -97,7 +113,7 @@ struct CustomHasher {
|
|||
output: u64,
|
||||
}
|
||||
|
||||
impl Hasher for CustomHasher {
|
||||
impl const Hasher for CustomHasher {
|
||||
fn finish(&self) -> u64 {
|
||||
self.output
|
||||
}
|
||||
|
@ -109,27 +125,29 @@ impl Hasher for CustomHasher {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for CustomHasher {
|
||||
impl const Default for CustomHasher {
|
||||
fn default() -> CustomHasher {
|
||||
CustomHasher { output: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Custom {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
impl const Hash for Custom {
|
||||
fn hash<H: ~const Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.hash);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_state() {
|
||||
fn hash<T: Hash>(t: &T) -> u64 {
|
||||
const fn hash<T: ~const Hash>(t: &T) -> u64 {
|
||||
let mut c = CustomHasher { output: 0 };
|
||||
t.hash(&mut c);
|
||||
c.finish()
|
||||
}
|
||||
|
||||
assert_eq!(hash(&Custom { hash: 5 }), 5);
|
||||
|
||||
const { assert!(hash(&Custom { hash: 6 }) == 6) };
|
||||
}
|
||||
|
||||
// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten.
|
||||
|
|
|
@ -8,7 +8,6 @@ use core::{mem, slice};
|
|||
struct Bytes<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> Hash for Bytes<'a> {
|
||||
#[allow(unused_must_use)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let Bytes(v) = *self;
|
||||
state.write(v);
|
||||
|
@ -24,6 +23,20 @@ fn hash<T: Hash>(x: &T) -> u64 {
|
|||
hash_with(SipHasher::new(), x)
|
||||
}
|
||||
|
||||
#[test]
|
||||
const fn test_const_sip() {
|
||||
let val1 = 0x45;
|
||||
let val2 = 0xfeed;
|
||||
|
||||
const fn const_hash<T: ~const Hash>(x: &T) -> u64 {
|
||||
let mut st = SipHasher::new();
|
||||
x.hash(&mut st);
|
||||
st.finish()
|
||||
}
|
||||
|
||||
assert!(const_hash(&(val1)) != const_hash(&(val2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(unused_must_use)]
|
||||
fn test_siphash_1_3() {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#![feature(const_caller_location)]
|
||||
#![feature(const_cell_into_inner)]
|
||||
#![feature(const_convert)]
|
||||
#![feature(const_hash)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_maybe_uninit_assume_init_read)]
|
||||
|
|
|
@ -3161,14 +3161,16 @@ impl DefaultHasher {
|
|||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
||||
#[inline]
|
||||
#[allow(deprecated)]
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
#[must_use]
|
||||
pub fn new() -> DefaultHasher {
|
||||
pub const fn new() -> DefaultHasher {
|
||||
DefaultHasher(SipHasher13::new_with_keys(0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
||||
impl Default for DefaultHasher {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Default for DefaultHasher {
|
||||
/// Creates a new `DefaultHasher` using [`new`].
|
||||
/// See its documentation for more.
|
||||
///
|
||||
|
@ -3180,7 +3182,8 @@ impl Default for DefaultHasher {
|
|||
}
|
||||
|
||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
||||
impl Hasher for DefaultHasher {
|
||||
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
|
||||
impl const Hasher for DefaultHasher {
|
||||
// The underlying `SipHasher13` doesn't override the other
|
||||
// `write_*` methods, so it's ok not to forward them here.
|
||||
|
||||
|
|
|
@ -352,6 +352,7 @@
|
|||
//
|
||||
// Only for const-ness:
|
||||
#![feature(const_collections_with_hasher)]
|
||||
#![feature(const_hash)]
|
||||
#![feature(const_io_structs)]
|
||||
#![feature(const_ip)]
|
||||
#![feature(const_ipv4)]
|
||||
|
|
|
@ -27,7 +27,7 @@ trait Copy { }
|
|||
//x86_64: define win64cc void @has_efiapi
|
||||
//i686: define void @has_efiapi
|
||||
//aarch64: define dso_local void @has_efiapi
|
||||
//arm: define dso_local void @has_efiapi
|
||||
//arm: define dso_local arm_aapcscc void @has_efiapi
|
||||
//riscv: define dso_local void @has_efiapi
|
||||
#[no_mangle]
|
||||
pub extern "efiapi" fn has_efiapi() {}
|
||||
|
|
|
@ -22,6 +22,10 @@ LL | f(2);
|
|||
| ^^^^ expected an `Fn<(i32,)>` closure, found `dyn Fn(i32) -> i32`
|
||||
|
|
||||
= help: the trait `Fn<(i32,)>` is not implemented for `dyn Fn(i32) -> i32`
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn main() where dyn Fn(i32) -> i32: Fn<(i32,)> {
|
||||
| ++++++++++++++++++++++++++++++++++++
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
6
src/test/ui/consts/invalid-const-in-body.rs
Normal file
6
src/test/ui/consts/invalid-const-in-body.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn f() -> impl Sized {
|
||||
2.0E
|
||||
//~^ ERROR expected at least one digit in exponent
|
||||
}
|
||||
|
||||
fn main() {}
|
8
src/test/ui/consts/invalid-const-in-body.stderr
Normal file
8
src/test/ui/consts/invalid-const-in-body.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: expected at least one digit in exponent
|
||||
--> $DIR/invalid-const-in-body.rs:2:5
|
||||
|
|
||||
LL | 2.0E
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
30
src/test/ui/suggestions/issue-104086-suggest-let.rs
Normal file
30
src/test/ui/suggestions/issue-104086-suggest-let.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
fn main() {
|
||||
x = x = x;
|
||||
//~^ ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `x` in this scope
|
||||
|
||||
x = y = y = y;
|
||||
//~^ ERROR cannot find value `y` in this scope
|
||||
//~| ERROR cannot find value `y` in this scope
|
||||
//~| ERROR cannot find value `y` in this scope
|
||||
//~| ERROR cannot find value `x` in this scope
|
||||
|
||||
x = y = y;
|
||||
//~^ ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `y` in this scope
|
||||
//~| ERROR cannot find value `y` in this scope
|
||||
|
||||
x = x = y;
|
||||
//~^ ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `y` in this scope
|
||||
|
||||
x = x; // will suggest add `let`
|
||||
//~^ ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `x` in this scope
|
||||
|
||||
x = y // will suggest add `let`
|
||||
//~^ ERROR cannot find value `x` in this scope
|
||||
//~| ERROR cannot find value `y` in this scope
|
||||
}
|
135
src/test/ui/suggestions/issue-104086-suggest-let.stderr
Normal file
135
src/test/ui/suggestions/issue-104086-suggest-let.stderr
Normal file
|
@ -0,0 +1,135 @@
|
|||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:2:5
|
||||
|
|
||||
LL | x = x = x;
|
||||
| ^
|
||||
|
|
||||
help: you might have meant to introduce a new binding
|
||||
|
|
||||
LL | let x = x = x;
|
||||
| +++
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:2:9
|
||||
|
|
||||
LL | x = x = x;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:2:13
|
||||
|
|
||||
LL | x = x = x;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:7:5
|
||||
|
|
||||
LL | x = y = y = y;
|
||||
| ^
|
||||
|
|
||||
help: you might have meant to introduce a new binding
|
||||
|
|
||||
LL | let x = y = y = y;
|
||||
| +++
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:7:9
|
||||
|
|
||||
LL | x = y = y = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:7:13
|
||||
|
|
||||
LL | x = y = y = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:7:17
|
||||
|
|
||||
LL | x = y = y = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:13:5
|
||||
|
|
||||
LL | x = y = y;
|
||||
| ^
|
||||
|
|
||||
help: you might have meant to introduce a new binding
|
||||
|
|
||||
LL | let x = y = y;
|
||||
| +++
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:13:9
|
||||
|
|
||||
LL | x = y = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:13:13
|
||||
|
|
||||
LL | x = y = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:18:5
|
||||
|
|
||||
LL | x = x = y;
|
||||
| ^
|
||||
|
|
||||
help: you might have meant to introduce a new binding
|
||||
|
|
||||
LL | let x = x = y;
|
||||
| +++
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:18:9
|
||||
|
|
||||
LL | x = x = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:18:13
|
||||
|
|
||||
LL | x = x = y;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:23:5
|
||||
|
|
||||
LL | x = x; // will suggest add `let`
|
||||
| ^
|
||||
|
|
||||
help: you might have meant to introduce a new binding
|
||||
|
|
||||
LL | let x = x; // will suggest add `let`
|
||||
| +++
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:23:9
|
||||
|
|
||||
LL | x = x; // will suggest add `let`
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0425]: cannot find value `x` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:27:5
|
||||
|
|
||||
LL | x = y // will suggest add `let`
|
||||
| ^
|
||||
|
|
||||
help: you might have meant to introduce a new binding
|
||||
|
|
||||
LL | let x = y // will suggest add `let`
|
||||
| +++
|
||||
|
||||
error[E0425]: cannot find value `y` in this scope
|
||||
--> $DIR/issue-104086-suggest-let.rs:27:9
|
||||
|
|
||||
LL | x = y // will suggest add `let`
|
||||
| ^ not found in this scope
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
9
src/test/ui/suggestions/option-to-bool.rs
Normal file
9
src/test/ui/suggestions/option-to-bool.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#![cfg_attr(let_chains, feature(let_chains))]
|
||||
|
||||
fn foo(x: Option<i32>) {
|
||||
if true && x {}
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP use `Option::is_some` to test if the `Option` has a value
|
||||
}
|
||||
|
||||
fn main() {}
|
16
src/test/ui/suggestions/option-to-bool.stderr
Normal file
16
src/test/ui/suggestions/option-to-bool.stderr
Normal file
|
@ -0,0 +1,16 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/option-to-bool.rs:4:16
|
||||
|
|
||||
LL | if true && x {}
|
||||
| ^ expected `bool`, found enum `Option`
|
||||
|
|
||||
= note: expected type `bool`
|
||||
found enum `Option<i32>`
|
||||
help: use `Option::is_some` to test if the `Option` has a value
|
||||
|
|
||||
LL | if true && x.is_some() {}
|
||||
| ++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Add a link
Reference in a new issue