Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ pub enum InferenceDiagnostic {
expr: ExprId,
found: StoredTy,
},
CannotImplicitlyDerefTraitObject {
#[type_visitable(ignore)]
pat: PatId,
found: StoredTy,
},
CannotIndexInto {
#[type_visitable(ignore)]
expr: ExprId,
Expand Down
25 changes: 13 additions & 12 deletions crates/hir-ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,22 +959,23 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
local_ty
}

fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> {
fn check_dereferenceable(
&mut self,
expected: Ty<'db>,
pat: PatId,
inner: PatId,
) -> Result<(), ()> {
if let Pat::Bind { .. } = self.store[inner]
&& let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true)
&& let TyKind::Dynamic(..) = pointee_ty.kind()
{
// This is "x = dyn SomeTrait" being reduced from
// "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error.
// FIXME: Emit an error. rustc emits this message:
const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\
This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \
pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \
this type has no compile-time size. Therefore, all accesses to trait types must be through \
pointers. If you encounter this error you should try to avoid dereferencing the pointer.

You can read more about trait objects in the Trait Objects section of the Reference: \
https://doc.rust-lang.org/reference/types.html#trait-objects";
self.push_diagnostic(InferenceDiagnostic::CannotImplicitlyDerefTraitObject {
pat,
found: expected.store(),
});
return Err(());
}
Ok(())
}
Expand Down Expand Up @@ -1260,7 +1261,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
) -> Ty<'db> {
let interner = self.interner();
let (box_ty, inner_ty) = self
.check_dereferenceable(expected, inner)
.check_dereferenceable(expected, pat, inner)
.map(|()| {
// Here, `demand::subtype` is good enough, but I don't
// think any errors can be introduced by using `demand::eqtype`.
Expand Down Expand Up @@ -1473,7 +1474,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
}
}

let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) {
let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, pat, inner) {
Ok(()) => {
// `demand::subtype` would be good enough, but using `eqtype` turns
// out to be equally general. See (note_1) for details.
Expand Down
1 change: 1 addition & 0 deletions crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ pub(super) mod resolve_completely {
self.resolve_completely(diagnostic);

if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. }
| InferenceDiagnostic::CannotImplicitlyDerefTraitObject { found: ty, .. }
| InferenceDiagnostic::CannotIndexInto { found: ty, .. }
| InferenceDiagnostic::ExpectedFunction { found: ty, .. }
| InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. }
Expand Down
11 changes: 11 additions & 0 deletions crates/hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ diagnostics![AnyDiagnostic<'db> ->
AwaitOutsideOfAsync,
BreakOutsideOfLoop,
CannotBeDereferenced<'db>,
CannotImplicitlyDerefTraitObject<'db>,
CannotIndexInto<'db>,
CastToUnsized<'db>,
ExpectedArrayOrSlicePat<'db>,
Expand Down Expand Up @@ -334,6 +335,12 @@ pub struct CannotBeDereferenced<'db> {
pub found: Type<'db>,
}

#[derive(Debug)]
pub struct CannotImplicitlyDerefTraitObject<'db> {
pub pat: InFile<ExprOrPatPtr>,
pub found: Type<'db>,
}

#[derive(Debug)]
pub struct CannotIndexInto<'db> {
pub expr: InFile<ExprOrPatPtr>,
Expand Down Expand Up @@ -973,6 +980,10 @@ impl<'db> AnyDiagnostic<'db> {
let expr = expr_syntax(*expr)?;
CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into()
}
InferenceDiagnostic::CannotImplicitlyDerefTraitObject { pat, found } => {
let pat = pat_syntax(*pat)?.map(Into::into);
CannotImplicitlyDerefTraitObject { pat, found: new_ty(found.as_ref()) }.into()
}
InferenceDiagnostic::CannotIndexInto { expr, found } => {
let expr = expr_syntax(*expr)?;
CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use hir::HirDisplay;

use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};

// Diagnostic: cannot-implicitly-deref-trait-object
//
// This diagnostic is triggered when a pointer to a trait object is implicitly
// dereferenced by a pattern.
pub(crate) fn cannot_implicitly_deref_trait_object(
ctx: &DiagnosticsContext<'_, '_>,
d: &hir::CannotImplicitlyDerefTraitObject<'_>,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0033"),
format!(
"type `{}` cannot be dereferenced",
d.found.display(ctx.sema.db, ctx.display_target)
),
d.pat.map(Into::into),
)
.stable()
}

#[cfg(test)]
mod tests {
use crate::tests::check_diagnostics;

#[test]
fn trait_object_pattern_deref() {
check_diagnostics(
r#"
trait Trait {}

fn f(x: &dyn Trait) {
let &ref _y = x;
//^^^^^^^ error: type `&(dyn Trait + 'static)` cannot be dereferenced
}
"#,
);
}

#[test]
fn allows_sized_ref_pattern_deref() {
check_diagnostics(
r#"
fn f(x: &i32) {
let &ref _y = x;
}
"#,
);
}
}
2 changes: 2 additions & 0 deletions crates/ide-diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod handlers {
pub(crate) mod bad_rtn;
pub(crate) mod break_outside_of_loop;
pub(crate) mod cannot_be_dereferenced;
pub(crate) mod cannot_implicitly_deref_trait_object;
pub(crate) mod cannot_index_into;
pub(crate) mod duplicate_field;
pub(crate) mod elided_lifetimes_in_path;
Expand Down Expand Up @@ -436,6 +437,7 @@ pub fn semantic_diagnostics(
let d = match diag {
AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d),
AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d),
AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d),
AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d),
AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d),
AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => {
Expand Down
Loading