1
Fork 0

Auto merge of #114063 - matthiaskrgr:rollup-c90czu6, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #114008 (coverage: Obtain the `__llvm_covfun` section name outside a per-function loop)
 - #114014 (builtin_macros: expect raw strings too)
 - #114043 (docs(LazyLock): add example pass local LazyLock variable to struct)
 - #114051 (Add regression test for invalid "unused const" in method)
 - #114052 (Suggest `{Option,Result}::as_ref()` instead of `cloned()` in some cases)
 - #114058 (Add help for crate arg when crate name is invalid)
 - #114060 (abi: unsized field in union - assert to delay bug )

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-07-25 18:58:30 +00:00
commit 18fa7b9104
31 changed files with 319 additions and 74 deletions

View file

@ -763,7 +763,9 @@ pub trait LayoutCalculator {
let mut size = Size::ZERO;
let only_variant = &variants[FIRST_VARIANT];
for field in only_variant {
assert!(field.0.is_sized());
if field.0.is_unsized() {
self.delay_bug("unsized field in union".to_string());
}
align = align.max(field.align());
max_repr_align = max_repr_align.max(field.max_repr_align());

View file

@ -1345,7 +1345,6 @@ impl Abi {
/// Discard validity range information and allow undef.
pub fn to_union(&self) -> Self {
assert!(self.is_sized());
match *self {
Abi::Scalar(s) => Abi::Scalar(s.to_union()),
Abi::ScalarPair(s1, s2) => Abi::ScalarPair(s1.to_union(), s2.to_union()),

View file

@ -2306,11 +2306,10 @@ mod error {
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) {
if let None = self.tainted_by_errors {
self.tainted_by_errors = Some(
self.tcx
.sess
.delay_span_bug(t.span.clone(), "diagnostic buffered but not emitted"),
)
self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug(
t.span.clone_ignoring_labels(),
"diagnostic buffered but not emitted",
))
}
t.buffer(&mut self.buffered);
}

View file

@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
.suggestion = remove the value
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
.cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead
.other = use `std::env::var("{$var}")` to read the variable at run time
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
.custom = use `std::env::var({$var_expr})` to read the variable at run time
builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments

View file

@ -4,7 +4,7 @@
//
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{self as ast, GenericArg};
use rustc_ast::{self as ast, AstDeref, GenericArg};
use rustc_expand::base::{self, *};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
@ -76,27 +76,36 @@ pub fn expand_env<'cx>(
},
};
let sp = cx.with_def_site_ctxt(sp);
let span = cx.with_def_site_ctxt(sp);
let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
let e = match value {
None => {
// Use the string literal in the code in the diagnostic to avoid confusing diagnostics,
// e.g. when the literal contains escape sequences.
let ast::ExprKind::Lit(ast::token::Lit {
kind: ast::token::LitKind::Str,
symbol: original_var,
kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..),
symbol,
..
}) = &var_expr.kind
else {
unreachable!("`expr_to_string` ensures this is a string lit")
};
cx.emit_err(errors::EnvNotDefined {
span: sp,
msg: custom_msg,
var: *original_var,
help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())),
});
if let Some(msg_from_user) = custom_msg {
cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user });
} else if is_cargo_env_var(var.as_str()) {
cx.emit_err(errors::EnvNotDefined::CargoEnvVar {
span,
var: *symbol,
var_expr: var_expr.ast_deref(),
});
} else {
cx.emit_err(errors::EnvNotDefined::CustomEnvVar {
span,
var: *symbol,
var_expr: var_expr.ast_deref(),
});
}
return DummyResult::any(sp);
}
Some(value) => cx.expr_str(sp, value),
@ -104,13 +113,9 @@ pub fn expand_env<'cx>(
MacEager::expr(e)
}
fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp {
if var.starts_with("CARGO_")
/// Returns `true` if an environment variable from `env!` is one used by Cargo.
fn is_cargo_env_var(var: &str) -> bool {
var.starts_with("CARGO_")
|| var.starts_with("DEP_")
|| matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
{
errors::EnvNotDefinedHelp::CargoVar
} else {
errors::EnvNotDefinedHelp::Other
}
}

View file

@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs {
pub(crate) span: Span,
}
//#[derive(Diagnostic)]
//#[diag(builtin_macros_env_not_defined)]
pub(crate) struct EnvNotDefined {
pub(crate) struct EnvNotDefinedWithUserMessage {
pub(crate) span: Span,
pub(crate) msg: Option<Symbol>,
pub(crate) var: Symbol,
pub(crate) help: Option<EnvNotDefinedHelp>,
pub(crate) msg_from_user: Symbol,
}
// Hand-written implementation to support custom user messages
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined {
// Hand-written implementation to support custom user messages.
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage {
#[track_caller]
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
let mut diag = if let Some(msg) = self.msg {
#[expect(
rustc::untranslatable_diagnostic,
reason = "cannot translate user-provided messages"
)]
handler.struct_diagnostic(msg.to_string())
} else {
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
};
diag.set_arg("var", self.var);
#[expect(
rustc::untranslatable_diagnostic,
reason = "cannot translate user-provided messages"
)]
let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string());
diag.set_span(self.span);
if let Some(help) = self.help {
diag.subdiagnostic(help);
}
diag
}
}
#[derive(Subdiagnostic)]
pub(crate) enum EnvNotDefinedHelp {
#[derive(Diagnostic)]
pub(crate) enum EnvNotDefined<'a> {
#[diag(builtin_macros_env_not_defined)]
#[help(builtin_macros_cargo)]
CargoVar,
#[help(builtin_macros_other)]
Other,
CargoEnvVar {
#[primary_span]
span: Span,
var: Symbol,
var_expr: &'a rustc_ast::Expr,
},
#[diag(builtin_macros_env_not_defined)]
#[help(builtin_macros_custom)]
CustomEnvVar {
#[primary_span]
span: Span,
var: Symbol,
var_expr: &'a rustc_ast::Expr,
},
}
#[derive(Diagnostic)]

View file

@ -100,9 +100,11 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val);
let covfun_section_name = coverageinfo::covfun_section_name(cx);
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
save_function_record(
cx,
&covfun_section_name,
mangled_function_name,
source_hash,
filenames_ref,
@ -228,6 +230,7 @@ impl CoverageMapGenerator {
/// specific, well-known section and name.
fn save_function_record(
cx: &CodegenCx<'_, '_>,
covfun_section_name: &str,
mangled_function_name: &str,
source_hash: u64,
filenames_ref: u64,
@ -254,7 +257,13 @@ fn save_function_record(
/*packed=*/ true,
);
coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used);
coverageinfo::save_func_record_to_mod(
cx,
covfun_section_name,
func_name_hash,
func_record_val,
is_used,
);
}
/// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for

View file

@ -408,6 +408,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
covfun_section_name: &str,
func_name_hash: u64,
func_record_val: &'ll llvm::Value,
is_used: bool,
@ -423,20 +424,33 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
let func_record_var_name =
format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" });
debug!("function record var name: {:?}", func_record_var_name);
let func_record_section_name = llvm::build_string(|s| unsafe {
llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
})
.expect("Rust Coverage function record section name failed UTF-8 conversion");
debug!("function record section name: {:?}", func_record_section_name);
debug!("function record section name: {:?}", covfun_section_name);
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
llvm::set_initializer(llglobal, func_record_val);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
llvm::set_section(llglobal, &func_record_section_name);
llvm::set_section(llglobal, covfun_section_name);
llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
cx.add_used_global(llglobal);
}
/// Returns the section name string to pass through to the linker when embedding
/// per-function coverage information in the object file, according to the target
/// platform's object file format.
///
/// LLVM's coverage tools read coverage mapping details from this section when
/// producing coverage reports.
///
/// Typical values are:
/// - `__llvm_covfun` on Linux
/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String {
llvm::build_string(|s| unsafe {
llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
})
.expect("Rust Coverage function record section name failed UTF-8 conversion")
}

View file

@ -533,6 +533,14 @@ impl MultiSpan {
pub fn has_span_labels(&self) -> bool {
self.span_labels.iter().any(|(sp, _)| !sp.is_dummy())
}
/// Clone this `MultiSpan` without keeping any of the span labels - sometimes a `MultiSpan` is
/// to be re-used in another diagnostic, but includes `span_labels` which have translated
/// messages. These translated messages would fail to translate without their diagnostic
/// arguments which are unlikely to be cloned alongside the `Span`.
pub fn clone_ignoring_labels(&self) -> Self {
Self { primary_spans: self.primary_spans.clone(), ..MultiSpan::new() }
}
}
impl From<Span> for MultiSpan {

View file

@ -164,6 +164,12 @@ impl IntoDiagnosticArg for hir::ConstContext {
}
}
impl IntoDiagnosticArg for ast::Expr {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
}
}
impl IntoDiagnosticArg for ast::Path {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))

View file

@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|| self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, 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_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr)
|| 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)

View file

@ -1085,12 +1085,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
pub(crate) fn suggest_copied_or_cloned(
pub(crate) fn suggest_copied_cloned_or_as_ref(
&self,
diag: &mut Diagnostic,
expr: &hir::Expr<'_>,
expr_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>,
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> bool {
let ty::Adt(adt_def, args) = expr_ty.kind() else {
return false;
@ -1102,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return false;
}
let mut suggest_copied_or_cloned = || {
let mut suggest_copied_cloned_or_as_ref = || {
let expr_inner_ty = args.type_at(0);
let expected_inner_ty = expected_args.type_at(0);
if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
@ -1119,6 +1120,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
return true;
} else if let Some(expected_ty_expr) = expected_ty_expr {
diag.span_suggestion_verbose(
expected_ty_expr.span.shrink_to_hi(),
format!(
"use `{def_path}::as_ref()` to convert `{expected_ty}` to `{expr_ty}`"
),
".as_ref()",
Applicability::MachineApplicable,
);
return true;
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
self,
@ -1146,11 +1157,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Check that the error types are equal
&& self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
{
return suggest_copied_or_cloned();
return suggest_copied_cloned_or_as_ref();
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
&& adt_def.did() == option_did
{
return suggest_copied_or_cloned();
return suggest_copied_cloned_or_as_ref();
}
false

View file

@ -45,6 +45,7 @@ session_int_literal_too_large = integer literal is too large
.note = value exceeds limit of `{$limit}`
session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
session_invalid_character_in_create_name_help = you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name
session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal
.label = invalid suffix `{$suffix}`

View file

@ -199,6 +199,14 @@ pub struct InvalidCharacterInCrateName {
pub span: Option<Span>,
pub character: char,
pub crate_name: Symbol,
#[subdiagnostic]
pub crate_name_help: Option<InvalidCrateNameHelp>,
}
#[derive(Subdiagnostic)]
pub enum InvalidCrateNameHelp {
#[help(session_invalid_character_in_create_name_help)]
AddCrateName,
}
#[derive(Subdiagnostic)]

View file

@ -2,7 +2,7 @@
use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
use crate::errors::{
CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
InvalidCharacterInCrateName,
InvalidCharacterInCrateName, InvalidCrateNameHelp,
};
use crate::Session;
use rustc_ast::{self as ast, attr};
@ -101,7 +101,16 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
continue;
}
err_count += 1;
sess.emit_err(InvalidCharacterInCrateName { span: sp, character: c, crate_name: s });
sess.emit_err(InvalidCharacterInCrateName {
span: sp,
character: c,
crate_name: s,
crate_name_help: if sp.is_none() {
Some(InvalidCrateNameHelp::AddCrateName)
} else {
None
},
});
}
}

View file

@ -25,6 +25,8 @@ union Data<T, F> {
///
/// # Examples
///
/// Initialize static variables with `LazyLock`.
///
/// ```
/// #![feature(lazy_cell)]
///
@ -54,6 +56,24 @@ union Data<T, F> {
/// // Some("Hoyten")
/// }
/// ```
/// Initialize fields with `LazyLock`.
/// ```
/// #![feature(lazy_cell)]
///
/// use std::sync::LazyLock;
///
/// #[derive(Debug)]
/// struct UseCellLock {
/// number: LazyLock<u32>,
/// }
/// fn main() {
/// let lock: LazyLock<u32> = LazyLock::new(|| 0u32);
///
/// let data = UseCellLock { number: lock };
/// println!("{}", *data.number);
/// }
/// ```
#[unstable(feature = "lazy_cell", issue = "109736")]
pub struct LazyLock<T, F = fn() -> T> {
once: Once,

View file

@ -328,7 +328,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
{
// Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces
// `&[u8]`. This change would prevent matching with different sized slices.
} else {
} else if !callsite.starts_with("env!") {
span_lint_and_sugg(
cx,
STRING_LIT_AS_BYTES,

View file

@ -116,7 +116,9 @@ pub fn check(path: &Path, bad: &mut bool) {
// must strip all of them.
let testname =
file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0;
if !file_path.with_file_name(testname).with_extension("rs").exists() {
if !file_path.with_file_name(testname).with_extension("rs").exists()
&& !testname.contains("ignore-tidy")
{
tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path);
}

View file

@ -0,0 +1,13 @@
// run-rustfix
use std::fmt::Debug;
pub fn foo<I: Iterator>(mut iter: I, value: &I::Item)
where
I::Item: Eq + Debug,
{
debug_assert_eq!(iter.next().as_ref(), Some(value));
//~^ ERROR mismatched types
}
fn main() {}

View file

@ -1,6 +1,8 @@
// run-rustfix
use std::fmt::Debug;
fn foo<I: Iterator>(mut iter: I, value: &I::Item)
pub fn foo<I: Iterator>(mut iter: I, value: &I::Item)
where
I::Item: Eq + Debug,
{

View file

@ -1,11 +1,15 @@
error[E0308]: mismatched types
--> $DIR/dont-suggest-cyclic-constraint.rs:7:35
--> $DIR/dont-suggest-cyclic-constraint.rs:9:35
|
LL | debug_assert_eq!(iter.next(), Some(value));
| ^^^^^^^^^^^ expected `Option<<I as Iterator>::Item>`, found `Option<&<I as Iterator>::Item>`
|
= note: expected enum `Option<<I as Iterator>::Item>`
found enum `Option<&<I as Iterator>::Item>`
help: use `Option::as_ref()` to convert `Option<<I as Iterator>::Item>` to `Option<&<I as Iterator>::Item>`
|
LL | debug_assert_eq!(iter.next().as_ref(), Some(value));
| +++++++++
error: aborting due to previous error

View file

@ -0,0 +1,2 @@
// issue: 113981
pub fn main() {}

View file

@ -0,0 +1,6 @@
error: invalid character `'.'` in crate name: `need_crate_arg_ignore_tidy.x`
|
= help: you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name
error: aborting due to previous error

View file

@ -0,0 +1,27 @@
#![feature(generators)]
// `foo` attempts to dereference `""`, which results in an error being reported. Later, the
// generator transform for `foo` then produces a union which contains a `str` type - unions should
// not contain unsized types, but this is okay because an error has been reported already.
// When const propagation happens later in compilation, it attempts to compute the layout of the
// generator (as part of checking whether something can be const propagated) and in turn attempts
// to compute the layout of `str` in the context of a union - where this caused an ICE. This test
// makes sure that doesn't happen again.
fn foo() {
let _y = static || {
let x = &mut 0;
*{
yield;
x
} += match { *"" }.len() {
//~^ ERROR cannot move a value of type `str` [E0161]
//~^^ ERROR cannot move out of a shared reference [E0507]
_ => 0,
};
};
}
fn main() {
foo()
}

View file

@ -0,0 +1,16 @@
error[E0161]: cannot move a value of type `str`
--> $DIR/issue-113279.rs:17:20
|
LL | } += match { *"" }.len() {
| ^^^^^^^ the size of `str` cannot be statically determined
error[E0507]: cannot move out of a shared reference
--> $DIR/issue-113279.rs:17:22
|
LL | } += match { *"" }.len() {
| ^^^ move occurs because value has type `str`, which does not implement the `Copy` trait
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0161, E0507.
For more information about an error, try `rustc --explain E0161`.

View file

@ -0,0 +1,23 @@
// regression test for https://github.com/rust-lang/rust/issues/69016
// check-pass
#![warn(unused)]
#![deny(warnings)]
fn _unused1(x: i32) -> i32 {
const F: i32 = 2;
let g = 1;
x * F + g
}
pub struct Foo {}
impl Foo {
fn _unused2(x: i32) -> i32 {
const F: i32 = 2;
let g = 1;
x * F + g
}
}
fn main() {}

View file

@ -0,0 +1,10 @@
// unset-rustc-env:oopsie
// unset-rustc-env:a""a
env![r#"oopsie"#];
//~^ ERROR environment variable `oopsie` not defined at compile time
env![r#"a""a"#];
//~^ ERROR environment variable `a""a` not defined at compile time
fn main() {}

View file

@ -0,0 +1,20 @@
error: environment variable `oopsie` not defined at compile time
--> $DIR/builtin-env-issue-114010.rs:4:1
|
LL | env![r#"oopsie"#];
| ^^^^^^^^^^^^^^^^^
|
= help: use `std::env::var(r#"oopsie"#)` to read the variable at run time
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: environment variable `a""a` not defined at compile time
--> $DIR/builtin-env-issue-114010.rs:7:1
|
LL | env![r#"a""a"#];
| ^^^^^^^^^^^^^^^
|
= help: use `std::env::var(r#"a""a"#)` to read the variable at run time
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors

View file

@ -20,4 +20,12 @@ fn main() {
expect::<Result<String, ()>>(x.cloned());
//~^ ERROR mismatched types
//~| HELP use `Result::cloned` to clone the value inside the `Result`
let s = String::new();
let x = Some(s.clone());
let y = Some(&s);
println!("{}", x.as_ref() == y);
//~^ ERROR mismatched types
//~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
}

View file

@ -20,4 +20,12 @@ fn main() {
expect::<Result<String, ()>>(x);
//~^ ERROR mismatched types
//~| HELP use `Result::cloned` to clone the value inside the `Result`
let s = String::new();
let x = Some(s.clone());
let y = Some(&s);
println!("{}", x == y);
//~^ ERROR mismatched types
//~| HELP use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
}

View file

@ -78,6 +78,19 @@ help: use `Result::cloned` to clone the value inside the `Result`
LL | expect::<Result<String, ()>>(x.cloned());
| +++++++++
error: aborting due to 4 previous errors
error[E0308]: mismatched types
--> $DIR/copied-and-cloned.rs:27:25
|
LL | println!("{}", x == y);
| ^ expected `Option<String>`, found `Option<&String>`
|
= note: expected enum `Option<String>`
found enum `Option<&String>`
help: use `Option::as_ref()` to convert `Option<String>` to `Option<&String>`
|
LL | println!("{}", x.as_ref() == y);
| +++++++++
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`.