diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6ebf382083f25..10e1d3bdc5e1b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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. + // + // 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 } diff --git a/tests/ui/inference/unary-deref-late-raw-pointer-inference-issue-106138.rs b/tests/ui/inference/unary-deref-late-raw-pointer-inference-issue-106138.rs new file mode 100644 index 0000000000000..c2d67e02ed520 --- /dev/null +++ b/tests/ui/inference/unary-deref-late-raw-pointer-inference-issue-106138.rs @@ -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 { + loop {} +} + +fn main() { + let pointer = make(); + //~^ ERROR type annotations needed + let value = unsafe { *pointer }; + let _: *const u8 = pointer; + let _: u8 = value; +} diff --git a/tests/ui/inference/unary-deref-late-raw-pointer-inference-issue-106138.stderr b/tests/ui/inference/unary-deref-late-raw-pointer-inference-issue-106138.stderr new file mode 100644 index 0000000000000..f6a2a772ffe54 --- /dev/null +++ b/tests/ui/inference/unary-deref-late-raw-pointer-inference-issue-106138.stderr @@ -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`. diff --git a/tests/ui/inference/unary-neg-late-operand-inference-issue-26830.rs b/tests/ui/inference/unary-neg-late-operand-inference-issue-26830.rs new file mode 100644 index 0000000000000..9f2ad98e5d6b4 --- /dev/null +++ b/tests/ui/inference/unary-neg-late-operand-inference-issue-26830.rs @@ -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 { + 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(); +} diff --git a/tests/ui/inference/unary-op-associated-output-mismatch-issue-106138.rs b/tests/ui/inference/unary-op-associated-output-mismatch-issue-106138.rs new file mode 100644 index 0000000000000..b30535115602e --- /dev/null +++ b/tests/ui/inference/unary-op-associated-output-mismatch-issue-106138.rs @@ -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 { + 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; +} diff --git a/tests/ui/inference/unary-op-associated-output-mismatch-issue-106138.stderr b/tests/ui/inference/unary-op-associated-output-mismatch-issue-106138.stderr new file mode 100644 index 0000000000000..30318239390e5 --- /dev/null +++ b/tests/ui/inference/unary-op-associated-output-mismatch-issue-106138.stderr @@ -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 = ::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`. diff --git a/tests/ui/inference/unary-op-late-operand-inference-issue-106138.rs b/tests/ui/inference/unary-op-late-operand-inference-issue-106138.rs new file mode 100644 index 0000000000000..549c7a91d6f00 --- /dev/null +++ b/tests/ui/inference/unary-op-late-operand-inference-issue-106138.rs @@ -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) { + let closure = |i, a: &Vec| !a[i]; + let _ = closure(0, x); +} + +fn neg_index(x: &Vec) { + let closure = |i, a: &Vec| -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(); +}