Auto merge of #107290 - Dylan-DPC:rollup-tovojhr, r=Dylan-DPC
Rollup of 9 pull requests Successful merges: - #105552 (Add help message about function pointers) - #106583 (Suggest coercion of `Result` using `?`) - #106767 (Allow setting CFG_DISABLE_UNSTABLE_FEATURES to 0) - #106823 (Allow fmt::Arguments::as_str() to return more Some(_).) - #107166 (rustc_metadata: Support non-`Option` nullable values in metadata tables) - #107213 (Add suggestion to remove if in let..else block) - #107223 (`sub_ptr()` is equivalent to `usize::try_from().unwrap_unchecked()`, not `usize::from().unwrap_unchecked()`) - #107227 (`new_outside_solver` -> `evaluate_root_goal`) - #107232 (rustdoc: simplify settings popover DOM, CSS, JS) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
95b61d16d4
42 changed files with 752 additions and 284 deletions
|
@ -238,6 +238,7 @@ parse_const_let_mutually_exclusive = `const` and `let` are mutually exclusive
|
|||
|
||||
parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
|
||||
parse_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed
|
||||
parse_extra_if_in_let_else = remove the `if` if you meant to write a `let...else` statement
|
||||
|
||||
parse_compound_assignment_expression_in_let = can't reassign to an uninitialized variable
|
||||
.suggestion = initialize the variable
|
||||
|
|
|
@ -83,7 +83,8 @@ impl UnstableFeatures {
|
|||
/// Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
|
||||
pub fn from_environment(krate: Option<&str>) -> Self {
|
||||
// `true` if this is a feature-staged build, i.e., on the beta or stable channel.
|
||||
let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
|
||||
let disable_unstable_features =
|
||||
option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
|
||||
// Returns whether `krate` should be counted as unstable
|
||||
let is_unstable_crate = |var: &str| {
|
||||
krate.map_or(false, |name| var.split(',').any(|new_krate| new_krate == name))
|
||||
|
|
|
@ -1613,12 +1613,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
|||
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
|
||||
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
|
||||
}
|
||||
|
||||
let reported = err.emit_unless(unsized_return);
|
||||
|
||||
self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn note_unreachable_loop_return(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
|
|
@ -59,7 +59,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|
||||
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|
||||
|| self.suggest_into(err, expr, expr_ty, expected)
|
||||
|| self.suggest_floating_point_literal(err, expr, expected);
|
||||
|| self.suggest_floating_point_literal(err, expr, expected)
|
||||
|| self.note_result_coercion(err, expr, expected, expr_ty);
|
||||
if !suggested {
|
||||
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
|
||||
}
|
||||
|
@ -81,7 +82,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.annotate_expected_due_to_let_ty(err, expr, error);
|
||||
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
|
||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
||||
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
|
||||
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
|
||||
|
@ -697,6 +697,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
pub(crate) fn note_result_coercion(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let ty::Adt(e, substs_e) = expected.kind() else { return false; };
|
||||
let ty::Adt(f, substs_f) = found.kind() else { return false; };
|
||||
if e.did() != f.did() {
|
||||
return false;
|
||||
}
|
||||
if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) {
|
||||
return false;
|
||||
}
|
||||
let map = self.tcx.hir();
|
||||
if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id)
|
||||
&& let hir::ExprKind::Ret(_) = expr.kind
|
||||
{
|
||||
// `return foo;`
|
||||
} else if map.get_return_block(expr.hir_id).is_some() {
|
||||
// Function's tail expression.
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
let e = substs_e.type_at(1);
|
||||
let f = substs_f.type_at(1);
|
||||
if self
|
||||
.infcx
|
||||
.type_implements_trait(
|
||||
self.tcx.get_diagnostic_item(sym::Into).unwrap(),
|
||||
[f, e],
|
||||
self.param_env,
|
||||
)
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
|
||||
in `Ok` so the expression remains of type `Result`",
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "Ok(".to_string()),
|
||||
(expr.span.shrink_to_hi(), "?)".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// If the expected type is an enum (Issue #55250) with any variants whose
|
||||
/// sole field is of the found type, suggest such variants. (Issue #42764)
|
||||
fn suggest_compatible_variants(
|
||||
|
|
|
@ -926,43 +926,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn note_need_for_fn_pointer(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
|
||||
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
||||
let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
|
||||
let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
|
||||
if sig1 != sig2 {
|
||||
return;
|
||||
}
|
||||
err.note(
|
||||
"different `fn` items always have unique types, even if their signatures are \
|
||||
the same",
|
||||
);
|
||||
(sig1, *did1, substs1)
|
||||
}
|
||||
(ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
|
||||
let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
|
||||
if sig1 != *sig2 {
|
||||
return;
|
||||
}
|
||||
(sig1, *did, substs)
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
err.help(&format!("change the expected type to be function pointer `{}`", sig));
|
||||
err.help(&format!(
|
||||
"if the expected type is due to type inference, cast the expected `fn` to a function \
|
||||
pointer: `{} as {}`",
|
||||
self.tcx.def_path_str_with_substs(did, substs),
|
||||
sig
|
||||
));
|
||||
}
|
||||
|
||||
// Instantiates the given path, which must refer to an item with the given
|
||||
// number of type parameters and type.
|
||||
#[instrument(skip(self, span), level = "debug")]
|
||||
|
|
|
@ -1841,6 +1841,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
|
||||
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
|
||||
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
|
||||
self.suggest_function_pointers(cause, span, &exp_found, diag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2585,7 +2586,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
|
||||
/// FloatVar inference type are compatible with themselves or their concrete types (Int and
|
||||
/// Float types, respectively). When comparing two ADTs, these rules apply recursively.
|
||||
pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
|
||||
pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
|
||||
let (a, b) = self.resolve_vars_if_possible((a, b));
|
||||
SameTypeModuloInfer(self).relate(a, b).is_ok()
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_middle::traits::{
|
|||
StatementAsExpression,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
|
||||
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
|
||||
use crate::errors::SuggAddLetForLetChains;
|
||||
|
@ -351,6 +351,82 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_function_pointers(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
|
||||
let ty::error::ExpectedFound { expected, found } = exp_found;
|
||||
let expected_inner = expected.peel_refs();
|
||||
let found_inner = found.peel_refs();
|
||||
if !expected_inner.is_fn() || !found_inner.is_fn() {
|
||||
return;
|
||||
}
|
||||
match (&expected_inner.kind(), &found_inner.kind()) {
|
||||
(ty::FnPtr(sig), ty::FnDef(did, substs)) => {
|
||||
let expected_sig = &(self.normalize_fn_sig)(*sig);
|
||||
let found_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did).subst(self.tcx, substs));
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
|
||||
|
||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
||||
|| !sig.is_suggestable(self.tcx, true)
|
||||
|| ty::util::is_intrinsic(self.tcx, *did)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let (msg, sugg) = match (expected.is_ref(), found.is_ref()) {
|
||||
(true, false) => {
|
||||
let msg = "consider using a reference";
|
||||
let sug = format!("&{fn_name}");
|
||||
(msg, sug)
|
||||
}
|
||||
(false, true) => {
|
||||
let msg = "consider removing the reference";
|
||||
let sug = format!("{fn_name}");
|
||||
(msg, sug)
|
||||
}
|
||||
(true, true) => {
|
||||
diag.note("fn items are distinct from fn pointers");
|
||||
let msg = "consider casting to a fn pointer";
|
||||
let sug = format!("&({fn_name} as {sig})");
|
||||
(msg, sug)
|
||||
}
|
||||
(false, false) => {
|
||||
diag.note("fn items are distinct from fn pointers");
|
||||
let msg = "consider casting to a fn pointer";
|
||||
let sug = format!("{fn_name} as {sig}");
|
||||
(msg, sug)
|
||||
}
|
||||
};
|
||||
diag.span_suggestion(span, msg, &sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
||||
let expected_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1));
|
||||
let found_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2));
|
||||
|
||||
if self.same_type_modulo_infer(*found_sig, *expected_sig) {
|
||||
diag.note(
|
||||
"different fn items have unique types, even if their signatures are the same",
|
||||
);
|
||||
}
|
||||
}
|
||||
(ty::FnDef(_, _), ty::FnPtr(_)) => {
|
||||
diag.note("fn items are distinct from fn pointers");
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
||||
(expected.kind(), found.kind())
|
||||
|
|
|
@ -985,7 +985,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
let vis = self.get_visibility(id);
|
||||
let span = self.get_span(id, sess);
|
||||
let macro_rules = match kind {
|
||||
DefKind::Macro(..) => self.root.tables.macro_rules.get(self, id).is_some(),
|
||||
DefKind::Macro(..) => self.root.tables.is_macro_rules.get(self, id),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
@ -1283,7 +1283,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
|
||||
match self.def_kind(id) {
|
||||
DefKind::Macro(_) => {
|
||||
let macro_rules = self.root.tables.macro_rules.get(self, id).is_some();
|
||||
let macro_rules = self.root.tables.is_macro_rules.get(self, id);
|
||||
let body =
|
||||
self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess));
|
||||
ast::MacroDef { macro_rules, body: ast::ptr::P(body) }
|
||||
|
@ -1595,11 +1595,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
}
|
||||
|
||||
fn get_attr_flags(self, index: DefIndex) -> AttrFlags {
|
||||
self.root.tables.attr_flags.get(self, index).unwrap_or(AttrFlags::empty())
|
||||
self.root.tables.attr_flags.get(self, index)
|
||||
}
|
||||
|
||||
fn get_is_intrinsic(self, index: DefIndex) -> bool {
|
||||
self.root.tables.is_intrinsic.get(self, index).is_some()
|
||||
self.root.tables.is_intrinsic.get(self, index)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -226,12 +226,7 @@ provide! { tcx, def_id, other, cdata,
|
|||
deduced_param_attrs => { table }
|
||||
is_type_alias_impl_trait => {
|
||||
debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy);
|
||||
cdata
|
||||
.root
|
||||
.tables
|
||||
.is_type_alias_impl_trait
|
||||
.get(cdata, def_id.index)
|
||||
.is_some()
|
||||
cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index)
|
||||
}
|
||||
collect_return_position_impl_trait_in_trait_tys => {
|
||||
Ok(cdata
|
||||
|
|
|
@ -483,7 +483,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map()))
|
||||
}
|
||||
|
||||
fn encode_source_map(&mut self) -> LazyTable<u32, LazyValue<rustc_span::SourceFile>> {
|
||||
fn encode_source_map(&mut self) -> LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>> {
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let all_source_files = source_map.files();
|
||||
|
||||
|
@ -1130,7 +1130,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
|
||||
}
|
||||
if !attr_flags.is_empty() {
|
||||
self.tables.attr_flags.set(def_id.local_def_index, attr_flags);
|
||||
self.tables.attr_flags.set_nullable(def_id.local_def_index, attr_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1387,7 +1387,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
if impl_item.kind == ty::AssocKind::Fn {
|
||||
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
|
||||
if tcx.is_intrinsic(def_id) {
|
||||
self.tables.is_intrinsic.set(def_id.index, ());
|
||||
self.tables.is_intrinsic.set_nullable(def_id.index, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1519,7 +1519,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
if macro_def.macro_rules {
|
||||
self.tables.macro_rules.set(def_id.index, ());
|
||||
self.tables.is_macro_rules.set_nullable(def_id.index, true);
|
||||
}
|
||||
record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
|
||||
}
|
||||
|
@ -1529,7 +1529,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
hir::ItemKind::OpaqueTy(ref opaque) => {
|
||||
self.encode_explicit_item_bounds(def_id);
|
||||
if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) {
|
||||
self.tables.is_type_alias_impl_trait.set(def_id.index, ());
|
||||
self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Enum(..) => {
|
||||
|
@ -1636,7 +1636,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
if let hir::ItemKind::Fn(..) = item.kind {
|
||||
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
|
||||
if tcx.is_intrinsic(def_id) {
|
||||
self.tables.is_intrinsic.set(def_id.index, ());
|
||||
self.tables.is_intrinsic.set_nullable(def_id.index, true);
|
||||
}
|
||||
}
|
||||
if let hir::ItemKind::Impl { .. } = item.kind {
|
||||
|
@ -2038,7 +2038,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
|
||||
if tcx.is_intrinsic(def_id) {
|
||||
self.tables.is_intrinsic.set(def_id.index, ());
|
||||
self.tables.is_intrinsic.set_nullable(def_id.index, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,9 +185,9 @@ enum LazyState {
|
|||
Previous(NonZeroUsize),
|
||||
}
|
||||
|
||||
type SyntaxContextTable = LazyTable<u32, LazyValue<SyntaxContextData>>;
|
||||
type ExpnDataTable = LazyTable<ExpnIndex, LazyValue<ExpnData>>;
|
||||
type ExpnHashTable = LazyTable<ExpnIndex, LazyValue<ExpnHash>>;
|
||||
type SyntaxContextTable = LazyTable<u32, Option<LazyValue<SyntaxContextData>>>;
|
||||
type ExpnDataTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnData>>>;
|
||||
type ExpnHashTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnHash>>>;
|
||||
|
||||
#[derive(MetadataEncodable, MetadataDecodable)]
|
||||
pub(crate) struct ProcMacroData {
|
||||
|
@ -253,7 +253,7 @@ pub(crate) struct CrateRoot {
|
|||
|
||||
def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>,
|
||||
|
||||
source_map: LazyTable<u32, LazyValue<rustc_span::SourceFile>>,
|
||||
source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
|
||||
|
||||
compiler_builtins: bool,
|
||||
needs_allocator: bool,
|
||||
|
@ -315,21 +315,27 @@ pub(crate) struct IncoherentImpls {
|
|||
|
||||
/// Define `LazyTables` and `TableBuilders` at the same time.
|
||||
macro_rules! define_tables {
|
||||
($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => {
|
||||
(
|
||||
- nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
|
||||
- optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+
|
||||
) => {
|
||||
#[derive(MetadataEncodable, MetadataDecodable)]
|
||||
pub(crate) struct LazyTables {
|
||||
$($name: LazyTable<$IDX, $T>),+
|
||||
$($name1: LazyTable<$IDX1, $T1>,)+
|
||||
$($name2: LazyTable<$IDX2, Option<$T2>>,)+
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TableBuilders {
|
||||
$($name: TableBuilder<$IDX, $T>),+
|
||||
$($name1: TableBuilder<$IDX1, $T1>,)+
|
||||
$($name2: TableBuilder<$IDX2, Option<$T2>>,)+
|
||||
}
|
||||
|
||||
impl TableBuilders {
|
||||
fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
|
||||
LazyTables {
|
||||
$($name: self.$name.encode(buf)),+
|
||||
$($name1: self.$name1.encode(buf),)+
|
||||
$($name2: self.$name2.encode(buf),)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,9 +343,15 @@ macro_rules! define_tables {
|
|||
}
|
||||
|
||||
define_tables! {
|
||||
- nullable:
|
||||
is_intrinsic: Table<DefIndex, bool>,
|
||||
is_macro_rules: Table<DefIndex, bool>,
|
||||
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
||||
attr_flags: Table<DefIndex, AttrFlags>,
|
||||
|
||||
- optional:
|
||||
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
|
||||
children: Table<DefIndex, LazyArray<DefIndex>>,
|
||||
|
||||
opt_def_kind: Table<DefIndex, DefKind>,
|
||||
visibility: Table<DefIndex, LazyValue<ty::Visibility<DefIndex>>>,
|
||||
def_span: Table<DefIndex, LazyValue<Span>>,
|
||||
|
@ -370,7 +382,6 @@ define_tables! {
|
|||
impl_parent: Table<DefIndex, RawDefId>,
|
||||
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
|
||||
constness: Table<DefIndex, hir::Constness>,
|
||||
is_intrinsic: Table<DefIndex, ()>,
|
||||
impl_defaultness: Table<DefIndex, hir::Defaultness>,
|
||||
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
|
||||
coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,
|
||||
|
@ -380,7 +391,6 @@ define_tables! {
|
|||
fn_arg_names: Table<DefIndex, LazyArray<Ident>>,
|
||||
generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
|
||||
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
|
||||
|
||||
trait_item_def_id: Table<DefIndex, RawDefId>,
|
||||
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
|
||||
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
|
||||
|
@ -395,18 +405,12 @@ define_tables! {
|
|||
def_path_hashes: Table<DefIndex, DefPathHash>,
|
||||
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
|
||||
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
|
||||
attr_flags: Table<DefIndex, AttrFlags>,
|
||||
variant_data: Table<DefIndex, LazyValue<VariantData>>,
|
||||
assoc_container: Table<DefIndex, ty::AssocItemContainer>,
|
||||
// Slot is full when macro is macro_rules.
|
||||
macro_rules: Table<DefIndex, ()>,
|
||||
macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
|
||||
proc_macro: Table<DefIndex, MacroKind>,
|
||||
module_reexports: Table<DefIndex, LazyArray<ModChild>>,
|
||||
deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>,
|
||||
// Slot is full when opaque is TAIT.
|
||||
is_type_alias_impl_trait: Table<DefIndex, ()>,
|
||||
|
||||
trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, Ty<'static>>>>,
|
||||
}
|
||||
|
||||
|
@ -419,6 +423,7 @@ struct VariantData {
|
|||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct AttrFlags: u8 {
|
||||
const MAY_HAVE_DOC_LINKS = 1 << 0;
|
||||
const IS_DOC_HIDDEN = 1 << 1;
|
||||
|
|
|
@ -16,6 +16,7 @@ use std::num::NonZeroUsize;
|
|||
/// but this has no impact on safety.
|
||||
pub(super) trait FixedSizeEncoding: Default {
|
||||
/// This should be `[u8; BYTE_LEN]`;
|
||||
/// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
|
||||
type ByteArray;
|
||||
|
||||
fn from_bytes(b: &Self::ByteArray) -> Self;
|
||||
|
@ -199,31 +200,31 @@ impl FixedSizeEncoding for Option<RawDefId> {
|
|||
}
|
||||
}
|
||||
|
||||
impl FixedSizeEncoding for Option<AttrFlags> {
|
||||
impl FixedSizeEncoding for AttrFlags {
|
||||
type ByteArray = [u8; 1];
|
||||
|
||||
#[inline]
|
||||
fn from_bytes(b: &[u8; 1]) -> Self {
|
||||
(b[0] != 0).then(|| AttrFlags::from_bits_truncate(b[0]))
|
||||
AttrFlags::from_bits_truncate(b[0])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 1]) {
|
||||
b[0] = self.map_or(0, |flags| flags.bits())
|
||||
b[0] = self.bits();
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedSizeEncoding for Option<()> {
|
||||
impl FixedSizeEncoding for bool {
|
||||
type ByteArray = [u8; 1];
|
||||
|
||||
#[inline]
|
||||
fn from_bytes(b: &[u8; 1]) -> Self {
|
||||
(b[0] != 0).then(|| ())
|
||||
b[0] != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 1]) {
|
||||
b[0] = self.is_some() as u8
|
||||
b[0] = self as u8
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,44 +274,38 @@ impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
|
|||
}
|
||||
|
||||
/// Helper for constructing a table's serialization (also see `Table`).
|
||||
pub(super) struct TableBuilder<I: Idx, T>
|
||||
where
|
||||
Option<T>: FixedSizeEncoding,
|
||||
{
|
||||
blocks: IndexVec<I, <Option<T> as FixedSizeEncoding>::ByteArray>,
|
||||
pub(super) struct TableBuilder<I: Idx, T: FixedSizeEncoding> {
|
||||
blocks: IndexVec<I, T::ByteArray>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<I: Idx, T> Default for TableBuilder<I, T>
|
||||
where
|
||||
Option<T>: FixedSizeEncoding,
|
||||
{
|
||||
impl<I: Idx, T: FixedSizeEncoding> Default for TableBuilder<I, T> {
|
||||
fn default() -> Self {
|
||||
TableBuilder { blocks: Default::default(), _marker: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Idx, T> TableBuilder<I, T>
|
||||
impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
|
||||
where
|
||||
Option<T>: FixedSizeEncoding,
|
||||
Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
pub(crate) fn set<const N: usize>(&mut self, i: I, value: T)
|
||||
where
|
||||
Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
pub(crate) fn set(&mut self, i: I, value: T) {
|
||||
self.set_nullable(i, Some(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
|
||||
pub(crate) fn set_nullable(&mut self, i: I, value: T) {
|
||||
// FIXME(eddyb) investigate more compact encodings for sparse tables.
|
||||
// On the PR @michaelwoerister mentioned:
|
||||
// > Space requirements could perhaps be optimized by using the HAMT `popcnt`
|
||||
// > trick (i.e. divide things into buckets of 32 or 64 items and then
|
||||
// > store bit-masks of which item in each bucket is actually serialized).
|
||||
self.blocks.ensure_contains_elem(i, || [0; N]);
|
||||
Some(value).write_to_bytes(&mut self.blocks[i]);
|
||||
value.write_to_bytes(&mut self.blocks[i]);
|
||||
}
|
||||
|
||||
pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T>
|
||||
where
|
||||
Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
|
||||
let pos = buf.position();
|
||||
for block in &self.blocks {
|
||||
buf.emit_raw_bytes(block);
|
||||
|
@ -323,34 +318,27 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I: Idx, T: ParameterizedOverTcx> LazyTable<I, T>
|
||||
impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]> + ParameterizedOverTcx>
|
||||
LazyTable<I, T>
|
||||
where
|
||||
Option<T>: FixedSizeEncoding,
|
||||
for<'tcx> T::Value<'tcx>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
/// Given the metadata, extract out the value at a particular index (if any).
|
||||
#[inline(never)]
|
||||
pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>, const N: usize>(
|
||||
&self,
|
||||
metadata: M,
|
||||
i: I,
|
||||
) -> Option<T::Value<'tcx>>
|
||||
where
|
||||
Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
|
||||
debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
|
||||
|
||||
let start = self.position.get();
|
||||
let bytes = &metadata.blob()[start..start + self.encoded_size];
|
||||
let (bytes, []) = bytes.as_chunks::<N>() else { panic!() };
|
||||
let bytes = bytes.get(i.index())?;
|
||||
FixedSizeEncoding::from_bytes(bytes)
|
||||
match bytes.get(i.index()) {
|
||||
Some(bytes) => FixedSizeEncoding::from_bytes(bytes),
|
||||
None => FixedSizeEncoding::from_bytes(&[0; N]),
|
||||
}
|
||||
}
|
||||
|
||||
/// Size of the table in entries, including possible gaps.
|
||||
pub(super) fn size<const N: usize>(&self) -> usize
|
||||
where
|
||||
for<'tcx> Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
pub(super) fn size(&self) -> usize {
|
||||
self.encoded_size / N
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,7 +337,9 @@ pub(crate) struct IfExpressionMissingThenBlock {
|
|||
#[primary_span]
|
||||
pub if_span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sub: IfExpressionMissingThenBlockSub,
|
||||
pub missing_then_block_sub: IfExpressionMissingThenBlockSub,
|
||||
#[subdiagnostic]
|
||||
pub let_else_sub: Option<IfExpressionLetSomeSub>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
@ -348,6 +350,13 @@ pub(crate) enum IfExpressionMissingThenBlockSub {
|
|||
AddThenBlock(#[primary_span] Span),
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(parse_extra_if_in_let_else)]
|
||||
pub(crate) struct IfExpressionLetSomeSub {
|
||||
#[primary_span]
|
||||
pub if_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_if_expression_missing_condition)]
|
||||
pub(crate) struct IfExpressionMissingCondition {
|
||||
|
|
|
@ -11,15 +11,15 @@ use crate::errors::{
|
|||
ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
|
||||
ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet,
|
||||
FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
|
||||
IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
|
||||
InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
|
||||
InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
|
||||
InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
|
||||
LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
|
||||
MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
|
||||
MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
|
||||
NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
|
||||
OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
|
||||
IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock,
|
||||
IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator,
|
||||
InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex,
|
||||
InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported,
|
||||
LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath,
|
||||
MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg,
|
||||
MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub,
|
||||
MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator,
|
||||
NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
|
||||
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
|
||||
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
|
||||
UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
|
||||
|
@ -2251,9 +2251,10 @@ impl<'a> Parser<'a> {
|
|||
if let ExprKind::Block(_, None) = right.kind => {
|
||||
self.sess.emit_err(IfExpressionMissingThenBlock {
|
||||
if_span: lo,
|
||||
sub: IfExpressionMissingThenBlockSub::UnfinishedCondition(
|
||||
cond_span.shrink_to_lo().to(*binop_span)
|
||||
),
|
||||
missing_then_block_sub:
|
||||
IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)),
|
||||
let_else_sub: None,
|
||||
|
||||
});
|
||||
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
|
||||
},
|
||||
|
@ -2279,9 +2280,15 @@ impl<'a> Parser<'a> {
|
|||
if let Some(block) = recover_block_from_condition(self) {
|
||||
block
|
||||
} else {
|
||||
let let_else_sub = matches!(cond.kind, ExprKind::Let(..))
|
||||
.then(|| IfExpressionLetSomeSub { if_span: lo });
|
||||
|
||||
self.sess.emit_err(IfExpressionMissingThenBlock {
|
||||
if_span: lo,
|
||||
sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()),
|
||||
missing_then_block_sub: IfExpressionMissingThenBlockSub::AddThenBlock(
|
||||
cond_span.shrink_to_hi(),
|
||||
),
|
||||
let_else_sub,
|
||||
});
|
||||
self.mk_block_err(cond_span.shrink_to_hi())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::mem;
|
||||
|
||||
use super::{Certainty, InferCtxtEvalExt};
|
||||
use rustc_infer::{
|
||||
infer::InferCtxt,
|
||||
traits::{
|
||||
|
@ -8,8 +9,6 @@ use rustc_infer::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::{search_graph, Certainty, EvalCtxt};
|
||||
|
||||
/// A trait engine using the new trait solver.
|
||||
///
|
||||
/// This is mostly identical to how `evaluate_all` works inside of the
|
||||
|
@ -66,9 +65,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
|||
let mut has_changed = false;
|
||||
for obligation in mem::take(&mut self.obligations) {
|
||||
let goal = obligation.clone().into();
|
||||
let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx);
|
||||
let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph);
|
||||
let (changed, certainty) = match ecx.evaluate_goal(goal) {
|
||||
let (changed, certainty) = match infcx.evaluate_root_goal(goal) {
|
||||
Ok(result) => result,
|
||||
Err(NoSolution) => {
|
||||
errors.push(FulfillmentError {
|
||||
|
|
|
@ -152,6 +152,36 @@ impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait InferCtxtEvalExt<'tcx> {
|
||||
/// Evaluates a goal from **outside** of the trait solver.
|
||||
///
|
||||
/// Using this while inside of the solver is wrong as it uses a new
|
||||
/// search graph which would break cycle detection.
|
||||
fn evaluate_root_goal(
|
||||
&self,
|
||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||
) -> Result<(bool, Certainty), NoSolution>;
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||
fn evaluate_root_goal(
|
||||
&self,
|
||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||
) -> Result<(bool, Certainty), NoSolution> {
|
||||
let mut search_graph = search_graph::SearchGraph::new(self.tcx);
|
||||
|
||||
let result = EvalCtxt {
|
||||
search_graph: &mut search_graph,
|
||||
infcx: self,
|
||||
var_values: CanonicalVarValues::dummy(),
|
||||
}
|
||||
.evaluate_goal(goal);
|
||||
|
||||
assert!(search_graph.is_empty());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
struct EvalCtxt<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
var_values: CanonicalVarValues<'tcx>,
|
||||
|
@ -164,18 +194,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
|||
self.infcx.tcx
|
||||
}
|
||||
|
||||
/// Creates a new evaluation context outside of the trait solver.
|
||||
///
|
||||
/// With this solver making a canonical response doesn't make much sense.
|
||||
/// The `search_graph` for this solver has to be completely empty.
|
||||
fn new_outside_solver(
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
|
||||
) -> EvalCtxt<'a, 'tcx> {
|
||||
assert!(search_graph.is_empty());
|
||||
EvalCtxt { infcx, var_values: CanonicalVarValues::dummy(), search_graph }
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx, search_graph), ret)]
|
||||
fn evaluate_canonical_goal(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
@ -489,9 +489,26 @@ pub struct Arguments<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Arguments<'a> {
|
||||
/// Get the formatted string, if it has no arguments to be formatted.
|
||||
/// Get the formatted string, if it has no arguments to be formatted at runtime.
|
||||
///
|
||||
/// This can be used to avoid allocations in the most trivial case.
|
||||
/// This can be used to avoid allocations in some cases.
|
||||
///
|
||||
/// # Guarantees
|
||||
///
|
||||
/// For `format_args!("just a literal")`, this function is guaranteed to
|
||||
/// return `Some("just a literal")`.
|
||||
///
|
||||
/// For most cases with placeholders, this function will return `None`.
|
||||
///
|
||||
/// However, the compiler may perform optimizations that can cause this
|
||||
/// function to return `Some(_)` even if the format string contains
|
||||
/// placeholders. For example, `format_args!("Hello, {}!", "world")` may be
|
||||
/// optimized to `format_args!("Hello, world!")`, such that `as_str()`
|
||||
/// returns `Some("Hello, world!")`.
|
||||
///
|
||||
/// The behavior for anything but the trivial case (without placeholders)
|
||||
/// is not guaranteed, and should not be relied upon for anything other
|
||||
/// than optimization.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -512,7 +529,7 @@ impl<'a> Arguments<'a> {
|
|||
/// ```rust
|
||||
/// assert_eq!(format_args!("hello").as_str(), Some("hello"));
|
||||
/// assert_eq!(format_args!("").as_str(), Some(""));
|
||||
/// assert_eq!(format_args!("{}", 1).as_str(), None);
|
||||
/// assert_eq!(format_args!("{:?}", std::env::current_dir()).as_str(), None);
|
||||
/// ```
|
||||
#[stable(feature = "fmt_as_str", since = "1.52.0")]
|
||||
#[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
|
||||
|
|
|
@ -731,7 +731,7 @@ impl<T: ?Sized> *const T {
|
|||
/// This computes the same value that [`offset_from`](#method.offset_from)
|
||||
/// would compute, but with the added precondition that the offset is
|
||||
/// guaranteed to be non-negative. This method is equivalent to
|
||||
/// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
|
||||
/// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
|
||||
/// but it provides slightly more information to the optimizer, which can
|
||||
/// sometimes allow it to optimize slightly better with some backends.
|
||||
///
|
||||
|
|
|
@ -904,7 +904,7 @@ impl<T: ?Sized> *mut T {
|
|||
/// This computes the same value that [`offset_from`](#method.offset_from)
|
||||
/// would compute, but with the added precondition that the offset is
|
||||
/// guaranteed to be non-negative. This method is equivalent to
|
||||
/// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
|
||||
/// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
|
||||
/// but it provides slightly more information to the optimizer, which can
|
||||
/// sometimes allow it to optimize slightly better with some backends.
|
||||
///
|
||||
|
|
|
@ -309,7 +309,8 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
|||
// FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
|
||||
fn is_nightly() -> bool {
|
||||
// Whether this is a feature-staged build, i.e., on the beta or stable channel
|
||||
let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
|
||||
let disable_unstable_features =
|
||||
option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
|
||||
// Whether we should enable unstable features for bootstrapping
|
||||
let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.setting-line .radio-line input,
|
||||
.setting-line .settings-toggle input {
|
||||
.setting-radio input, .setting-check input {
|
||||
margin-right: 0.3em;
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
|
@ -14,21 +13,20 @@
|
|||
-webkit-appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.setting-line .radio-line input {
|
||||
.setting-radio input {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.setting-line .settings-toggle input:checked {
|
||||
.setting-check input:checked {
|
||||
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
|
||||
<path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
|
||||
<path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
|
||||
}
|
||||
|
||||
.setting-line .radio-line input + span,
|
||||
.setting-line .settings-toggle span {
|
||||
.setting-radio span, .setting-check span {
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.radio-line .choice {
|
||||
.setting-radio {
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0.1em;
|
||||
min-width: 3.8em;
|
||||
|
@ -37,11 +35,11 @@
|
|||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.radio-line .choice + .choice {
|
||||
.setting-radio + .setting-radio {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.settings-toggle {
|
||||
.setting-check {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-right: 20px;
|
||||
|
@ -50,23 +48,21 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.setting-line .radio-line input:checked {
|
||||
.setting-radio input:checked {
|
||||
box-shadow: inset 0 0 0 3px var(--main-background-color);
|
||||
background-color: var(--settings-input-color);
|
||||
}
|
||||
.setting-line .settings-toggle input:checked {
|
||||
.setting-check input:checked {
|
||||
background-color: var(--settings-input-color);
|
||||
}
|
||||
.setting-line .radio-line input:focus,
|
||||
.setting-line .settings-toggle input:focus {
|
||||
.setting-radio input:focus, .setting-check input:focus {
|
||||
box-shadow: 0 0 1px 1px var(--settings-input-color);
|
||||
}
|
||||
/* In here we combine both `:focus` and `:checked` properties. */
|
||||
.setting-line .radio-line input:checked:focus {
|
||||
.setting-radio input:checked:focus {
|
||||
box-shadow: inset 0 0 0 3px var(--main-background-color),
|
||||
0 0 2px 2px var(--settings-input-color);
|
||||
}
|
||||
.setting-line .radio-line input:hover,
|
||||
.setting-line .settings-toggle input:hover {
|
||||
.setting-radio input:hover, .setting-check input:hover {
|
||||
border-color: var(--settings-input-color) !important;
|
||||
}
|
||||
|
|
|
@ -48,13 +48,13 @@
|
|||
}
|
||||
|
||||
function showLightAndDark() {
|
||||
removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
|
||||
removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
|
||||
removeClass(document.getElementById("preferred-light-theme"), "hidden");
|
||||
removeClass(document.getElementById("preferred-dark-theme"), "hidden");
|
||||
}
|
||||
|
||||
function hideLightAndDark() {
|
||||
addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
|
||||
addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
|
||||
addClass(document.getElementById("preferred-light-theme"), "hidden");
|
||||
addClass(document.getElementById("preferred-dark-theme"), "hidden");
|
||||
}
|
||||
|
||||
function updateLightAndDark() {
|
||||
|
@ -80,17 +80,6 @@
|
|||
toggle.onkeyup = handleKey;
|
||||
toggle.onkeyrelease = handleKey;
|
||||
});
|
||||
onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => {
|
||||
const select = elem.getElementsByTagName("select")[0];
|
||||
const settingId = select.id;
|
||||
const settingValue = getSettingValue(settingId);
|
||||
if (settingValue !== null) {
|
||||
select.value = settingValue;
|
||||
}
|
||||
select.onchange = function() {
|
||||
changeSetting(this.id, this.value);
|
||||
};
|
||||
});
|
||||
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
|
||||
const settingId = elem.name;
|
||||
let settingValue = getSettingValue(settingId);
|
||||
|
@ -127,38 +116,40 @@
|
|||
let output = "";
|
||||
|
||||
for (const setting of settings) {
|
||||
output += "<div class=\"setting-line\">";
|
||||
const js_data_name = setting["js_name"];
|
||||
const setting_name = setting["name"];
|
||||
|
||||
if (setting["options"] !== undefined) {
|
||||
// This is a select setting.
|
||||
output += `\
|
||||
<div class="radio-line" id="${js_data_name}">
|
||||
<div class="setting-name">${setting_name}</div>
|
||||
<div class="choices">`;
|
||||
<div class="setting-line" id="${js_data_name}">
|
||||
<div class="setting-radio-name">${setting_name}</div>
|
||||
<div class="setting-radio-choices">`;
|
||||
onEach(setting["options"], option => {
|
||||
const checked = option === setting["default"] ? " checked" : "";
|
||||
const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
|
||||
|
||||
output += `\
|
||||
<label for="${full}" class="choice">
|
||||
<input type="radio" name="${js_data_name}"
|
||||
id="${full}" value="${option}"${checked}>
|
||||
<span>${option}</span>
|
||||
</label>`;
|
||||
<label for="${full}" class="setting-radio">
|
||||
<input type="radio" name="${js_data_name}"
|
||||
id="${full}" value="${option}"${checked}>
|
||||
<span>${option}</span>
|
||||
</label>`;
|
||||
});
|
||||
output += "</div></div>";
|
||||
output += `\
|
||||
</div>
|
||||
</div>`;
|
||||
} else {
|
||||
// This is a checkbox toggle.
|
||||
const checked = setting["default"] === true ? " checked" : "";
|
||||
output += `\
|
||||
<label class="settings-toggle">\
|
||||
<input type="checkbox" id="${js_data_name}"${checked}>\
|
||||
<span class="label">${setting_name}</span>\
|
||||
</label>`;
|
||||
<div class="setting-line">\
|
||||
<label class="setting-check">\
|
||||
<input type="checkbox" id="${js_data_name}"${checked}>\
|
||||
<span>${setting_name}</span>\
|
||||
</label>\
|
||||
</div>`;
|
||||
}
|
||||
output += "</div>";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ goto: "file://" + |DOC_PATH| + "/settings.html"
|
|||
size: (400, 600)
|
||||
// Ignored for now https://github.com/rust-lang/rust/issues/93784.
|
||||
// compare-elements-position-near-false: (
|
||||
// "#preferred-light-theme .setting-name",
|
||||
// "#preferred-light-theme .choice",
|
||||
// "#preferred-light-theme .setting-radio-name",
|
||||
// "#preferred-light-theme .setting-radio",
|
||||
// {"y": 16},
|
||||
// )
|
||||
|
|
|
@ -43,12 +43,12 @@ wait-for: "#settings"
|
|||
// We check that the "Use system theme" is disabled.
|
||||
assert-property: ("#theme-system-preference", {"checked": "false"})
|
||||
// Meaning that only the "theme" menu is showing up.
|
||||
assert: ".setting-line:not(.hidden) #theme"
|
||||
assert: ".setting-line.hidden #preferred-dark-theme"
|
||||
assert: ".setting-line.hidden #preferred-light-theme"
|
||||
assert: "#theme.setting-line:not(.hidden)"
|
||||
assert: "#preferred-dark-theme.setting-line.hidden"
|
||||
assert: "#preferred-light-theme.setting-line.hidden"
|
||||
|
||||
// We check that the correct theme is selected.
|
||||
assert-property: ("#theme .choices #theme-dark", {"checked": "true"})
|
||||
assert-property: ("#theme .setting-radio-choices #theme-dark", {"checked": "true"})
|
||||
|
||||
// Some style checks...
|
||||
move-cursor-to: "#settings-menu > a"
|
||||
|
@ -109,31 +109,31 @@ assert-css: (
|
|||
"box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
|
||||
},
|
||||
)
|
||||
// Now we check the setting-name for radio buttons is on a different line than the label.
|
||||
// Now we check the setting-radio-name is on a different line than the label.
|
||||
compare-elements-position-near: (
|
||||
"#theme .setting-name",
|
||||
"#theme .choices",
|
||||
"#theme .setting-radio-name",
|
||||
"#theme .setting-radio-choices",
|
||||
{"x": 1}
|
||||
)
|
||||
compare-elements-position-near-false: (
|
||||
"#theme .setting-name",
|
||||
"#theme .choices",
|
||||
"#theme .setting-radio-name",
|
||||
"#theme .setting-radio-choices",
|
||||
{"y": 1}
|
||||
)
|
||||
// Now we check that the label positions are all on the same line.
|
||||
compare-elements-position-near: (
|
||||
"#theme .choices #theme-light",
|
||||
"#theme .choices #theme-dark",
|
||||
"#theme .setting-radio-choices #theme-light",
|
||||
"#theme .setting-radio-choices #theme-dark",
|
||||
{"y": 1}
|
||||
)
|
||||
compare-elements-position-near: (
|
||||
"#theme .choices #theme-dark",
|
||||
"#theme .choices #theme-ayu",
|
||||
"#theme .setting-radio-choices #theme-dark",
|
||||
"#theme .setting-radio-choices #theme-ayu",
|
||||
{"y": 1}
|
||||
)
|
||||
compare-elements-position-near: (
|
||||
"#theme .choices #theme-ayu",
|
||||
"#theme .choices #theme-system-preference",
|
||||
"#theme .setting-radio-choices #theme-ayu",
|
||||
"#theme .setting-radio-choices #theme-system-preference",
|
||||
{"y": 1}
|
||||
)
|
||||
|
||||
|
@ -180,17 +180,17 @@ assert-css: (
|
|||
// We now switch the display.
|
||||
click: "#theme-system-preference"
|
||||
// Wait for the hidden element to show up.
|
||||
wait-for: ".setting-line:not(.hidden) #preferred-dark-theme"
|
||||
assert: ".setting-line:not(.hidden) #preferred-light-theme"
|
||||
wait-for: "#preferred-dark-theme.setting-line:not(.hidden)"
|
||||
assert: "#preferred-light-theme.setting-line:not(.hidden)"
|
||||
|
||||
// We check their text as well.
|
||||
assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
|
||||
assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
|
||||
assert-text: ("#preferred-dark-theme .setting-radio-name", "Preferred dark theme")
|
||||
assert-text: ("#preferred-light-theme .setting-radio-name", "Preferred light theme")
|
||||
|
||||
// We now check that clicking on the toggles' text is like clicking on the checkbox.
|
||||
// To test it, we use the "Disable keyboard shortcuts".
|
||||
local-storage: {"rustdoc-disable-shortcuts": "false"}
|
||||
click: ".setting-line:last-child .settings-toggle .label"
|
||||
click: ".setting-line:last-child .setting-check span"
|
||||
assert-local-storage: {"rustdoc-disable-shortcuts": "true"}
|
||||
|
||||
// Make sure that "Disable keyboard shortcuts" actually took effect.
|
||||
|
@ -200,7 +200,7 @@ assert-false: "#help-button .popover"
|
|||
wait-for-css: ("#settings-menu .popover", {"display": "block"})
|
||||
|
||||
// Now turn keyboard shortcuts back on, and see if they work.
|
||||
click: ".setting-line:last-child .settings-toggle .label"
|
||||
click: ".setting-line:last-child .setting-check span"
|
||||
assert-local-storage: {"rustdoc-disable-shortcuts": "false"}
|
||||
press-key: "Escape"
|
||||
press-key: "?"
|
||||
|
|
|
@ -43,7 +43,7 @@ assert-local-storage: { "rustdoc-theme": "ayu" }
|
|||
|
||||
assert-local-storage-false: { "rustdoc-use-system-theme": "true" }
|
||||
click: "#theme-system-preference"
|
||||
wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
|
||||
wait-for: "#preferred-light-theme.setting-line:not(.hidden)"
|
||||
assert-local-storage: { "rustdoc-use-system-theme": "true" }
|
||||
// We click on both preferred light and dark themes to be sure that there is a change.
|
||||
click: "#preferred-light-theme-dark"
|
||||
|
@ -52,16 +52,16 @@ wait-for-css: ("body", { "background-color": |background_dark| })
|
|||
|
||||
reload:
|
||||
// Ensure that the "preferred themes" are still displayed.
|
||||
wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
|
||||
wait-for: "#preferred-light-theme.setting-line:not(.hidden)"
|
||||
click: "#theme-light"
|
||||
wait-for-css: ("body", { "background-color": |background_light| })
|
||||
assert-local-storage: { "rustdoc-theme": "light" }
|
||||
// Ensure it's now hidden again
|
||||
wait-for: ".setting-line.hidden #preferred-light-theme"
|
||||
wait-for: "#preferred-light-theme.setting-line.hidden"
|
||||
// And ensure the theme was rightly set.
|
||||
wait-for-css: ("body", { "background-color": |background_light| })
|
||||
assert-local-storage: { "rustdoc-theme": "light" }
|
||||
|
||||
reload:
|
||||
wait-for: "#settings"
|
||||
assert: ".setting-line.hidden #preferred-light-theme"
|
||||
assert: "#preferred-light-theme.setting-line.hidden"
|
||||
|
|
|
@ -19,6 +19,7 @@ LL | let x = f == g;
|
|||
|
|
||||
= note: expected fn item `fn() {f}`
|
||||
found fn item `fn() {g}`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
// Test that the types of distinct fn items are not compatible by
|
||||
// default. See also `run-pass/fn-item-type-*.rs`.
|
||||
|
||||
fn foo<T>(x: isize) -> isize { x * 2 }
|
||||
fn bar<T>(x: isize) -> isize { x * 4 }
|
||||
fn foo<T>(x: isize) -> isize {
|
||||
x * 2
|
||||
}
|
||||
fn bar<T>(x: isize) -> isize {
|
||||
x * 4
|
||||
}
|
||||
|
||||
fn eq<T>(x: T, y: T) { }
|
||||
fn eq<T>(x: T, y: T) {}
|
||||
|
||||
trait Foo { fn foo() { /* this is a default fn */ } }
|
||||
impl<T> Foo for T { /* `foo` is still default here */ }
|
||||
trait Foo {
|
||||
fn foo() { /* this is a default fn */
|
||||
}
|
||||
}
|
||||
impl<T> Foo for T {
|
||||
/* `foo` is still default here */
|
||||
}
|
||||
|
||||
fn main() {
|
||||
eq(foo::<u8>, bar::<u8>);
|
||||
|
@ -15,39 +24,29 @@ fn main() {
|
|||
//~| expected fn item `fn(_) -> _ {foo::<u8>}`
|
||||
//~| found fn item `fn(_) -> _ {bar::<u8>}`
|
||||
//~| expected fn item, found a different fn item
|
||||
//~| different `fn` items always have unique types, even if their signatures are the same
|
||||
//~| change the expected type to be function pointer
|
||||
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
|
||||
eq(foo::<u8>, foo::<i8>);
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected `u8`, found `i8`
|
||||
//~| different `fn` items always have unique types, even if their signatures are the same
|
||||
//~| change the expected type to be function pointer
|
||||
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
|
||||
eq(bar::<String>, bar::<Vec<u8>>);
|
||||
//~^ ERROR mismatched types
|
||||
//~| found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
|
||||
//~| expected struct `String`, found struct `Vec`
|
||||
//~| different `fn` items always have unique types, even if their signatures are the same
|
||||
//~| change the expected type to be function pointer
|
||||
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
|
||||
// Make sure we distinguish between trait methods correctly.
|
||||
eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected `u8`, found `u16`
|
||||
//~| different `fn` items always have unique types, even if their signatures are the same
|
||||
//~| change the expected type to be function pointer
|
||||
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
|
||||
eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
|
||||
//~^ ERROR mismatched types
|
||||
//~| found fn pointer `fn(_) -> _`
|
||||
//~| expected fn item, found fn pointer
|
||||
//~| change the expected type to be function pointer
|
||||
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
|
||||
|
||||
eq(foo::<u8> as fn(isize) -> isize, bar::<u8>); // ok!
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-item-type.rs:13:19
|
||||
--> $DIR/fn-item-type.rs:22:19
|
||||
|
|
||||
LL | eq(foo::<u8>, bar::<u8>);
|
||||
| -- ^^^^^^^^^ expected fn item, found a different fn item
|
||||
|
@ -8,17 +8,15 @@ LL | eq(foo::<u8>, bar::<u8>);
|
|||
|
|
||||
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
|
||||
found fn item `fn(_) -> _ {bar::<u8>}`
|
||||
= note: different `fn` items always have unique types, even if their signatures are the same
|
||||
= help: change the expected type to be function pointer `fn(isize) -> isize`
|
||||
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
note: function defined here
|
||||
--> $DIR/fn-item-type.rs:7:4
|
||||
--> $DIR/fn-item-type.rs:11:4
|
||||
|
|
||||
LL | fn eq<T>(x: T, y: T) { }
|
||||
LL | fn eq<T>(x: T, y: T) {}
|
||||
| ^^ ----
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-item-type.rs:22:19
|
||||
--> $DIR/fn-item-type.rs:29:19
|
||||
|
|
||||
LL | eq(foo::<u8>, foo::<i8>);
|
||||
| -- ^^^^^^^^^ expected `u8`, found `i8`
|
||||
|
@ -27,17 +25,15 @@ LL | eq(foo::<u8>, foo::<i8>);
|
|||
|
|
||||
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
|
||||
found fn item `fn(_) -> _ {foo::<i8>}`
|
||||
= note: different `fn` items always have unique types, even if their signatures are the same
|
||||
= help: change the expected type to be function pointer `fn(isize) -> isize`
|
||||
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
note: function defined here
|
||||
--> $DIR/fn-item-type.rs:7:4
|
||||
--> $DIR/fn-item-type.rs:11:4
|
||||
|
|
||||
LL | fn eq<T>(x: T, y: T) { }
|
||||
LL | fn eq<T>(x: T, y: T) {}
|
||||
| ^^ ----
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-item-type.rs:29:23
|
||||
--> $DIR/fn-item-type.rs:34:23
|
||||
|
|
||||
LL | eq(bar::<String>, bar::<Vec<u8>>);
|
||||
| -- ^^^^^^^^^^^^^^ expected struct `String`, found struct `Vec`
|
||||
|
@ -46,17 +42,15 @@ LL | eq(bar::<String>, bar::<Vec<u8>>);
|
|||
|
|
||||
= note: expected fn item `fn(_) -> _ {bar::<String>}`
|
||||
found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
|
||||
= note: different `fn` items always have unique types, even if their signatures are the same
|
||||
= help: change the expected type to be function pointer `fn(isize) -> isize`
|
||||
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar::<String> as fn(isize) -> isize`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
note: function defined here
|
||||
--> $DIR/fn-item-type.rs:7:4
|
||||
--> $DIR/fn-item-type.rs:11:4
|
||||
|
|
||||
LL | fn eq<T>(x: T, y: T) { }
|
||||
LL | fn eq<T>(x: T, y: T) {}
|
||||
| ^^ ----
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-item-type.rs:38:26
|
||||
--> $DIR/fn-item-type.rs:41:26
|
||||
|
|
||||
LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
|
||||
| -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
|
||||
|
@ -65,17 +59,15 @@ LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
|
|||
|
|
||||
= note: expected fn item `fn() {<u8 as Foo>::foo}`
|
||||
found fn item `fn() {<u16 as Foo>::foo}`
|
||||
= note: different `fn` items always have unique types, even if their signatures are the same
|
||||
= help: change the expected type to be function pointer `fn()`
|
||||
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
note: function defined here
|
||||
--> $DIR/fn-item-type.rs:7:4
|
||||
--> $DIR/fn-item-type.rs:11:4
|
||||
|
|
||||
LL | fn eq<T>(x: T, y: T) { }
|
||||
LL | fn eq<T>(x: T, y: T) {}
|
||||
| ^^ ----
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-item-type.rs:45:19
|
||||
--> $DIR/fn-item-type.rs:46:19
|
||||
|
|
||||
LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
|
||||
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
||||
|
@ -84,12 +76,11 @@ LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
|
|||
|
|
||||
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
|
||||
found fn pointer `fn(_) -> _`
|
||||
= help: change the expected type to be function pointer `fn(isize) -> isize`
|
||||
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
|
||||
= note: fn items are distinct from fn pointers
|
||||
note: function defined here
|
||||
--> $DIR/fn-item-type.rs:7:4
|
||||
--> $DIR/fn-item-type.rs:11:4
|
||||
|
|
||||
LL | fn eq<T>(x: T, y: T) { }
|
||||
LL | fn eq<T>(x: T, y: T) {}
|
||||
| ^^ ----
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
|
56
tests/ui/fn/fn-pointer-mismatch.rs
Normal file
56
tests/ui/fn/fn-pointer-mismatch.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
fn foo(x: u32) -> u32 {
|
||||
x * 2
|
||||
}
|
||||
|
||||
fn bar(x: u32) -> u32 {
|
||||
x * 3
|
||||
}
|
||||
|
||||
// original example from Issue #102608
|
||||
fn foobar(n: u32) -> u32 {
|
||||
let g = if n % 2 == 0 { &foo } else { &bar };
|
||||
//~^ ERROR `if` and `else` have incompatible types
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
g(n)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(foobar(7), 21);
|
||||
assert_eq!(foobar(8), 16);
|
||||
|
||||
// general mismatch of fn item types
|
||||
let mut a = foo;
|
||||
a = bar;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected fn item `fn(_) -> _ {foo}`
|
||||
//~| found fn item `fn(_) -> _ {bar}`
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
|
||||
// display note even when boxed
|
||||
let mut b = Box::new(foo);
|
||||
b = Box::new(bar);
|
||||
//~^ ERROR mismatched types
|
||||
//~| different fn items have unique types, even if their signatures are the same
|
||||
|
||||
// suggest removing reference
|
||||
let c: fn(u32) -> u32 = &foo;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected fn pointer `fn(u32) -> u32`
|
||||
//~| found reference `&fn(u32) -> u32 {foo}`
|
||||
|
||||
// suggest using reference
|
||||
let d: &fn(u32) -> u32 = foo;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected reference `&fn(u32) -> u32`
|
||||
//~| found fn item `fn(u32) -> u32 {foo}`
|
||||
|
||||
// suggest casting with reference
|
||||
let e: &fn(u32) -> u32 = &foo;
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected reference `&fn(u32) -> u32`
|
||||
//~| found reference `&fn(u32) -> u32 {foo}`
|
||||
|
||||
// OK
|
||||
let mut z: fn(u32) -> u32 = foo as fn(u32) -> u32;
|
||||
z = bar;
|
||||
}
|
81
tests/ui/fn/fn-pointer-mismatch.stderr
Normal file
81
tests/ui/fn/fn-pointer-mismatch.stderr
Normal file
|
@ -0,0 +1,81 @@
|
|||
error[E0308]: `if` and `else` have incompatible types
|
||||
--> $DIR/fn-pointer-mismatch.rs:11:43
|
||||
|
|
||||
LL | let g = if n % 2 == 0 { &foo } else { &bar };
|
||||
| ---- ^^^^ expected fn item, found a different fn item
|
||||
| |
|
||||
| expected because of this
|
||||
|
|
||||
= note: expected reference `&fn(u32) -> u32 {foo}`
|
||||
found reference `&fn(u32) -> u32 {bar}`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-pointer-mismatch.rs:23:9
|
||||
|
|
||||
LL | let mut a = foo;
|
||||
| --- expected due to this value
|
||||
LL | a = bar;
|
||||
| ^^^ expected fn item, found a different fn item
|
||||
|
|
||||
= note: expected fn item `fn(_) -> _ {foo}`
|
||||
found fn item `fn(_) -> _ {bar}`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-pointer-mismatch.rs:31:18
|
||||
|
|
||||
LL | b = Box::new(bar);
|
||||
| -------- ^^^ expected fn item, found a different fn item
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
= note: expected fn item `fn(_) -> _ {foo}`
|
||||
found fn item `fn(_) -> _ {bar}`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
note: associated function defined here
|
||||
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-pointer-mismatch.rs:36:29
|
||||
|
|
||||
LL | let c: fn(u32) -> u32 = &foo;
|
||||
| -------------- ^^^^
|
||||
| | |
|
||||
| | expected fn pointer, found reference
|
||||
| | help: consider removing the reference: `foo`
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected fn pointer `fn(u32) -> u32`
|
||||
found reference `&fn(u32) -> u32 {foo}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-pointer-mismatch.rs:42:30
|
||||
|
|
||||
LL | let d: &fn(u32) -> u32 = foo;
|
||||
| --------------- ^^^
|
||||
| | |
|
||||
| | expected `&fn(u32) -> u32`, found fn item
|
||||
| | help: consider using a reference: `&foo`
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected reference `&fn(u32) -> u32`
|
||||
found fn item `fn(u32) -> u32 {foo}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/fn-pointer-mismatch.rs:48:30
|
||||
|
|
||||
LL | let e: &fn(u32) -> u32 = &foo;
|
||||
| --------------- ^^^^
|
||||
| | |
|
||||
| | expected fn pointer, found fn item
|
||||
| | help: consider casting to a fn pointer: `&(foo as fn(u32) -> u32)`
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected reference `&fn(u32) -> u32`
|
||||
found reference `&fn(u32) -> u32 {foo}`
|
||||
= note: fn items are distinct from fn pointers
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
6
tests/ui/let-else/accidental-if.rs
Normal file
6
tests/ui/let-else/accidental-if.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main() {
|
||||
let x = Some(123);
|
||||
if let Some(y) = x else { //~ ERROR this `if` expression is missing a block
|
||||
return;
|
||||
};
|
||||
}
|
19
tests/ui/let-else/accidental-if.stderr
Normal file
19
tests/ui/let-else/accidental-if.stderr
Normal file
|
@ -0,0 +1,19 @@
|
|||
error: this `if` expression is missing a block after the condition
|
||||
--> $DIR/accidental-if.rs:3:5
|
||||
|
|
||||
LL | if let Some(y) = x else {
|
||||
| ^^
|
||||
|
|
||||
help: add a block here
|
||||
--> $DIR/accidental-if.rs:3:23
|
||||
|
|
||||
LL | if let Some(y) = x else {
|
||||
| ^
|
||||
help: remove the `if` if you meant to write a `let...else` statement
|
||||
--> $DIR/accidental-if.rs:3:5
|
||||
|
|
||||
LL | if let Some(y) = x else {
|
||||
| ^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -23,9 +23,7 @@ LL | std::intrinsics::unlikely,
|
|||
|
|
||||
= note: expected fn item `extern "rust-intrinsic" fn(_) -> _ {likely}`
|
||||
found fn item `extern "rust-intrinsic" fn(_) -> _ {unlikely}`
|
||||
= note: different `fn` items always have unique types, even if their signatures are the same
|
||||
= help: change the expected type to be function pointer `extern "rust-intrinsic" fn(bool) -> bool`
|
||||
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `likely as extern "rust-intrinsic" fn(bool) -> bool`
|
||||
= note: different fn items have unique types, even if their signatures are the same
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ help: add a block here
|
|||
|
|
||||
LL | if let Some(n) = opt else {
|
||||
| ^
|
||||
help: remove the `if` if you meant to write a `let...else` statement
|
||||
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5
|
||||
|
|
||||
LL | if let Some(n) = opt else {
|
||||
| ^^
|
||||
|
||||
error: this `if` expression is missing a block after the condition
|
||||
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5
|
||||
|
|
|
@ -5,12 +5,15 @@ LL | #[target_feature(enable = "sse2")]
|
|||
| ---------------------------------- `#[target_feature]` added here
|
||||
...
|
||||
LL | let foo: fn() = foo;
|
||||
| ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
|
||||
| |
|
||||
| ---- ^^^
|
||||
| | |
|
||||
| | cannot coerce functions with `#[target_feature]` to safe function pointers
|
||||
| | help: consider casting to a fn pointer: `foo as fn()`
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected fn pointer `fn()`
|
||||
found fn item `fn() {foo}`
|
||||
= note: fn items are distinct from fn pointers
|
||||
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
|
@ -5,12 +5,15 @@ LL | #[target_feature(enable = "sse2")]
|
|||
| ---------------------------------- `#[target_feature]` added here
|
||||
...
|
||||
LL | let foo: fn() = foo;
|
||||
| ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
|
||||
| |
|
||||
| ---- ^^^
|
||||
| | |
|
||||
| | cannot coerce functions with `#[target_feature]` to safe function pointers
|
||||
| | help: consider casting to a fn pointer: `foo as fn()`
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected fn pointer `fn()`
|
||||
found fn item `fn() {foo}`
|
||||
= note: fn items are distinct from fn pointers
|
||||
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
|
@ -2,10 +2,14 @@ error[E0308]: mismatched types
|
|||
--> $DIR/static-reference-to-fn-1.rs:17:15
|
||||
|
|
||||
LL | func: &foo,
|
||||
| ^^^^ expected fn pointer, found fn item
|
||||
| ^^^^
|
||||
| |
|
||||
| expected fn pointer, found fn item
|
||||
| help: consider casting to a fn pointer: `&(foo as fn() -> Option<isize>)`
|
||||
|
|
||||
= note: expected reference `&fn() -> Option<isize>`
|
||||
found reference `&fn() -> Option<isize> {foo}`
|
||||
= note: fn items are distinct from fn pointers
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
24
tests/ui/type/type-check/coerce-result-return-value-2.rs
Normal file
24
tests/ui/type/type-check/coerce-result-return-value-2.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
struct A;
|
||||
struct B;
|
||||
impl From<A> for B {
|
||||
fn from(_: A) -> Self { B }
|
||||
}
|
||||
fn foo4(x: Result<(), A>) -> Result<(), B> {
|
||||
match true {
|
||||
true => x, //~ ERROR mismatched types
|
||||
false => x,
|
||||
}
|
||||
}
|
||||
fn foo5(x: Result<(), A>) -> Result<(), B> {
|
||||
match true {
|
||||
true => return x, //~ ERROR mismatched types
|
||||
false => return x,
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
let _ = foo4(Ok(()));
|
||||
let _ = foo5(Ok(()));
|
||||
let _: Result<(), B> = { //~ ERROR mismatched types
|
||||
Err(A);
|
||||
};
|
||||
}
|
47
tests/ui/type/type-check/coerce-result-return-value-2.stderr
Normal file
47
tests/ui/type/type-check/coerce-result-return-value-2.stderr
Normal file
|
@ -0,0 +1,47 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value-2.rs:8:17
|
||||
|
|
||||
LL | fn foo4(x: Result<(), A>) -> Result<(), B> {
|
||||
| ------------- expected `Result<(), B>` because of return type
|
||||
LL | match true {
|
||||
LL | true => x,
|
||||
| ^ expected struct `B`, found struct `A`
|
||||
|
|
||||
= note: expected enum `Result<_, B>`
|
||||
found enum `Result<_, A>`
|
||||
help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
|
||||
|
|
||||
LL | true => Ok(x?),
|
||||
| +++ ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value-2.rs:14:24
|
||||
|
|
||||
LL | fn foo5(x: Result<(), A>) -> Result<(), B> {
|
||||
| ------------- expected `Result<(), B>` because of return type
|
||||
LL | match true {
|
||||
LL | true => return x,
|
||||
| ^ expected struct `B`, found struct `A`
|
||||
|
|
||||
= note: expected enum `Result<_, B>`
|
||||
found enum `Result<_, A>`
|
||||
help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
|
||||
|
|
||||
LL | true => return Ok(x?),
|
||||
| +++ ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value-2.rs:21:28
|
||||
|
|
||||
LL | let _: Result<(), B> = {
|
||||
| ____________________________^
|
||||
LL | | Err(A);
|
||||
LL | | };
|
||||
| |_____^ expected enum `Result`, found `()`
|
||||
|
|
||||
= note: expected enum `Result<(), B>`
|
||||
found unit type `()`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
24
tests/ui/type/type-check/coerce-result-return-value.fixed
Normal file
24
tests/ui/type/type-check/coerce-result-return-value.fixed
Normal file
|
@ -0,0 +1,24 @@
|
|||
// run-rustfix
|
||||
struct A;
|
||||
struct B;
|
||||
impl From<A> for B {
|
||||
fn from(_: A) -> Self { B }
|
||||
}
|
||||
fn foo1(x: Result<(), A>) -> Result<(), B> {
|
||||
Ok(x?) //~ ERROR mismatched types
|
||||
}
|
||||
fn foo2(x: Result<(), A>) -> Result<(), B> {
|
||||
return Ok(x?); //~ ERROR mismatched types
|
||||
}
|
||||
fn foo3(x: Result<(), A>) -> Result<(), B> {
|
||||
if true {
|
||||
Ok(x?) //~ ERROR mismatched types
|
||||
} else {
|
||||
Ok(x?) //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
let _ = foo1(Ok(()));
|
||||
let _ = foo2(Ok(()));
|
||||
let _ = foo3(Ok(()));
|
||||
}
|
24
tests/ui/type/type-check/coerce-result-return-value.rs
Normal file
24
tests/ui/type/type-check/coerce-result-return-value.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
// run-rustfix
|
||||
struct A;
|
||||
struct B;
|
||||
impl From<A> for B {
|
||||
fn from(_: A) -> Self { B }
|
||||
}
|
||||
fn foo1(x: Result<(), A>) -> Result<(), B> {
|
||||
x //~ ERROR mismatched types
|
||||
}
|
||||
fn foo2(x: Result<(), A>) -> Result<(), B> {
|
||||
return x; //~ ERROR mismatched types
|
||||
}
|
||||
fn foo3(x: Result<(), A>) -> Result<(), B> {
|
||||
if true {
|
||||
x //~ ERROR mismatched types
|
||||
} else {
|
||||
x //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
let _ = foo1(Ok(()));
|
||||
let _ = foo2(Ok(()));
|
||||
let _ = foo3(Ok(()));
|
||||
}
|
65
tests/ui/type/type-check/coerce-result-return-value.stderr
Normal file
65
tests/ui/type/type-check/coerce-result-return-value.stderr
Normal file
|
@ -0,0 +1,65 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value.rs:8:5
|
||||
|
|
||||
LL | fn foo1(x: Result<(), A>) -> Result<(), B> {
|
||||
| ------------- expected `Result<(), B>` because of return type
|
||||
LL | x
|
||||
| ^ expected struct `B`, found struct `A`
|
||||
|
|
||||
= note: expected enum `Result<_, B>`
|
||||
found enum `Result<_, A>`
|
||||
help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
|
||||
|
|
||||
LL | Ok(x?)
|
||||
| +++ ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value.rs:11:12
|
||||
|
|
||||
LL | fn foo2(x: Result<(), A>) -> Result<(), B> {
|
||||
| ------------- expected `Result<(), B>` because of return type
|
||||
LL | return x;
|
||||
| ^ expected struct `B`, found struct `A`
|
||||
|
|
||||
= note: expected enum `Result<_, B>`
|
||||
found enum `Result<_, A>`
|
||||
help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
|
||||
|
|
||||
LL | return Ok(x?);
|
||||
| +++ ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value.rs:15:9
|
||||
|
|
||||
LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
|
||||
| ------------- expected `Result<(), B>` because of return type
|
||||
LL | if true {
|
||||
LL | x
|
||||
| ^ expected struct `B`, found struct `A`
|
||||
|
|
||||
= note: expected enum `Result<_, B>`
|
||||
found enum `Result<_, A>`
|
||||
help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
|
||||
|
|
||||
LL | Ok(x?)
|
||||
| +++ ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/coerce-result-return-value.rs:17:9
|
||||
|
|
||||
LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
|
||||
| ------------- expected `Result<(), B>` because of return type
|
||||
...
|
||||
LL | x
|
||||
| ^ expected struct `B`, found struct `A`
|
||||
|
|
||||
= note: expected enum `Result<_, B>`
|
||||
found enum `Result<_, A>`
|
||||
help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
|
||||
|
|
||||
LL | Ok(x?)
|
||||
| +++ ++
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Add a link
Reference in a new issue