Skip to content
Open
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
31 changes: 21 additions & 10 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,23 +421,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ty::new_error(tcx, guar);
}

let oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
// `Not` and `Neg` can be selected using an unresolved operand type,
// later constraints may still determine that type,
// so we call `resolve_vars_with_obligations` for them.
match unop {
hir::UnOp::Deref => self.lookup_derefing(expr, oprnd, oprnd_t).unwrap_or_else(|| {
let mut err =
self.dcx().create_err(CantDereference { span: expr.span, ty: oprnd_t });
let sp = tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) = tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
Ty::new_error(tcx, err.emit())
}),
hir::UnOp::Deref => {
// Dereferencing must distinguish builtin pointers from
// overloaded `Deref`, so it still requires a structurally resolved type.
Comment thread
chenyukang marked this conversation as resolved.
//
// This is necessary as raw pointers do not implement `Deref`.
let oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
self.lookup_derefing(expr, oprnd, oprnd_t).unwrap_or_else(|| {
let mut err =
self.dcx().create_err(CantDereference { span: expr.span, ty: oprnd_t });
let sp = tcx.sess.source_map().start_point(expr.span).with_parent(None);
if let Some(sp) = tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
Ty::new_error(tcx, err.emit())
})
}
hir::UnOp::Not => {
let oprnd_t = self.resolve_vars_with_obligations(oprnd_t);
let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
// If it's builtin, we can reuse the type, this helps inference.
if oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool { oprnd_t } else { result }
}
hir::UnOp::Neg => {
let oprnd_t = self.resolve_vars_with_obligations(oprnd_t);
let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
// If it's builtin, we can reuse the type, this helps inference.
if oprnd_t.is_numeric() { oprnd_t } else { result }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Regression test for https://github.com/rust-lang/rust/issues/106138.
//! An unresolved dereference must not treat a raw pointer as an overloaded `Deref`.

fn make<T>() -> T {
loop {}
}

fn main() {
let pointer = make();
//~^ ERROR type annotations needed
let value = unsafe { *pointer };
let _: *const u8 = pointer;
let _: u8 = value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0282]: type annotations needed
--> $DIR/unary-deref-late-raw-pointer-inference-issue-106138.rs:9:9
|
LL | let pointer = make();
| ^^^^^^^
LL |
LL | let value = unsafe { *pointer };
| -------- type must be known at this point
|
help: consider giving `pointer` an explicit type
|
LL | let pointer: /* Type */ = make();
| ++++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0282`.
27 changes: 27 additions & 0 deletions tests/ui/inference/unary-neg-late-operand-inference-issue-26830.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Regression test for https://github.com/rust-lang/rust/issues/26830.
//! Unary negation should allow later constraints to determine its operand type.

//@ check-pass

fn make<T>() -> T {
loop {}
}

fn constrained_by_value() {
let input = make();
let output = -input;
let _: i32 = input;
let _: i32 = output;
}

fn constrained_by_borrow() {
let input = make();
let output = -input;
let _: &i32 = &input;
let _: i32 = output;
}

fn main() {
constrained_by_value();
constrained_by_borrow();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Regression test for https://github.com/rust-lang/rust/issues/106138.
//! From comment https://github.com/rust-lang/rust/pull/107567#discussion_r1093589448
//! Unary operator inference must not assume that the associated output type equals `Self`.

#[derive(Copy, Clone, Default)]
struct A;

struct B;

impl std::ops::Not for A {
type Output = B;

fn not(self) -> B {
B
}
}

#[derive(Copy, Clone)]
struct NoNot;

fn make<T>() -> T {
loop {}
}

fn resolved_without_impl() {
let value = make();
let _ = !value;
//~^ ERROR the trait bound `NoNot: Not` is not satisfied
let _: NoNot = value;
}

fn unconstrained() {
let value = make();
//~^ ERROR type annotations needed
let _ = !value;
}

fn main() {
let x = Default::default();
//~^ ERROR cannot call associated function on trait
let _: A = !x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
error[E0277]: the trait bound `NoNot: Not` is not satisfied
--> $DIR/unary-op-associated-output-mismatch-issue-106138.rs:27:13
|
LL | let _ = !value;
| ^^^^^^ unsatisfied trait bound
|
help: the trait `Not` is not implemented for `NoNot`
--> $DIR/unary-op-associated-output-mismatch-issue-106138.rs:19:1
|
LL | struct NoNot;
| ^^^^^^^^^^^^

error[E0284]: type annotations needed
--> $DIR/unary-op-associated-output-mismatch-issue-106138.rs:33:9
|
LL | let value = make();
| ^^^^^
LL |
LL | let _ = !value;
| ------ type must be known at this point
|
= note: cannot satisfy `<_ as Not>::Output == _`
= note: the type must also implement `Not`
help: consider giving `value` an explicit type
|
LL | let value: /* Type */ = make();
| ++++++++++++

error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
--> $DIR/unary-op-associated-output-mismatch-issue-106138.rs:39:13
|
LL | let x = Default::default();
| ^^^^^^^^^^^^^^^^^^ cannot call associated function of trait
|
help: use a fully-qualified path to a specific available implementation
|
LL | let x = </* self type */ as Default>::default();
| +++++++++++++++++++ +

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0284, E0790.
For more information about an error, try `rustc --explain E0277`.
49 changes: 49 additions & 0 deletions tests/ui/inference/unary-op-late-operand-inference-issue-106138.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Regression test for https://github.com/rust-lang/rust/issues/106138.
//! Unary operators should allow their operand types to be inferred by later constraints.

//@ check-pass

use std::ops::Not;

fn not_index(x: &Vec<bool>) {
let closure = |i, a: &Vec<bool>| !a[i];
let _ = closure(0, x);
}

fn neg_index(x: &Vec<i32>) {
let closure = |i, a: &Vec<i32>| -a[i];
let _ = closure(0, x);
}

#[derive(Copy, Clone, Default)]
struct Input;

struct Output;

impl Not for Input {
type Output = Output;

fn not(self) -> Self::Output {
Output
}
}

fn output_differs_from_operand() {
let input = Default::default();
let output = !input;
let _: Input = input;
let _: Output = output;
}

fn output_expectation_differs_from_operand() {
let input = Default::default();
let _: Output = !input;
let _: Input = input;
}

fn main() {
not_index(&vec![true]);
neg_index(&vec![1]);
output_differs_from_operand();
output_expectation_differs_from_operand();
}
Loading