From b225aab5ef00820375b29089d39aa283eaf90ef8 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 24 Nov 2023 03:16:27 -0500 Subject: Stabilize `type_name_of_val` Make the following API stable: // in core::any pub fn type_name_of_val(_val: &T) -> &'static str Const stability is not added because this relies on `type_name` which is also not const. That has a blocking issue. Fixes #66359 --- library/core/src/any.rs | 39 ++++++++++++-------------- tests/ui/hygiene/cross-crate-define-and-use.rs | 1 - 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 8f5404d9713..8c18788e591 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -690,44 +690,41 @@ pub const fn type_name() -> &'static str { intrinsics::type_name::() } -/// Returns the name of the type of the pointed-to value as a string slice. +/// Returns the type name of the pointed-to value as a string slice. +/// /// This is the same as `type_name::()`, but can be used where the type of a /// variable is not easily available. /// /// # Note /// -/// This is intended for diagnostic use. The exact contents and format of the -/// string are not specified, other than being a best-effort description of the -/// type. For example, `type_name_of_val::>(None)` could return -/// `"Option"` or `"std::option::Option"`, but not -/// `"foobar"`. In addition, the output may change between versions of the -/// compiler. -/// -/// This function does not resolve trait objects, -/// meaning that `type_name_of_val(&7u32 as &dyn Debug)` -/// may return `"dyn Debug"`, but not `"u32"`. +/// Like [`type_name`], this is intended for diagnostic use and the exact output is not +/// guaranteed. It provides a best-effort description, but the output may change between +/// versions of the compiler. /// -/// The type name should not be considered a unique identifier of a type; -/// multiple types may share the same type name. +/// In short: use this for debugging, avoid using the output to affect program behavior. More +/// information is available at [`type_name`]. /// -/// The current implementation uses the same infrastructure as compiler -/// diagnostics and debuginfo, but this is not guaranteed. +/// Additionally, this function does not resolve trait objects. This means that +/// `type_name_of_val(&7u32 as &dyn Debug)` may return `"dyn Debug"`, but will not return `"u32"` +/// at this time. /// /// # Examples /// /// Prints the default integer and float types. /// /// ```rust -/// #![feature(type_name_of_val)] /// use std::any::type_name_of_val; /// -/// let x = 1; -/// println!("{}", type_name_of_val(&x)); -/// let y = 1.0; -/// println!("{}", type_name_of_val(&y)); +/// let s = "foo"; +/// let x: i32 = 1; +/// let y: f32 = 1.0; +/// +/// assert!(type_name_of_val(&s).contains("str")); +/// assert!(type_name_of_val(&x).contains("i32")); +/// assert!(type_name_of_val(&y).contains("f32")); /// ``` #[must_use] -#[unstable(feature = "type_name_of_val", issue = "66359")] +#[stable(feature = "type_name_of_val", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() diff --git a/tests/ui/hygiene/cross-crate-define-and-use.rs b/tests/ui/hygiene/cross-crate-define-and-use.rs index 94f1adff626..62b1820235c 100644 --- a/tests/ui/hygiene/cross-crate-define-and-use.rs +++ b/tests/ui/hygiene/cross-crate-define-and-use.rs @@ -6,7 +6,6 @@ // check-pass // aux-build:use_by_macro.rs -#![feature(type_name_of_val)] extern crate use_by_macro; use use_by_macro::*; -- cgit 1.4.1-3-g733a5 From 552143c875d4bd037ae9bbd73440dfc77c6e6dee Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Dec 2023 12:26:09 +0100 Subject: Simplify `src-script.js` code --- src/librustdoc/html/static/js/src-script.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index 27b5cf1e2ae..8b7a9161d71 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -131,12 +131,8 @@ function createSrcSidebar() { } } -const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/; - -function highlightSrcLines(match) { - if (typeof match === "undefined") { - match = window.location.hash.match(lineNumbersRegex); - } +function highlightSrcLines() { + const match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/); if (!match) { return; } @@ -218,12 +214,7 @@ const handleSrcHighlight = (function() { }; }()); -window.addEventListener("hashchange", () => { - const match = window.location.hash.match(lineNumbersRegex); - if (match) { - return highlightSrcLines(match); - } -}); +window.addEventListener("hashchange", highlightSrcLines); onEachLazy(document.getElementsByClassName("src-line-numbers"), el => { el.addEventListener("click", handleSrcHighlight); -- cgit 1.4.1-3-g733a5 From c3dd266f4727baa885c87266874efe9e42b5e34d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 16 Dec 2023 00:15:15 +0900 Subject: Remove @JohnTitor from diagnostics pings I've been inactive around diagnostics for a while so would like to remove myself from the diagnostics-related ping groups for now. --- triagebot.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 30f049c79d6..24b82c5cfaa 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -555,15 +555,15 @@ cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@celinval", "@vakar [mentions."compiler/rustc_error_messages"] message = "`rustc_error_messages` was changed" -cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"] +cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"] [mentions."compiler/rustc_errors/src/translation.rs"] message = "`rustc_errors::translation` was changed" -cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"] +cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"] [mentions."compiler/rustc_macros/src/diagnostics"] message = "`rustc_macros::diagnostics` was changed" -cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"] +cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"] [mentions."compiler/stable_mir"] message = "This PR changes Stable MIR" -- cgit 1.4.1-3-g733a5 From 028a3135c82cc72b38a7be44a86da2563959044e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Dec 2023 16:56:11 +0100 Subject: Simplify JS code a little bit --- src/librustdoc/html/render/write_shared.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index c4a6e920031..6408e97df50 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -328,8 +328,8 @@ pub(super) fn write_shared( v.push_str( r#"\ ]')); -if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)}; -if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); "#, ); Ok(v.into_bytes()) -- cgit 1.4.1-3-g733a5 From 6bbbff5189604d8a187e5837bd763e303d7a79d6 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 15 Dec 2023 08:56:49 -0700 Subject: rustdoc: add regression test for JS data file loading Follow up for #118961 --- tests/rustdoc-gui/globals.goml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/rustdoc-gui/globals.goml diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml new file mode 100644 index 00000000000..53f96ff086e --- /dev/null +++ b/tests/rustdoc-gui/globals.goml @@ -0,0 +1,24 @@ +// Make sure search stores its data in `window` +// It needs to use a global to avoid racing on search-index.js and search.js +// https://github.com/rust-lang/rust/pull/118961 + +// URL query +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" +wait-for: "#search-tabs" +assert-window-property-false: {"searchIndex": null} +assert-window-property: {"srcIndex": null} + +// Form input +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" +write: (".search-input", "Foo") +press-key: 'Enter' +wait-for: "#search-tabs" +assert-window-property-false: {"searchIndex": null} +assert-window-property: {"srcIndex": null} + +// source sidebar +go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" +click: "#src-sidebar-toggle" +wait-for: "#src-sidebar details" +assert-window-property-false: {"srcIndex": null} +assert-window-property: {"searchIndex": null} -- cgit 1.4.1-3-g733a5 From b863e9ba5791d860100fc458c790323707186249 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sat, 11 Nov 2023 11:33:10 +0000 Subject: Stabilize `ptr::{from_ref, from_mut}` --- library/core/src/ptr/mod.rs | 7 ++- library/std/src/lib.rs | 1 - tests/ui/lint/ptr_null_checks.rs | 2 - tests/ui/lint/ptr_null_checks.stderr | 54 +++++++++++----------- tests/ui/lint/reference_casting.rs | 2 - tests/ui/lint/reference_casting.stderr | 84 +++++++++++++++++----------------- tests/ui/structs-enums/type-sizes.rs | 1 - 7 files changed, 74 insertions(+), 77 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index d6603681a0b..eed793186b9 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -720,7 +720,8 @@ where /// type or mutability, in particular if the code is refactored. #[inline(always)] #[must_use] -#[unstable(feature = "ptr_from_ref", issue = "106116")] +#[stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")] #[rustc_never_returns_null_ptr] #[rustc_diagnostic_item = "ptr_from_ref"] pub const fn from_ref(r: &T) -> *const T { @@ -733,7 +734,9 @@ pub const fn from_ref(r: &T) -> *const T { /// type or mutability, in particular if the code is refactored. #[inline(always)] #[must_use] -#[unstable(feature = "ptr_from_ref", issue = "106116")] +#[stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "ptr_from_ref", since = "CURRENT_RUSTC_VERSION")] +#[rustc_allow_const_fn_unstable(const_mut_refs)] #[rustc_never_returns_null_ptr] pub const fn from_mut(r: &mut T) -> *mut T { r diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index d08f0b1c9d0..76081833e05 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -338,7 +338,6 @@ #![feature(portable_simd)] #![feature(prelude_2024)] #![feature(ptr_as_uninit)] -#![feature(ptr_from_ref)] #![feature(raw_os_nonzero)] #![feature(round_ties_even)] #![feature(slice_internals)] diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs index 3028084e962..4925019be1e 100644 --- a/tests/ui/lint/ptr_null_checks.rs +++ b/tests/ui/lint/ptr_null_checks.rs @@ -1,7 +1,5 @@ // check-pass -#![feature(ptr_from_ref)] - use std::ptr; extern "C" fn c_fn() {} diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr index 0edc1b86536..70a27790c5b 100644 --- a/tests/ui/lint/ptr_null_checks.stderr +++ b/tests/ui/lint/ptr_null_checks.stderr @@ -1,5 +1,5 @@ warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:14:8 + --> $DIR/ptr_null_checks.rs:12:8 | LL | if (fn_ptr as *mut ()).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | if (fn_ptr as *mut ()).is_null() {} = note: `#[warn(useless_ptr_null_checks)]` on by default warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:16:8 + --> $DIR/ptr_null_checks.rs:14:8 | LL | if (fn_ptr as *const u8).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | if (fn_ptr as *const u8).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:18:8 + --> $DIR/ptr_null_checks.rs:16:8 | LL | if (fn_ptr as *const ()) == std::ptr::null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | if (fn_ptr as *const ()) == std::ptr::null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:20:8 + --> $DIR/ptr_null_checks.rs:18:8 | LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | if (fn_ptr as *mut ()) == std::ptr::null_mut() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:22:8 + --> $DIR/ptr_null_checks.rs:20:8 | LL | if (fn_ptr as *const ()) == (0 as *const ()) {} | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +50,7 @@ LL | if (fn_ptr as *const ()) == (0 as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:24:8 + --> $DIR/ptr_null_checks.rs:22:8 | LL | if <*const _>::is_null(fn_ptr as *const ()) {} | ^^^^^^^^^^^^^^^^^^^^------^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | if <*const _>::is_null(fn_ptr as *const ()) {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:26:8 + --> $DIR/ptr_null_checks.rs:24:8 | LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ LL | if (fn_ptr as *mut fn() as *const fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:28:8 + --> $DIR/ptr_null_checks.rs:26:8 | LL | if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | if (fn_ptr as *mut fn() as *const fn()).cast_mut().is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:30:8 + --> $DIR/ptr_null_checks.rs:28:8 | LL | if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() {} | ^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | if ((fn_ptr as *mut fn()).cast() as *const fn()).cast_mut().is_null() { = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:32:8 + --> $DIR/ptr_null_checks.rs:30:8 | LL | if (fn_ptr as fn() as *const ()).is_null() {} | ^--------------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | if (fn_ptr as fn() as *const ()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: function pointers are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:34:8 + --> $DIR/ptr_null_checks.rs:32:8 | LL | if (c_fn as *const fn()).is_null() {} | ^----^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | if (c_fn as *const fn()).is_null() {} = help: wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:38:8 + --> $DIR/ptr_null_checks.rs:36:8 | LL | if (&mut 8 as *mut i32).is_null() {} | ^------^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,13 +118,13 @@ LL | if (&mut 8 as *mut i32).is_null() {} | expression has type `&mut i32` warning: returned pointer of `from_mut` call is never null, so checking it for null will always return false - --> $DIR/ptr_null_checks.rs:40:8 + --> $DIR/ptr_null_checks.rs:38:8 | LL | if ptr::from_mut(&mut 8).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:42:8 + --> $DIR/ptr_null_checks.rs:40:8 | LL | if (&8 as *const i32).is_null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,25 +132,25 @@ LL | if (&8 as *const i32).is_null() {} | expression has type `&i32` warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false - --> $DIR/ptr_null_checks.rs:44:8 + --> $DIR/ptr_null_checks.rs:42:8 | LL | if ptr::from_ref(&8).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false - --> $DIR/ptr_null_checks.rs:46:8 + --> $DIR/ptr_null_checks.rs:44:8 | LL | if ptr::from_ref(&8).cast_mut().is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false - --> $DIR/ptr_null_checks.rs:48:8 + --> $DIR/ptr_null_checks.rs:46:8 | LL | if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:50:8 + --> $DIR/ptr_null_checks.rs:48:8 | LL | if (&8 as *const i32) == std::ptr::null() {} | ^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -158,7 +158,7 @@ LL | if (&8 as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:53:8 + --> $DIR/ptr_null_checks.rs:51:8 | LL | if (ref_num as *const i32) == std::ptr::null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | if (ref_num as *const i32) == std::ptr::null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:55:8 + --> $DIR/ptr_null_checks.rs:53:8 | LL | if (b"\0" as *const u8).is_null() {} | ^-----^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL | if (b"\0" as *const u8).is_null() {} | expression has type `&[u8; 1]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:57:8 + --> $DIR/ptr_null_checks.rs:55:8 | LL | if ("aa" as *const str).is_null() {} | ^----^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -182,7 +182,7 @@ LL | if ("aa" as *const str).is_null() {} | expression has type `&str` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:59:8 + --> $DIR/ptr_null_checks.rs:57:8 | LL | if (&[1, 2] as *const i32).is_null() {} | ^-------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL | if (&[1, 2] as *const i32).is_null() {} | expression has type `&[i32; 2]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:61:8 + --> $DIR/ptr_null_checks.rs:59:8 | LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} | ^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +198,7 @@ LL | if (&mut [1, 2] as *mut i32) == std::ptr::null_mut() {} | expression has type `&mut [i32; 2]` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:63:8 + --> $DIR/ptr_null_checks.rs:61:8 | LL | if (static_i32() as *const i32).is_null() {} | ^------------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +206,7 @@ LL | if (static_i32() as *const i32).is_null() {} | expression has type `&i32` warning: references are not nullable, so checking them for null will always return false - --> $DIR/ptr_null_checks.rs:65:8 + --> $DIR/ptr_null_checks.rs:63:8 | LL | if (&*{ static_i32() } as *const i32).is_null() {} | ^------------------^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -214,13 +214,13 @@ LL | if (&*{ static_i32() } as *const i32).is_null() {} | expression has type `&i32` warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false - --> $DIR/ptr_null_checks.rs:69:8 + --> $DIR/ptr_null_checks.rs:67:8 | LL | if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false - --> $DIR/ptr_null_checks.rs:71:8 + --> $DIR/ptr_null_checks.rs:69:8 | LL | if ptr::NonNull::::dangling().as_ptr().is_null() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs index fe8c6e3ff23..84267c0af03 100644 --- a/tests/ui/lint/reference_casting.rs +++ b/tests/ui/lint/reference_casting.rs @@ -1,7 +1,5 @@ // check-fail -#![feature(ptr_from_ref)] - extern "C" { // N.B., mutability can be easily incorrect in FFI calls -- as // in C, the default is mutable pointers. diff --git a/tests/ui/lint/reference_casting.stderr b/tests/ui/lint/reference_casting.stderr index 2820696508a..374a58d7b7b 100644 --- a/tests/ui/lint/reference_casting.stderr +++ b/tests/ui/lint/reference_casting.stderr @@ -1,5 +1,5 @@ error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:19:16 + --> $DIR/reference_casting.rs:17:16 | LL | let _num = &mut *(num as *const i32 as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _num = &mut *(num as *const i32 as *mut i32); = note: `#[deny(invalid_reference_casting)]` on by default error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:21:16 + --> $DIR/reference_casting.rs:19:16 | LL | let _num = &mut *(num as *const i32).cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _num = &mut *(num as *const i32).cast_mut(); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:23:16 + --> $DIR/reference_casting.rs:21:16 | LL | let _num = &mut *std::ptr::from_ref(num).cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _num = &mut *std::ptr::from_ref(num).cast_mut(); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:25:16 + --> $DIR/reference_casting.rs:23:16 | LL | let _num = &mut *std::ptr::from_ref({ num }).cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _num = &mut *std::ptr::from_ref({ num }).cast_mut(); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:27:16 + --> $DIR/reference_casting.rs:25:16 | LL | let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut(); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:29:16 + --> $DIR/reference_casting.rs:27:16 | LL | let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:31:16 + --> $DIR/reference_casting.rs:29:16 | LL | let _num = &mut *(num as *const i32).cast::().cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _num = &mut *(num as *const i32).cast::().cast_mut(); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:33:16 + --> $DIR/reference_casting.rs:31:16 | LL | let _num = &mut *(num as *const i32).cast::().cast_mut().cast_const().cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _num = &mut *(num as *const i32).cast::().cast_mut().cast_cons = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:35:16 + --> $DIR/reference_casting.rs:33:16 | LL | let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _num = &mut *(std::ptr::from_ref(static_u8()) as *mut i32); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:37:16 + --> $DIR/reference_casting.rs:35:16 | LL | let _num = &mut *std::mem::transmute::<_, *mut i32>(num); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _num = &mut *std::mem::transmute::<_, *mut i32>(num); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:39:16 + --> $DIR/reference_casting.rs:37:16 | LL | let _num = &mut *(std::mem::transmute::<_, *mut i32>(num) as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _num = &mut *(std::mem::transmute::<_, *mut i32>(num) as *mut i32); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:41:16 + --> $DIR/reference_casting.rs:39:16 | LL | let _num = &mut *std::cell::UnsafeCell::raw_get( | ________________^ @@ -100,7 +100,7 @@ LL | | ); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:47:16 + --> $DIR/reference_casting.rs:45:16 | LL | let deferred = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -110,7 +110,7 @@ LL | let _num = &mut *deferred; = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:50:16 + --> $DIR/reference_casting.rs:48:16 | LL | let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32; | ---------------------------------------------------------------------------- casting happend here @@ -120,7 +120,7 @@ LL | let _num = &mut *deferred; = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:53:16 + --> $DIR/reference_casting.rs:51:16 | LL | let deferred = (std::ptr::from_ref(num) as *const i32 as *const i32).cast_mut() as *mut i32; | ---------------------------------------------------------------------------- casting happend here @@ -131,7 +131,7 @@ LL | let _num = &mut *deferred_rebind; = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:55:16 + --> $DIR/reference_casting.rs:53:16 | LL | let _num = &mut *(num as *const _ as usize as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -139,7 +139,7 @@ LL | let _num = &mut *(num as *const _ as usize as *mut i32); = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:57:16 + --> $DIR/reference_casting.rs:55:16 | LL | let _num = &mut *(std::mem::transmute::<_, *mut _>(num as *const i32) as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -147,7 +147,7 @@ LL | let _num = &mut *(std::mem::transmute::<_, *mut _>(num as *const i32) a = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:64:16 + --> $DIR/reference_casting.rs:62:16 | LL | let num = NUM as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -158,7 +158,7 @@ LL | let _num = &mut *num; = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:68:16 + --> $DIR/reference_casting.rs:66:16 | LL | let _num = &mut *(cell as *const _ as *mut i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -167,7 +167,7 @@ LL | let _num = &mut *(cell as *const _ as *mut i32); = note: even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get` error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:72:9 + --> $DIR/reference_casting.rs:70:9 | LL | &mut *((this as *const _) as *mut _) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -175,7 +175,7 @@ LL | &mut *((this as *const _) as *mut _) = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:77:18 + --> $DIR/reference_casting.rs:75:18 | LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,7 +183,7 @@ LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *con = note: for more information, visit error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - --> $DIR/reference_casting.rs:82:18 + --> $DIR/reference_casting.rs:80:18 | LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *const _) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -191,7 +191,7 @@ LL | unsafe { &mut *std::cell::UnsafeCell::raw_get(x as *const _ as *con = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:92:5 + --> $DIR/reference_casting.rs:90:5 | LL | *(a as *const _ as *mut _) = String::from("Replaced"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | *(a as *const _ as *mut _) = String::from("Replaced"); = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:94:5 + --> $DIR/reference_casting.rs:92:5 | LL | *(a as *const _ as *mut String) += " world"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL | *(a as *const _ as *mut String) += " world"; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:96:5 + --> $DIR/reference_casting.rs:94:5 | LL | *std::ptr::from_ref(num).cast_mut() += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -215,7 +215,7 @@ LL | *std::ptr::from_ref(num).cast_mut() += 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:98:5 + --> $DIR/reference_casting.rs:96:5 | LL | *std::ptr::from_ref({ num }).cast_mut() += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ LL | *std::ptr::from_ref({ num }).cast_mut() += 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:100:5 + --> $DIR/reference_casting.rs:98:5 | LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -231,7 +231,7 @@ LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:102:5 + --> $DIR/reference_casting.rs:100:5 | LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -239,7 +239,7 @@ LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:104:5 + --> $DIR/reference_casting.rs:102:5 | LL | *std::mem::transmute::<_, *mut i32>(num) += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -247,7 +247,7 @@ LL | *std::mem::transmute::<_, *mut i32>(num) += 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:106:5 + --> $DIR/reference_casting.rs:104:5 | LL | *(std::mem::transmute::<_, *mut i32>(num) as *mut i32) += 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | *(std::mem::transmute::<_, *mut i32>(num) as *mut i32) += 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:108:5 + --> $DIR/reference_casting.rs:106:5 | LL | / std::ptr::write( LL | | @@ -267,7 +267,7 @@ LL | | ); = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:113:5 + --> $DIR/reference_casting.rs:111:5 | LL | *((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -276,7 +276,7 @@ LL | *((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5; = note: even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get` error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:117:5 + --> $DIR/reference_casting.rs:115:5 | LL | let value = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -286,7 +286,7 @@ LL | *value = 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:121:5 + --> $DIR/reference_casting.rs:119:5 | LL | let value = value as *mut i32; | ----------------- casting happend here @@ -296,7 +296,7 @@ LL | *value = 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:124:5 + --> $DIR/reference_casting.rs:122:5 | LL | let value = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -306,7 +306,7 @@ LL | *value = 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:127:5 + --> $DIR/reference_casting.rs:125:5 | LL | let value = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -317,7 +317,7 @@ LL | *value_rebind = 1; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:129:5 + --> $DIR/reference_casting.rs:127:5 | LL | *(num as *const i32).cast::().cast_mut() = 2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL | *(num as *const i32).cast::().cast_mut() = 2; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:131:5 + --> $DIR/reference_casting.rs:129:5 | LL | *(num as *const _ as usize as *mut i32) = 2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -333,7 +333,7 @@ LL | *(num as *const _ as usize as *mut i32) = 2; = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:133:5 + --> $DIR/reference_casting.rs:131:5 | LL | let value = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -344,7 +344,7 @@ LL | std::ptr::write(value, 2); = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:135:5 + --> $DIR/reference_casting.rs:133:5 | LL | let value = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -355,7 +355,7 @@ LL | std::ptr::write_unaligned(value, 2); = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:137:5 + --> $DIR/reference_casting.rs:135:5 | LL | let value = num as *const i32 as *mut i32; | ----------------------------- casting happend here @@ -366,7 +366,7 @@ LL | std::ptr::write_volatile(value, 2); = note: for more information, visit error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell` - --> $DIR/reference_casting.rs:141:9 + --> $DIR/reference_casting.rs:139:9 | LL | *(this as *const _ as *mut _) = a; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 406e5c8441e..490d6a2f918 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -4,7 +4,6 @@ #![allow(dead_code)] #![feature(never_type)] #![feature(pointer_is_aligned)] -#![feature(ptr_from_ref)] #![feature(strict_provenance)] use std::mem::size_of; -- cgit 1.4.1-3-g733a5 From 5b0b7cd8f9181037dd5fb8a0f77826d8261e43ad Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Dec 2023 15:55:51 +0000 Subject: Move type relations into submodule in rustc_infer --- compiler/rustc_infer/src/infer/combine.rs | 611 --------------------- compiler/rustc_infer/src/infer/equate.rs | 190 ------- compiler/rustc_infer/src/infer/generalize.rs | 555 ------------------- compiler/rustc_infer/src/infer/glb.rs | 154 ------ .../rustc_infer/src/infer/higher_ranked/mod.rs | 2 +- compiler/rustc_infer/src/infer/lattice.rs | 128 ----- compiler/rustc_infer/src/infer/lub.rs | 154 ------ compiler/rustc_infer/src/infer/mod.rs | 12 +- compiler/rustc_infer/src/infer/nll_relate/mod.rs | 4 +- compiler/rustc_infer/src/infer/relate/combine.rs | 611 +++++++++++++++++++++ compiler/rustc_infer/src/infer/relate/equate.rs | 188 +++++++ .../rustc_infer/src/infer/relate/generalize.rs | 555 +++++++++++++++++++ compiler/rustc_infer/src/infer/relate/glb.rs | 153 ++++++ compiler/rustc_infer/src/infer/relate/lattice.rs | 128 +++++ compiler/rustc_infer/src/infer/relate/lub.rs | 153 ++++++ compiler/rustc_infer/src/infer/relate/mod.rs | 11 + compiler/rustc_infer/src/infer/relate/sub.rs | 217 ++++++++ compiler/rustc_infer/src/infer/sub.rs | 217 -------- .../occurs-check/associated-type.old.stderr | 16 +- .../next-solver/issue-118950-root-region.stderr | 16 +- 20 files changed, 2038 insertions(+), 2037 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/combine.rs delete mode 100644 compiler/rustc_infer/src/infer/equate.rs delete mode 100644 compiler/rustc_infer/src/infer/generalize.rs delete mode 100644 compiler/rustc_infer/src/infer/glb.rs delete mode 100644 compiler/rustc_infer/src/infer/lattice.rs delete mode 100644 compiler/rustc_infer/src/infer/lub.rs create mode 100644 compiler/rustc_infer/src/infer/relate/combine.rs create mode 100644 compiler/rustc_infer/src/infer/relate/equate.rs create mode 100644 compiler/rustc_infer/src/infer/relate/generalize.rs create mode 100644 compiler/rustc_infer/src/infer/relate/glb.rs create mode 100644 compiler/rustc_infer/src/infer/relate/lattice.rs create mode 100644 compiler/rustc_infer/src/infer/relate/lub.rs create mode 100644 compiler/rustc_infer/src/infer/relate/mod.rs create mode 100644 compiler/rustc_infer/src/infer/relate/sub.rs delete mode 100644 compiler/rustc_infer/src/infer/sub.rs diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs deleted file mode 100644 index fe58fc7122c..00000000000 --- a/compiler/rustc_infer/src/infer/combine.rs +++ /dev/null @@ -1,611 +0,0 @@ -//! There are four type combiners: [Equate], [Sub], [Lub], and [Glb]. -//! Each implements the trait [TypeRelation] and contains methods for -//! combining two instances of various things and yielding a new instance. -//! These combiner methods always yield a `Result`. To relate two -//! types, you can use `infcx.at(cause, param_env)` which then allows -//! you to use the relevant methods of [At](super::at::At). -//! -//! Combiners mostly do their specific behavior and then hand off the -//! bulk of the work to [InferCtxt::super_combine_tys] and -//! [InferCtxt::super_combine_consts]. -//! -//! Combining two types may have side-effects on the inference contexts -//! which can be undone by using snapshots. You probably want to use -//! either [InferCtxt::commit_if_ok] or [InferCtxt::probe]. -//! -//! On success, the LUB/GLB operations return the appropriate bound. The -//! return value of `Equate` or `Sub` shouldn't really be used. -//! -//! ## Contravariance -//! -//! We explicitly track which argument is expected using -//! [TypeRelation::a_is_expected], so when dealing with contravariance -//! this should be correctly updated. - -use super::equate::Equate; -use super::glb::Glb; -use super::lub::Lub; -use super::sub::Sub; -use super::{DefineOpaqueTypes, InferCtxt, TypeTrace}; -use crate::infer::generalize::{self, CombineDelegate, Generalization}; -use crate::traits::{Obligation, PredicateObligations}; -use rustc_middle::infer::canonical::OriginalQueryValues; -use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::relate::{RelateResult, TypeRelation}; -use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::ty::{AliasRelationDirection, TyVar}; -use rustc_middle::ty::{IntType, UintType}; -use rustc_span::DUMMY_SP; - -#[derive(Clone)] -pub struct CombineFields<'infcx, 'tcx> { - pub infcx: &'infcx InferCtxt<'tcx>, - pub trace: TypeTrace<'tcx>, - pub cause: Option, - pub param_env: ty::ParamEnv<'tcx>, - pub obligations: PredicateObligations<'tcx>, - pub define_opaque_types: DefineOpaqueTypes, -} - -impl<'tcx> InferCtxt<'tcx> { - pub fn super_combine_tys( - &self, - relation: &mut R, - a: Ty<'tcx>, - b: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - R: ObligationEmittingRelation<'tcx>, - { - let a_is_expected = relation.a_is_expected(); - debug_assert!(!a.has_escaping_bound_vars()); - debug_assert!(!b.has_escaping_bound_vars()); - - match (a.kind(), b.kind()) { - // Relate integral variables to other types - (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { - self.inner - .borrow_mut() - .int_unification_table() - .unify_var_var(a_id, b_id) - .map_err(|e| int_unification_error(a_is_expected, e))?; - Ok(a) - } - (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => { - self.unify_integral_variable(a_is_expected, v_id, IntType(v)) - } - (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => { - self.unify_integral_variable(!a_is_expected, v_id, IntType(v)) - } - (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => { - self.unify_integral_variable(a_is_expected, v_id, UintType(v)) - } - (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => { - self.unify_integral_variable(!a_is_expected, v_id, UintType(v)) - } - - // Relate floating-point variables to other types - (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => { - self.inner - .borrow_mut() - .float_unification_table() - .unify_var_var(a_id, b_id) - .map_err(|e| float_unification_error(a_is_expected, e))?; - Ok(a) - } - (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => { - self.unify_float_variable(a_is_expected, v_id, v) - } - (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => { - self.unify_float_variable(!a_is_expected, v_id, v) - } - - // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm. - (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..)) - if self.next_trait_solver() => - { - bug!( - "We do not expect to encounter `TyVar` this late in combine \ - -- they should have been handled earlier" - ) - } - (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))) - | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _) - if self.next_trait_solver() => - { - bug!("We do not expect to encounter `Fresh` variables in the new solver") - } - - (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => { - relation.register_type_relate_obligation(a, b); - Ok(a) - } - - // All other cases of inference are errors - (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) - } - - // During coherence, opaque types should be treated as *possibly* - // equal to any other type (except for possibly itself). This is an - // extremely heavy hammer, but can be relaxed in a fowards-compatible - // way later. - (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => { - relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]); - Ok(a) - } - - _ => ty::relate::structurally_relate_tys(relation, a, b), - } - } - - pub fn super_combine_consts( - &self, - relation: &mut R, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> - where - R: ObligationEmittingRelation<'tcx>, - { - debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); - debug_assert!(!a.has_escaping_bound_vars()); - debug_assert!(!b.has_escaping_bound_vars()); - if a == b { - return Ok(a); - } - - let a = self.shallow_resolve(a); - let b = self.shallow_resolve(b); - - // We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the - // correct type for the generic param they are an argument for. However there have been a number of cases - // historically where asserting that the types are equal has found bugs in the compiler so this is valuable - // to check even if it is a bit nasty impl wise :( - // - // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find - // ourselves with a check to find bugs being required for code to compile because it made inference progress. - let compatible_types = self.probe(|_| { - if a.ty() == b.ty() { - return Ok(()); - } - - // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the - // two const param's types are able to be equal has to go through a canonical query with the actual logic - // in `rustc_trait_selection`. - let canonical = self.canonicalize_query( - relation.param_env().and((a.ty(), b.ty())), - &mut OriginalQueryValues::default(), - ); - self.tcx.check_tys_might_be_eq(canonical).map_err(|_| { - self.tcx.sess.span_delayed_bug( - DUMMY_SP, - format!("cannot relate consts of different types (a={a:?}, b={b:?})",), - ) - }) - }); - - // If the consts have differing types, just bail with a const error with - // the expected const's type. Specifically, we don't want const infer vars - // to do any type shapeshifting before and after resolution. - if let Err(guar) = compatible_types { - // HACK: equating both sides with `[const error]` eagerly prevents us - // from leaving unconstrained inference vars during things like impl - // matching in the solver. - let a_error = ty::Const::new_error(self.tcx, guar, a.ty()); - if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() { - return self.unify_const_variable(vid, a_error, relation.param_env()); - } - let b_error = ty::Const::new_error(self.tcx, guar, b.ty()); - if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() { - return self.unify_const_variable(vid, b_error, relation.param_env()); - } - - return Ok(if relation.a_is_expected() { a_error } else { b_error }); - } - - match (a.kind(), b.kind()) { - ( - ty::ConstKind::Infer(InferConst::Var(a_vid)), - ty::ConstKind::Infer(InferConst::Var(b_vid)), - ) => { - self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid); - return Ok(a); - } - - ( - ty::ConstKind::Infer(InferConst::EffectVar(a_vid)), - ty::ConstKind::Infer(InferConst::EffectVar(b_vid)), - ) => { - self.inner - .borrow_mut() - .effect_unification_table() - .unify_var_var(a_vid, b_vid) - .map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?; - return Ok(a); - } - - // All other cases of inference with other variables are errors. - ( - ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)), - ty::ConstKind::Infer(_), - ) - | ( - ty::ConstKind::Infer(_), - ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)), - ) => { - bug!( - "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}" - ) - } - - (ty::ConstKind::Infer(InferConst::Var(vid)), _) => { - return self.unify_const_variable(vid, b, relation.param_env()); - } - - (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { - return self.unify_const_variable(vid, a, relation.param_env()); - } - - (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => { - return self.unify_effect_variable( - relation.a_is_expected(), - vid, - EffectVarValue::Const(b), - ); - } - - (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => { - return self.unify_effect_variable( - !relation.a_is_expected(), - vid, - EffectVarValue::Const(a), - ); - } - - (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) - if self.tcx.features().generic_const_exprs || self.next_trait_solver() => - { - let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) }; - - relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() { - ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Equate, - ) - } else { - ty::PredicateKind::ConstEquate(a, b) - })]); - - return Ok(b); - } - _ => {} - } - - ty::relate::structurally_relate_consts(relation, a, b) - } - - /// Unifies the const variable `target_vid` with the given constant. - /// - /// This also tests if the given const `ct` contains an inference variable which was previously - /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` - /// would result in an infinite type as we continuously replace an inference variable - /// in `ct` with `ct` itself. - /// - /// This is especially important as unevaluated consts use their parents generics. - /// They therefore often contain unused args, making these errors far more likely. - /// - /// A good example of this is the following: - /// - /// ```compile_fail,E0308 - /// #![feature(generic_const_exprs)] - /// - /// fn bind(value: [u8; N]) -> [u8; 3 + 4] { - /// todo!() - /// } - /// - /// fn main() { - /// let mut arr = Default::default(); - /// arr = bind(arr); - /// } - /// ``` - /// - /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics - /// of `fn bind` (meaning that its args contain `N`). - /// - /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. - /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. - /// - /// As `3 + 4` contains `N` in its args, this must not succeed. - /// - /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. - #[instrument(level = "debug", skip(self))] - fn unify_const_variable( - &self, - target_vid: ty::ConstVid, - ct: ty::Const<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let span = - self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span; - // FIXME(generic_const_exprs): Occurs check failures for unevaluated - // constants and generic expressions are not yet handled correctly. - let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize( - self, - &mut CombineDelegate { infcx: self, span, param_env }, - ct, - target_vid, - ty::Variance::Invariant, - )?; - - self.inner.borrow_mut().const_unification_table().union_value( - target_vid, - ConstVarValue { - origin: ConstVariableOrigin { - kind: ConstVariableOriginKind::ConstInference, - span: DUMMY_SP, - }, - val: ConstVariableValue::Known { value }, - }, - ); - Ok(value) - } - - fn unify_integral_variable( - &self, - vid_is_expected: bool, - vid: ty::IntVid, - val: ty::IntVarValue, - ) -> RelateResult<'tcx, Ty<'tcx>> { - self.inner - .borrow_mut() - .int_unification_table() - .unify_var_value(vid, Some(val)) - .map_err(|e| int_unification_error(vid_is_expected, e))?; - match val { - IntType(v) => Ok(Ty::new_int(self.tcx, v)), - UintType(v) => Ok(Ty::new_uint(self.tcx, v)), - } - } - - fn unify_float_variable( - &self, - vid_is_expected: bool, - vid: ty::FloatVid, - val: ty::FloatTy, - ) -> RelateResult<'tcx, Ty<'tcx>> { - self.inner - .borrow_mut() - .float_unification_table() - .unify_var_value(vid, Some(ty::FloatVarValue(val))) - .map_err(|e| float_unification_error(vid_is_expected, e))?; - Ok(Ty::new_float(self.tcx, val)) - } - - fn unify_effect_variable( - &self, - vid_is_expected: bool, - vid: ty::EffectVid, - val: EffectVarValue<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.inner - .borrow_mut() - .effect_unification_table() - .unify_var_value(vid, Some(val)) - .map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?; - Ok(val.as_const(self.tcx)) - } -} - -impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { - pub fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'tcx> { - Equate::new(self, a_is_expected) - } - - pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> { - Sub::new(self, a_is_expected) - } - - pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> { - Lub::new(self, a_is_expected) - } - - pub fn glb<'a>(&'a mut self, a_is_expected: bool) -> Glb<'a, 'infcx, 'tcx> { - Glb::new(self, a_is_expected) - } - - /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`. - /// The idea is that we should ensure that the type `a_ty` is equal - /// to, a subtype of, or a supertype of (respectively) the type - /// to which `b_vid` is bound. - /// - /// Since `b_vid` has not yet been instantiated with a type, we - /// will first instantiate `b_vid` with a *generalized* version - /// of `a_ty`. Generalization introduces other inference - /// variables wherever subtyping could occur. - #[instrument(skip(self), level = "debug")] - pub fn instantiate( - &mut self, - a_ty: Ty<'tcx>, - ambient_variance: ty::Variance, - b_vid: ty::TyVid, - a_is_expected: bool, - ) -> RelateResult<'tcx, ()> { - // Get the actual variable that b_vid has been inferred to - debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown()); - - // Generalize type of `a_ty` appropriately depending on the - // direction. As an example, assume: - // - // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an - // inference variable, - // - and `dir` == `SubtypeOf`. - // - // Then the generalized form `b_ty` would be `&'?2 ?3`, where - // `'?2` and `?3` are fresh region/type inference - // variables. (Down below, we will relate `a_ty <: b_ty`, - // adding constraints like `'x: '?2` and `?1 <: ?3`.) - let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize( - self.infcx, - &mut CombineDelegate { - infcx: self.infcx, - param_env: self.param_env, - span: self.trace.span(), - }, - a_ty, - b_vid, - ambient_variance, - )?; - - // Constrain `b_vid` to the generalized type `b_ty`. - if let &ty::Infer(TyVar(b_ty_vid)) = b_ty.kind() { - self.infcx.inner.borrow_mut().type_variables().equate(b_vid, b_ty_vid); - } else { - self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); - } - - if needs_wf { - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - b_ty.into(), - ))), - )); - } - - // Finally, relate `b_ty` to `a_ty`, as described in previous comment. - // - // FIXME(#16847): This code is non-ideal because all these subtype - // relations wind up attributed to the same spans. We need - // to associate causes/spans with each of the relations in - // the stack to get this right. - if b_ty.is_ty_var() { - // This happens for cases like `::Assoc == ?0`. - // We can't instantiate `?0` here as that would result in a - // cyclic type. We instead delay the unification in case - // the alias can be normalized to something which does not - // mention `?0`. - if self.infcx.next_trait_solver() { - let (lhs, rhs, direction) = match ambient_variance { - ty::Variance::Invariant => { - (a_ty.into(), b_ty.into(), AliasRelationDirection::Equate) - } - ty::Variance::Covariant => { - (a_ty.into(), b_ty.into(), AliasRelationDirection::Subtype) - } - ty::Variance::Contravariant => { - (b_ty.into(), a_ty.into(), AliasRelationDirection::Subtype) - } - ty::Variance::Bivariant => unreachable!("bivariant generalization"), - }; - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::PredicateKind::AliasRelate(lhs, rhs, direction), - )); - } else { - match a_ty.kind() { - &ty::Alias(ty::AliasKind::Projection, data) => { - // FIXME: This does not handle subtyping correctly, we could - // instead create a new inference variable for `a_ty`, emitting - // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`. - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() }, - )) - } - // The old solver only accepts projection predicates for associated types. - ty::Alias( - ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque, - _, - ) => return Err(TypeError::CyclicTy(a_ty)), - _ => bug!("generalizated `{a_ty:?} to infer, not an alias"), - } - } - } else { - match ambient_variance { - ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty), - ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty), - ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance( - ty::Contravariant, - ty::VarianceDiagInfo::default(), - a_ty, - b_ty, - ), - ty::Variance::Bivariant => unreachable!("bivariant generalization"), - }?; - } - - Ok(()) - } - - pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.obligations.extend(obligations.into_iter()); - } - - pub fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.obligations.extend(obligations.into_iter().map(|to_pred| { - Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred) - })) - } -} - -pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { - /// Register obligations that must hold in order for this relation to hold - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); - - /// Register predicates that must hold in order for this relation to hold. Uses - /// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should - /// be used if control over the obligation causes is required. - fn register_predicates(&mut self, obligations: impl IntoIterator>); - - /// Register an obligation that both types must be related to each other according to - /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`] - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - self.alias_relate_direction(), - ))]); - } - - /// Relation direction emitted for `AliasRelate` predicates, corresponding to the direction - /// of the relation. - fn alias_relate_direction(&self) -> ty::AliasRelationDirection; -} - -fn int_unification_error<'tcx>( - a_is_expected: bool, - v: (ty::IntVarValue, ty::IntVarValue), -) -> TypeError<'tcx> { - let (a, b) = v; - TypeError::IntMismatch(ExpectedFound::new(a_is_expected, a, b)) -} - -fn float_unification_error<'tcx>( - a_is_expected: bool, - v: (ty::FloatVarValue, ty::FloatVarValue), -) -> TypeError<'tcx> { - let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v; - TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b)) -} - -fn effect_unification_error<'tcx>( - _tcx: TyCtxt<'tcx>, - _a_is_expected: bool, - (_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>), -) -> TypeError<'tcx> { - bug!("unexpected effect unification error") -} diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs deleted file mode 100644 index 5d929394eb0..00000000000 --- a/compiler/rustc_infer/src/infer/equate.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::infer::DefineOpaqueTypes; -use crate::traits::PredicateObligations; - -use super::combine::{CombineFields, ObligationEmittingRelation}; -use super::Subtype; - -use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::TyVar; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; - -use rustc_hir::def_id::DefId; - -/// Ensures `a` is made equal to `b`. Returns `a` on success. -pub struct Equate<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, -} - -impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> { - pub fn new( - fields: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, - ) -> Equate<'combine, 'infcx, 'tcx> { - Equate { fields, a_is_expected } - } -} - -impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "Equate" - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn a_is_expected(&self) -> bool { - self.a_is_expected - } - - fn relate_item_args( - &mut self, - _item_def_id: DefId, - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - // N.B., once we are equating types, we don't care about - // variance, so don't try to lookup the variance here. This - // also avoids some cycles (e.g., #41849) since looking up - // variance requires computing types which can require - // performing trait matching (which then performs equality - // unification). - - relate::relate_args_invariantly(self, a_arg, b_arg) - } - - fn relate_with_variance>( - &mut self, - _: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - self.relate(a, b) - } - - #[instrument(skip(self), level = "debug")] - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - if a == b { - return Ok(a); - } - - trace!(a = ?a.kind(), b = ?b.kind()); - - let infcx = self.fields.infcx; - - let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); - - match (a.kind(), b.kind()) { - (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { - infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); - } - - (&ty::Infer(TyVar(a_id)), _) => { - self.fields.instantiate(b, ty::Invariant, a_id, self.a_is_expected)?; - } - - (_, &ty::Infer(TyVar(b_id))) => { - self.fields.instantiate(a, ty::Invariant, b_id, self.a_is_expected)?; - } - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id => { - self.fields.infcx.super_combine_tys(self, a, b)?; - } - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if self.fields.define_opaque_types == DefineOpaqueTypes::Yes - && def_id.is_local() - && !self.fields.infcx.next_trait_solver() => - { - self.fields.obligations.extend( - infcx - .handle_opaque_type( - a, - b, - self.a_is_expected(), - &self.fields.trace.cause, - self.param_env(), - )? - .obligations, - ); - } - _ => { - self.fields.infcx.super_combine_tys(self, a, b)?; - } - } - - Ok(a) - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); - let origin = Subtype(Box::new(self.fields.trace.clone())); - self.fields - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .make_eqregion(origin, a, b); - Ok(a) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // A binder is equal to itself if it's structurally equal to itself - if a == b { - return Ok(a); - } - - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; - self.fields.higher_ranked_sub(b, a, self.a_is_expected)?; - } else { - // Fast path for the common case. - self.relate(a.skip_binder(), b.skip_binder())?; - } - Ok(a) - } -} - -impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> { - fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.fields.register_predicates(obligations); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.fields.register_obligations(obligations); - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - ty::AliasRelationDirection::Equate - } -} diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs deleted file mode 100644 index 383f3bdbe23..00000000000 --- a/compiler/rustc_infer/src/infer/generalize.rs +++ /dev/null @@ -1,555 +0,0 @@ -use std::mem; - -use rustc_data_structures::sso::SsoHashMap; -use rustc_hir::def_id::DefId; -use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::visit::MaxUniverse; -use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; -use rustc_span::Span; - -use crate::infer::nll_relate::TypeRelatingDelegate; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue}; -use crate::infer::{InferCtxt, RegionVariableOrigin}; - -/// Attempts to generalize `term` for the type variable `for_vid`. -/// This checks for cycles -- that is, whether the type `term` -/// references `for_vid`. -pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into> + Relate<'tcx>>( - infcx: &InferCtxt<'tcx>, - delegate: &mut D, - term: T, - for_vid: impl Into, - ambient_variance: ty::Variance, -) -> RelateResult<'tcx, Generalization> { - let (for_universe, root_vid) = match for_vid.into() { - ty::TermVid::Ty(ty_vid) => ( - infcx.probe_ty_var(ty_vid).unwrap_err(), - ty::TermVid::Ty(infcx.inner.borrow_mut().type_variables().sub_root_var(ty_vid)), - ), - ty::TermVid::Const(ct_vid) => ( - infcx.probe_const_var(ct_vid).unwrap_err(), - ty::TermVid::Const(infcx.inner.borrow_mut().const_unification_table().find(ct_vid).vid), - ), - }; - - let mut generalizer = Generalizer { - infcx, - delegate, - ambient_variance, - root_vid, - for_universe, - root_term: term.into(), - in_alias: false, - needs_wf: false, - cache: Default::default(), - }; - - assert!(!term.has_escaping_bound_vars()); - let value_may_be_infer = generalizer.relate(term, term)?; - let needs_wf = generalizer.needs_wf; - Ok(Generalization { value_may_be_infer, needs_wf }) -} - -/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking -/// in the generalizer code. -pub(super) trait GeneralizerDelegate<'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx>; - - fn forbid_inference_vars() -> bool; - - fn span(&self) -> Span; - - fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; -} - -pub(super) struct CombineDelegate<'cx, 'tcx> { - pub infcx: &'cx InferCtxt<'tcx>, - pub param_env: ty::ParamEnv<'tcx>, - pub span: Span, -} - -impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } - - fn forbid_inference_vars() -> bool { - false - } - - fn span(&self) -> Span { - self.span - } - - fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - // FIXME: This is non-ideal because we don't give a - // very descriptive origin for this region variable. - self.infcx - .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe) - } -} - -impl<'tcx, T> GeneralizerDelegate<'tcx> for T -where - T: TypeRelatingDelegate<'tcx>, -{ - fn param_env(&self) -> ty::ParamEnv<'tcx> { - >::param_env(self) - } - - fn forbid_inference_vars() -> bool { - >::forbid_inference_vars() - } - - fn span(&self) -> Span { - >::span(&self) - } - - fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { - >::generalize_existential(self, universe) - } -} - -/// The "generalizer" is used when handling inference variables. -/// -/// The basic strategy for handling a constraint like `?A <: B` is to -/// apply a "generalization strategy" to the term `B` -- this replaces -/// all the lifetimes in the term `B` with fresh inference variables. -/// (You can read more about the strategy in this [blog post].) -/// -/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x -/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the -/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which -/// establishes `'0: 'x` as a constraint. -/// -/// [blog post]: https://is.gd/0hKvIr -struct Generalizer<'me, 'tcx, D> { - infcx: &'me InferCtxt<'tcx>, - - /// This is used to abstract the behaviors of the three previous - /// generalizer-like implementations (`Generalizer`, `TypeGeneralizer`, - /// and `ConstInferUnifier`). See [`GeneralizerDelegate`] for more - /// information. - delegate: &'me mut D, - - /// After we generalize this type, we are going to relate it to - /// some other type. What will be the variance at this point? - ambient_variance: ty::Variance, - - /// The vid of the type variable that is in the process of being - /// instantiated. If we find this within the value we are folding, - /// that means we would have created a cyclic value. - root_vid: ty::TermVid, - - /// The universe of the type variable that is in the process of being - /// instantiated. If we find anything that this universe cannot name, - /// we reject the relation. - for_universe: ty::UniverseIndex, - - /// The root term (const or type) we're generalizing. Used for cycle errors. - root_term: Term<'tcx>, - - cache: SsoHashMap, Ty<'tcx>>, - - /// This is set once we're generalizing the arguments of an alias. - /// - /// This is necessary to correctly handle - /// `::Assoc>::Assoc == ?0`. This equality can - /// hold by either normalizing the outer or the inner associated type. - in_alias: bool, - - /// See the field `needs_wf` in `Generalization`. - needs_wf: bool, -} - -impl<'tcx, D> Generalizer<'_, 'tcx, D> { - /// Create an error that corresponds to the term kind in `root_term` - fn cyclic_term_error(&self) -> TypeError<'tcx> { - match self.root_term.unpack() { - ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty), - ty::TermKind::Const(ct) => TypeError::CyclicConst(ct), - } - } -} - -impl<'tcx, D> TypeRelation<'tcx> for Generalizer<'_, 'tcx, D> -where - D: GeneralizerDelegate<'tcx>, -{ - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.delegate.param_env() - } - - fn tag(&self) -> &'static str { - "Generalizer" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn relate_item_args( - &mut self, - item_def_id: DefId, - a_subst: ty::GenericArgsRef<'tcx>, - b_subst: ty::GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { - if self.ambient_variance == ty::Variance::Invariant { - // Avoid fetching the variance if we are in an invariant - // context; no need, and it can induce dependency cycles - // (e.g., #41849). - relate::relate_args_invariantly(self, a_subst, b_subst) - } else { - let tcx = self.tcx(); - let opt_variances = tcx.variances_of(item_def_id); - relate::relate_args_with_variances( - self, - item_def_id, - opt_variances, - a_subst, - b_subst, - false, - ) - } - } - - #[instrument(level = "debug", skip(self, variance, b), ret)] - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - debug!(?self.ambient_variance, "new ambient variance"); - let r = self.relate(a, b)?; - self.ambient_variance = old_ambient_variance; - Ok(r) - } - - #[instrument(level = "debug", skip(self, t2), ret)] - fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == - - if let Some(&result) = self.cache.get(&t) { - return Ok(result); - } - - // Check to see whether the type we are generalizing references - // any other type variable related to `vid` via - // subtyping. This is basically our "occurs check", preventing - // us from creating infinitely sized types. - let g = match *t.kind() { - ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) - if D::forbid_inference_vars() => - { - bug!("unexpected inference variable encountered in NLL generalization: {t}"); - } - - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected infer type: {t}") - } - - ty::Infer(ty::TyVar(vid)) => { - let mut inner = self.infcx.inner.borrow_mut(); - let vid = inner.type_variables().root_var(vid); - let sub_vid = inner.type_variables().sub_root_var(vid); - - if ty::TermVid::Ty(sub_vid) == self.root_vid { - // If sub-roots are equal, then `root_vid` and - // `vid` are related via subtyping. - Err(self.cyclic_term_error()) - } else { - let probe = inner.type_variables().probe(vid); - match probe { - TypeVariableValue::Known { value: u } => { - drop(inner); - self.relate(u, u) - } - TypeVariableValue::Unknown { universe } => { - match self.ambient_variance { - // Invariant: no need to make a fresh type variable - // if we can name the universe. - ty::Invariant => { - if self.for_universe.can_name(universe) { - return Ok(t); - } - } - - // Bivariant: make a fresh var, but we - // may need a WF predicate. See - // comment on `needs_wf` field for - // more info. - ty::Bivariant => self.needs_wf = true, - - // Co/contravariant: this will be - // sufficiently constrained later on. - ty::Covariant | ty::Contravariant => (), - } - - let origin = inner.type_variables().var_origin(vid); - let new_var_id = - inner.type_variables().new_var(self.for_universe, origin); - let u = Ty::new_var(self.tcx(), new_var_id); - - // Record that we replaced `vid` with `new_var_id` as part of a generalization - // operation. This is needed to detect cyclic types. To see why, see the - // docs in the `type_variables` module. - inner.type_variables().sub(vid, new_var_id); - debug!("replacing original vid={:?} with new={:?}", vid, u); - Ok(u) - } - } - } - } - - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { - // No matter what mode we are in, - // integer/floating-point types must be equal to be - // relatable. - Ok(t) - } - - ty::Placeholder(placeholder) => { - if self.for_universe.can_name(placeholder.universe) { - Ok(t) - } else { - debug!( - "root universe {:?} cannot name placeholder in universe {:?}", - self.for_universe, placeholder.universe - ); - Err(TypeError::Mismatch) - } - } - - ty::Alias(kind, data) => { - // An occurs check failure inside of an alias does not mean - // that the types definitely don't unify. We may be able - // to normalize the alias after all. - // - // We handle this by lazily equating the alias and generalizing - // it to an inference variable. - let is_nested_alias = mem::replace(&mut self.in_alias, true); - let result = match self.relate(data, data) { - Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)), - Err(e) => { - if is_nested_alias { - return Err(e); - } else { - let mut visitor = MaxUniverse::new(); - t.visit_with(&mut visitor); - let infer_replacement_is_complete = - self.for_universe.can_name(visitor.max_universe()) - && !t.has_escaping_bound_vars(); - if !infer_replacement_is_complete { - warn!("may incompletely handle alias type: {t:?}"); - } - - debug!("generalization failure in alias"); - Ok(self.infcx.next_ty_var_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.delegate.span(), - }, - self.for_universe, - )) - } - } - }; - self.in_alias = is_nested_alias; - result - } - - _ => relate::structurally_relate_tys(self, t, t), - }?; - - self.cache.insert(t, g); - Ok(g) - } - - #[instrument(level = "debug", skip(self, r2), ret)] - fn regions( - &mut self, - r: ty::Region<'tcx>, - r2: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == - - match *r { - // Never make variables for regions bound within the type itself, - // nor for erased regions. - ty::ReBound(..) | ty::ReErased => { - return Ok(r); - } - - // It doesn't really matter for correctness if we generalize ReError, - // since we're already on a doomed compilation path. - ty::ReError(_) => { - return Ok(r); - } - - ty::RePlaceholder(..) - | ty::ReVar(..) - | ty::ReStatic - | ty::ReEarlyParam(..) - | ty::ReLateParam(..) => { - // see common code below - } - } - - // If we are in an invariant context, we can re-use the region - // as is, unless it happens to be in some universe that we - // can't name. - if let ty::Invariant = self.ambient_variance { - let r_universe = self.infcx.universe_of_region(r); - if self.for_universe.can_name(r_universe) { - return Ok(r); - } - } - - Ok(self.delegate.generalize_region(self.for_universe)) - } - - #[instrument(level = "debug", skip(self, c2), ret)] - fn consts( - &mut self, - c: ty::Const<'tcx>, - c2: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be == - - match c.kind() { - ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { - bug!("unexpected inference variable encountered in NLL generalization: {:?}", c); - } - ty::ConstKind::Infer(InferConst::Var(vid)) => { - // If root const vids are equal, then `root_vid` and - // `vid` are related and we'd be inferring an infinitely - // deep const. - if ty::TermVid::Const( - self.infcx.inner.borrow_mut().const_unification_table().find(vid).vid, - ) == self.root_vid - { - return Err(self.cyclic_term_error()); - } - - let mut inner = self.infcx.inner.borrow_mut(); - let variable_table = &mut inner.const_unification_table(); - let var_value = variable_table.probe_value(vid); - match var_value.val { - ConstVariableValue::Known { value: u } => { - drop(inner); - self.relate(u, u) - } - ConstVariableValue::Unknown { universe } => { - if self.for_universe.can_name(universe) { - Ok(c) - } else { - let new_var_id = variable_table - .new_key(ConstVarValue { - origin: var_value.origin, - val: ConstVariableValue::Unknown { - universe: self.for_universe, - }, - }) - .vid; - Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty())) - } - } - } - } - ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c), - // FIXME: remove this branch once `structurally_relate_consts` is fully - // structural. - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { - let args = self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - args, - args, - )?; - Ok(ty::Const::new_unevaluated( - self.tcx(), - ty::UnevaluatedConst { def, args }, - c.ty(), - )) - } - ty::ConstKind::Placeholder(placeholder) => { - if self.for_universe.can_name(placeholder.universe) { - Ok(c) - } else { - debug!( - "root universe {:?} cannot name placeholder in universe {:?}", - self.for_universe, placeholder.universe - ); - Err(TypeError::Mismatch) - } - } - _ => relate::structurally_relate_consts(self, c, c), - } - } - - #[instrument(level = "debug", skip(self), ret)] - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - _: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - let result = self.relate(a.skip_binder(), a.skip_binder())?; - Ok(a.rebind(result)) - } -} - -/// Result from a generalization operation. This includes -/// not only the generalized type, but also a bool flag -/// indicating whether further WF checks are needed. -#[derive(Debug)] -pub(super) struct Generalization { - /// When generalizing `::Assoc` or - /// `::Assoc>>::Assoc` - /// for `?0` generalization returns an inference - /// variable. - /// - /// This has to be handled wotj care as it can - /// otherwise very easily result in infinite - /// recursion. - pub(super) value_may_be_infer: T, - - /// If true, then the generalized type may not be well-formed, - /// even if the source type is well-formed, so we should add an - /// additional check to enforce that it is. This arises in - /// particular around 'bivariant' type parameters that are only - /// constrained by a where-clause. As an example, imagine a type: - /// - /// struct Foo where A: Iterator { - /// data: A - /// } - /// - /// here, `A` will be covariant, but `B` is - /// unconstrained. However, whatever it is, for `Foo` to be WF, it - /// must be equal to `A::Item`. If we have an input `Foo`, - /// then after generalization we will wind up with a type like - /// `Foo`. When we enforce that `Foo <: Foo` (or `>:`), we will wind up with the requirement that `?A - /// <: ?C`, but no particular relationship between `?B` and `?D` - /// (after all, we do not know the variance of the normalized form - /// of `A::Item` with respect to `A`). If we do nothing else, this - /// may mean that `?D` goes unconstrained (as in #41677). So, in - /// this scenario where we create a new type variable in a - /// bivariant context, we set the `needs_wf` flag to true. This - /// will force the calling code to check that `WF(Foo)` - /// holds, which in turn implies that `?C::Item == ?D`. So once - /// `?C` is constrained, that should suffice to restrict `?D`. - pub(super) needs_wf: bool, -} diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs deleted file mode 100644 index 2f659d9a665..00000000000 --- a/compiler/rustc_infer/src/infer/glb.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! Greatest lower bound. See [`lattice`]. - -use super::combine::{CombineFields, ObligationEmittingRelation}; -use super::lattice::{self, LatticeDir}; -use super::Subtype; -use super::{DefineOpaqueTypes, InferCtxt}; - -use crate::traits::{ObligationCause, PredicateObligations}; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; - -/// "Greatest lower bound" (common subtype) -pub struct Glb<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, -} - -impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { - pub fn new( - fields: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, - ) -> Glb<'combine, 'infcx, 'tcx> { - Glb { fields, a_is_expected } - } -} - -impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "Glb" - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn a_is_expected(&self) -> bool { - self.a_is_expected - } - - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), - ty::Covariant => self.relate(a, b), - // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a), - ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b), - } - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - lattice::super_lattice_tys(self, a, b) - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); - - let origin = Subtype(Box::new(self.fields.trace.clone())); - // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( - self.tcx(), - origin, - a, - b, - )) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // GLB of a binder and itself is just itself - if a == b { - return Ok(a); - } - - debug!("binders(a={:?}, b={:?})", a, b); - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - // When higher-ranked types are involved, computing the GLB is - // very challenging, switch to invariance. This is obviously - // overly conservative but works ok in practice. - self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - a, - b, - )?; - Ok(a) - } else { - Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) - } - } -} - -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let mut sub = self.fields.sub(self.a_is_expected); - sub.relate(v, a)?; - sub.relate(v, b)?; - Ok(()) - } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } -} - -impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { - fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.fields.register_predicates(obligations); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.fields.register_obligations(obligations); - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::AliasRelationDirection::Equate - } -} diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index 510b1797d3c..0de988efaee 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -1,8 +1,8 @@ //! Helper routines for higher-ranked things. See the `doc` module at //! the end of the file for details. -use super::combine::CombineFields; use super::{HigherRankedType, InferCtxt}; +use crate::infer::relate::combine::CombineFields; use crate::infer::CombinedSnapshot; use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs deleted file mode 100644 index 9ef35429fe3..00000000000 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! # Lattice variables -//! -//! Generic code for operating on [lattices] of inference variables -//! that are characterized by an upper- and lower-bound. -//! -//! The code is defined quite generically so that it can be -//! applied both to type variables, which represent types being inferred, -//! and fn variables, which represent function types being inferred. -//! (It may eventually be applied to their types as well.) -//! In some cases, the functions are also generic with respect to the -//! operation on the lattice (GLB vs LUB). -//! -//! ## Note -//! -//! Although all the functions are generic, for simplicity, comments in the source code -//! generally refer to type variables and the LUB operation. -//! -//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) - -use super::combine::ObligationEmittingRelation; -use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use super::{DefineOpaqueTypes, InferCtxt}; - -use crate::traits::ObligationCause; -use rustc_middle::ty::relate::RelateResult; -use rustc_middle::ty::TyVar; -use rustc_middle::ty::{self, Ty}; - -/// Trait for returning data about a lattice, and for abstracting -/// over the "direction" of the lattice operation (LUB/GLB). -/// -/// GLB moves "down" the lattice (to smaller values); LUB moves -/// "up" the lattice (to bigger values). -pub trait LatticeDir<'f, 'tcx>: ObligationEmittingRelation<'tcx> { - fn infcx(&self) -> &'f InferCtxt<'tcx>; - - fn cause(&self) -> &ObligationCause<'tcx>; - - fn define_opaque_types(&self) -> DefineOpaqueTypes; - - // Relates the type `v` to `a` and `b` such that `v` represents - // the LUB/GLB of `a` and `b` as appropriate. - // - // Subtle hack: ordering *may* be significant here. This method - // relates `v` to `a` first, which may help us to avoid unnecessary - // type variable obligations. See caller for details. - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; -} - -/// Relates two types using a given lattice. -#[instrument(skip(this), level = "debug")] -pub fn super_lattice_tys<'a, 'tcx: 'a, L>( - this: &mut L, - a: Ty<'tcx>, - b: Ty<'tcx>, -) -> RelateResult<'tcx, Ty<'tcx>> -where - L: LatticeDir<'a, 'tcx>, -{ - debug!("{}", this.tag()); - - if a == b { - return Ok(a); - } - - let infcx = this.infcx(); - - let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); - - match (a.kind(), b.kind()) { - // If one side is known to be a variable and one is not, - // create a variable (`v`) to represent the LUB. Make sure to - // relate `v` to the non-type-variable first (by passing it - // first to `relate_bound`). Otherwise, we would produce a - // subtype obligation that must then be processed. - // - // Example: if the LHS is a type variable, and RHS is - // `Box`, then we current compare `v` to the RHS first, - // which will instantiate `v` with `Box`. Then when `v` - // is compared to the LHS, we instantiate LHS with `Box`. - // But if we did in reverse order, we would create a `v <: - // LHS` (or vice versa) constraint and then instantiate - // `v`. This would require further processing to achieve same - // end-result; in particular, this screws up some of the logic - // in coercion, which expects LUB to figure out that the LHS - // is (e.g.) `Box`. A more obvious solution might be to - // iterate on the subtype obligations that are returned, but I - // think this suffices. -nmatsakis - (&ty::Infer(TyVar(..)), _) => { - let v = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::LatticeVariable, - span: this.cause().span, - }); - this.relate_bound(v, b, a)?; - Ok(v) - } - (_, &ty::Infer(TyVar(..))) => { - let v = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::LatticeVariable, - span: this.cause().span, - }); - this.relate_bound(v, a, b)?; - Ok(v) - } - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b), - - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if this.define_opaque_types() == DefineOpaqueTypes::Yes - && def_id.is_local() - && !this.infcx().next_trait_solver() => - { - this.register_obligations( - infcx - .handle_opaque_type(a, b, this.a_is_expected(), this.cause(), this.param_env())? - .obligations, - ); - Ok(a) - } - - _ => infcx.super_combine_tys(this, a, b), - } -} diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs deleted file mode 100644 index e41ec7e6c01..00000000000 --- a/compiler/rustc_infer/src/infer/lub.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! Least upper bound. See [`lattice`]. - -use super::combine::{CombineFields, ObligationEmittingRelation}; -use super::lattice::{self, LatticeDir}; -use super::Subtype; -use super::{DefineOpaqueTypes, InferCtxt}; - -use crate::traits::{ObligationCause, PredicateObligations}; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; - -/// "Least upper bound" (common supertype) -pub struct Lub<'combine, 'infcx, 'tcx> { - fields: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, -} - -impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { - pub fn new( - fields: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, - ) -> Lub<'combine, 'infcx, 'tcx> { - Lub { fields, a_is_expected } - } -} - -impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "Lub" - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.fields.tcx() - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn a_is_expected(&self) -> bool { - self.a_is_expected - } - - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), - ty::Covariant => self.relate(a, b), - // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a), - ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), - } - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - lattice::super_lattice_tys(self, a, b) - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); - - let origin = Subtype(Box::new(self.fields.trace.clone())); - // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 - Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( - self.tcx(), - origin, - a, - b, - )) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // LUB of a binder and itself is just itself - if a == b { - return Ok(a); - } - - debug!("binders(a={:?}, b={:?})", a, b); - if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - // When higher-ranked types are involved, computing the LUB is - // very challenging, switch to invariance. This is obviously - // overly conservative but works ok in practice. - self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - a, - b, - )?; - Ok(a) - } else { - Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) - } - } -} - -impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { - fn infcx(&self) -> &'infcx InferCtxt<'tcx> { - self.fields.infcx - } - - fn cause(&self) -> &ObligationCause<'tcx> { - &self.fields.trace.cause - } - - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { - let mut sub = self.fields.sub(self.a_is_expected); - sub.relate(a, v)?; - sub.relate(b, v)?; - Ok(()) - } - - fn define_opaque_types(&self) -> DefineOpaqueTypes { - self.fields.define_opaque_types - } -} - -impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { - fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.fields.register_predicates(obligations); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.fields.register_obligations(obligations) - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - // FIXME(deferred_projection_equality): This isn't right, I think? - ty::AliasRelationDirection::Equate - } -} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e8a8808428c..5aa9c82b3ca 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -5,7 +5,7 @@ pub use self::BoundRegionConversionTime::*; pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; -pub use combine::ObligationEmittingRelation; +pub use relate::combine::ObligationEmittingRelation; use rustc_data_structures::captures::Captures; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey}; @@ -43,7 +43,6 @@ use rustc_span::{Span, DUMMY_SP}; use std::cell::{Cell, RefCell}; use std::fmt; -use self::combine::CombineFields; use self::error_reporting::TypeErrCtxt; use self::free_regions::RegionRelations; use self::lexical_region_resolve::LexicalRegionResolutions; @@ -51,29 +50,24 @@ use self::region_constraints::{GenericKind, VarInfos, VerifyBound}; use self::region_constraints::{ RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, }; +pub use self::relate::combine::CombineFields; use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; pub mod canonical; -mod combine; -mod equate; pub mod error_reporting; pub mod free_regions; mod freshen; mod fudge; -mod generalize; -mod glb; mod higher_ranked; -pub mod lattice; mod lexical_region_resolve; -mod lub; pub mod nll_relate; pub mod opaque_types; pub mod outlives; mod projection; pub mod region_constraints; +mod relate; pub mod resolve; -mod sub; pub mod type_variable; mod undo_log; diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index ab3c976a351..a4af9c8f6b2 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -30,8 +30,8 @@ use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; use rustc_span::{Span, Symbol}; use std::fmt::Debug; -use crate::infer::combine::ObligationEmittingRelation; -use crate::infer::generalize::{self, Generalization}; +use super::relate::combine::ObligationEmittingRelation; +use super::relate::generalize::{self, Generalization}; use crate::infer::InferCtxt; use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::traits::{Obligation, PredicateObligations}; diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs new file mode 100644 index 00000000000..043a916679e --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -0,0 +1,611 @@ +//! There are four type combiners: [Equate], [Sub], [Lub], and [Glb]. +//! Each implements the trait [TypeRelation] and contains methods for +//! combining two instances of various things and yielding a new instance. +//! These combiner methods always yield a `Result`. To relate two +//! types, you can use `infcx.at(cause, param_env)` which then allows +//! you to use the relevant methods of [At](crate::infer::at::At). +//! +//! Combiners mostly do their specific behavior and then hand off the +//! bulk of the work to [InferCtxt::super_combine_tys] and +//! [InferCtxt::super_combine_consts]. +//! +//! Combining two types may have side-effects on the inference contexts +//! which can be undone by using snapshots. You probably want to use +//! either [InferCtxt::commit_if_ok] or [InferCtxt::probe]. +//! +//! On success, the LUB/GLB operations return the appropriate bound. The +//! return value of `Equate` or `Sub` shouldn't really be used. +//! +//! ## Contravariance +//! +//! We explicitly track which argument is expected using +//! [TypeRelation::a_is_expected], so when dealing with contravariance +//! this should be correctly updated. + +use super::equate::Equate; +use super::generalize::{self, CombineDelegate, Generalization}; +use super::glb::Glb; +use super::lub::Lub; +use super::sub::Sub; +use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace}; +use crate::traits::{Obligation, PredicateObligations}; +use rustc_middle::infer::canonical::OriginalQueryValues; +use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{AliasRelationDirection, TyVar}; +use rustc_middle::ty::{IntType, UintType}; +use rustc_span::DUMMY_SP; + +#[derive(Clone)] +pub struct CombineFields<'infcx, 'tcx> { + pub infcx: &'infcx InferCtxt<'tcx>, + pub trace: TypeTrace<'tcx>, + pub cause: Option, + pub param_env: ty::ParamEnv<'tcx>, + pub obligations: PredicateObligations<'tcx>, + pub define_opaque_types: DefineOpaqueTypes, +} + +impl<'tcx> InferCtxt<'tcx> { + pub fn super_combine_tys( + &self, + relation: &mut R, + a: Ty<'tcx>, + b: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + R: ObligationEmittingRelation<'tcx>, + { + let a_is_expected = relation.a_is_expected(); + debug_assert!(!a.has_escaping_bound_vars()); + debug_assert!(!b.has_escaping_bound_vars()); + + match (a.kind(), b.kind()) { + // Relate integral variables to other types + (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { + self.inner + .borrow_mut() + .int_unification_table() + .unify_var_var(a_id, b_id) + .map_err(|e| int_unification_error(a_is_expected, e))?; + Ok(a) + } + (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => { + self.unify_integral_variable(a_is_expected, v_id, IntType(v)) + } + (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => { + self.unify_integral_variable(!a_is_expected, v_id, IntType(v)) + } + (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => { + self.unify_integral_variable(a_is_expected, v_id, UintType(v)) + } + (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => { + self.unify_integral_variable(!a_is_expected, v_id, UintType(v)) + } + + // Relate floating-point variables to other types + (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => { + self.inner + .borrow_mut() + .float_unification_table() + .unify_var_var(a_id, b_id) + .map_err(|e| float_unification_error(a_is_expected, e))?; + Ok(a) + } + (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => { + self.unify_float_variable(a_is_expected, v_id, v) + } + (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => { + self.unify_float_variable(!a_is_expected, v_id, v) + } + + // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm. + (ty::Alias(..), ty::Infer(ty::TyVar(_))) | (ty::Infer(ty::TyVar(_)), ty::Alias(..)) + if self.next_trait_solver() => + { + bug!( + "We do not expect to encounter `TyVar` this late in combine \ + -- they should have been handled earlier" + ) + } + (_, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))) + | (ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), _) + if self.next_trait_solver() => + { + bug!("We do not expect to encounter `Fresh` variables in the new solver") + } + + (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => { + relation.register_type_relate_obligation(a, b); + Ok(a) + } + + // All other cases of inference are errors + (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { + Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) + } + + // During coherence, opaque types should be treated as *possibly* + // equal to any other type (except for possibly itself). This is an + // extremely heavy hammer, but can be relaxed in a fowards-compatible + // way later. + (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => { + relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]); + Ok(a) + } + + _ => ty::relate::structurally_relate_tys(relation, a, b), + } + } + + pub fn super_combine_consts( + &self, + relation: &mut R, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> + where + R: ObligationEmittingRelation<'tcx>, + { + debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); + debug_assert!(!a.has_escaping_bound_vars()); + debug_assert!(!b.has_escaping_bound_vars()); + if a == b { + return Ok(a); + } + + let a = self.shallow_resolve(a); + let b = self.shallow_resolve(b); + + // We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the + // correct type for the generic param they are an argument for. However there have been a number of cases + // historically where asserting that the types are equal has found bugs in the compiler so this is valuable + // to check even if it is a bit nasty impl wise :( + // + // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find + // ourselves with a check to find bugs being required for code to compile because it made inference progress. + let compatible_types = self.probe(|_| { + if a.ty() == b.ty() { + return Ok(()); + } + + // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the + // two const param's types are able to be equal has to go through a canonical query with the actual logic + // in `rustc_trait_selection`. + let canonical = self.canonicalize_query( + relation.param_env().and((a.ty(), b.ty())), + &mut OriginalQueryValues::default(), + ); + self.tcx.check_tys_might_be_eq(canonical).map_err(|_| { + self.tcx.sess.span_delayed_bug( + DUMMY_SP, + format!("cannot relate consts of different types (a={a:?}, b={b:?})",), + ) + }) + }); + + // If the consts have differing types, just bail with a const error with + // the expected const's type. Specifically, we don't want const infer vars + // to do any type shapeshifting before and after resolution. + if let Err(guar) = compatible_types { + // HACK: equating both sides with `[const error]` eagerly prevents us + // from leaving unconstrained inference vars during things like impl + // matching in the solver. + let a_error = ty::Const::new_error(self.tcx, guar, a.ty()); + if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() { + return self.unify_const_variable(vid, a_error, relation.param_env()); + } + let b_error = ty::Const::new_error(self.tcx, guar, b.ty()); + if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() { + return self.unify_const_variable(vid, b_error, relation.param_env()); + } + + return Ok(if relation.a_is_expected() { a_error } else { b_error }); + } + + match (a.kind(), b.kind()) { + ( + ty::ConstKind::Infer(InferConst::Var(a_vid)), + ty::ConstKind::Infer(InferConst::Var(b_vid)), + ) => { + self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid); + return Ok(a); + } + + ( + ty::ConstKind::Infer(InferConst::EffectVar(a_vid)), + ty::ConstKind::Infer(InferConst::EffectVar(b_vid)), + ) => { + self.inner + .borrow_mut() + .effect_unification_table() + .unify_var_var(a_vid, b_vid) + .map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?; + return Ok(a); + } + + // All other cases of inference with other variables are errors. + ( + ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)), + ty::ConstKind::Infer(_), + ) + | ( + ty::ConstKind::Infer(_), + ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)), + ) => { + bug!( + "tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}" + ) + } + + (ty::ConstKind::Infer(InferConst::Var(vid)), _) => { + return self.unify_const_variable(vid, b, relation.param_env()); + } + + (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { + return self.unify_const_variable(vid, a, relation.param_env()); + } + + (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => { + return self.unify_effect_variable( + relation.a_is_expected(), + vid, + EffectVarValue::Const(b), + ); + } + + (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => { + return self.unify_effect_variable( + !relation.a_is_expected(), + vid, + EffectVarValue::Const(a), + ); + } + + (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) + if self.tcx.features().generic_const_exprs || self.next_trait_solver() => + { + let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) }; + + relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() { + ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ) + } else { + ty::PredicateKind::ConstEquate(a, b) + })]); + + return Ok(b); + } + _ => {} + } + + ty::relate::structurally_relate_consts(relation, a, b) + } + + /// Unifies the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused args, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its args contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its args, this must not succeed. + /// + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self))] + fn unify_const_variable( + &self, + target_vid: ty::ConstVid, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + let span = + self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span; + // FIXME(generic_const_exprs): Occurs check failures for unevaluated + // constants and generic expressions are not yet handled correctly. + let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize( + self, + &mut CombineDelegate { infcx: self, span, param_env }, + ct, + target_vid, + ty::Variance::Invariant, + )?; + + self.inner.borrow_mut().const_unification_table().union_value( + target_vid, + ConstVarValue { + origin: ConstVariableOrigin { + kind: ConstVariableOriginKind::ConstInference, + span: DUMMY_SP, + }, + val: ConstVariableValue::Known { value }, + }, + ); + Ok(value) + } + + fn unify_integral_variable( + &self, + vid_is_expected: bool, + vid: ty::IntVid, + val: ty::IntVarValue, + ) -> RelateResult<'tcx, Ty<'tcx>> { + self.inner + .borrow_mut() + .int_unification_table() + .unify_var_value(vid, Some(val)) + .map_err(|e| int_unification_error(vid_is_expected, e))?; + match val { + IntType(v) => Ok(Ty::new_int(self.tcx, v)), + UintType(v) => Ok(Ty::new_uint(self.tcx, v)), + } + } + + fn unify_float_variable( + &self, + vid_is_expected: bool, + vid: ty::FloatVid, + val: ty::FloatTy, + ) -> RelateResult<'tcx, Ty<'tcx>> { + self.inner + .borrow_mut() + .float_unification_table() + .unify_var_value(vid, Some(ty::FloatVarValue(val))) + .map_err(|e| float_unification_error(vid_is_expected, e))?; + Ok(Ty::new_float(self.tcx, val)) + } + + fn unify_effect_variable( + &self, + vid_is_expected: bool, + vid: ty::EffectVid, + val: EffectVarValue<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.inner + .borrow_mut() + .effect_unification_table() + .unify_var_value(vid, Some(val)) + .map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?; + Ok(val.as_const(self.tcx)) + } +} + +impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'tcx> { + Equate::new(self, a_is_expected) + } + + pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> { + Sub::new(self, a_is_expected) + } + + pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> { + Lub::new(self, a_is_expected) + } + + pub fn glb<'a>(&'a mut self, a_is_expected: bool) -> Glb<'a, 'infcx, 'tcx> { + Glb::new(self, a_is_expected) + } + + /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`. + /// The idea is that we should ensure that the type `a_ty` is equal + /// to, a subtype of, or a supertype of (respectively) the type + /// to which `b_vid` is bound. + /// + /// Since `b_vid` has not yet been instantiated with a type, we + /// will first instantiate `b_vid` with a *generalized* version + /// of `a_ty`. Generalization introduces other inference + /// variables wherever subtyping could occur. + #[instrument(skip(self), level = "debug")] + pub fn instantiate( + &mut self, + a_ty: Ty<'tcx>, + ambient_variance: ty::Variance, + b_vid: ty::TyVid, + a_is_expected: bool, + ) -> RelateResult<'tcx, ()> { + // Get the actual variable that b_vid has been inferred to + debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown()); + + // Generalize type of `a_ty` appropriately depending on the + // direction. As an example, assume: + // + // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an + // inference variable, + // - and `dir` == `SubtypeOf`. + // + // Then the generalized form `b_ty` would be `&'?2 ?3`, where + // `'?2` and `?3` are fresh region/type inference + // variables. (Down below, we will relate `a_ty <: b_ty`, + // adding constraints like `'x: '?2` and `?1 <: ?3`.) + let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize( + self.infcx, + &mut CombineDelegate { + infcx: self.infcx, + param_env: self.param_env, + span: self.trace.span(), + }, + a_ty, + b_vid, + ambient_variance, + )?; + + // Constrain `b_vid` to the generalized type `b_ty`. + if let &ty::Infer(TyVar(b_ty_vid)) = b_ty.kind() { + self.infcx.inner.borrow_mut().type_variables().equate(b_vid, b_ty_vid); + } else { + self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); + } + + if needs_wf { + self.obligations.push(Obligation::new( + self.tcx(), + self.trace.cause.clone(), + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + b_ty.into(), + ))), + )); + } + + // Finally, relate `b_ty` to `a_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + if b_ty.is_ty_var() { + // This happens for cases like `::Assoc == ?0`. + // We can't instantiate `?0` here as that would result in a + // cyclic type. We instead delay the unification in case + // the alias can be normalized to something which does not + // mention `?0`. + if self.infcx.next_trait_solver() { + let (lhs, rhs, direction) = match ambient_variance { + ty::Variance::Invariant => { + (a_ty.into(), b_ty.into(), AliasRelationDirection::Equate) + } + ty::Variance::Covariant => { + (a_ty.into(), b_ty.into(), AliasRelationDirection::Subtype) + } + ty::Variance::Contravariant => { + (b_ty.into(), a_ty.into(), AliasRelationDirection::Subtype) + } + ty::Variance::Bivariant => unreachable!("bivariant generalization"), + }; + self.obligations.push(Obligation::new( + self.tcx(), + self.trace.cause.clone(), + self.param_env, + ty::PredicateKind::AliasRelate(lhs, rhs, direction), + )); + } else { + match a_ty.kind() { + &ty::Alias(ty::AliasKind::Projection, data) => { + // FIXME: This does not handle subtyping correctly, we could + // instead create a new inference variable for `a_ty`, emitting + // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`. + self.obligations.push(Obligation::new( + self.tcx(), + self.trace.cause.clone(), + self.param_env, + ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() }, + )) + } + // The old solver only accepts projection predicates for associated types. + ty::Alias( + ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque, + _, + ) => return Err(TypeError::CyclicTy(a_ty)), + _ => bug!("generalizated `{a_ty:?} to infer, not an alias"), + } + } + } else { + match ambient_variance { + ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty), + ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty), + ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance( + ty::Contravariant, + ty::VarianceDiagInfo::default(), + a_ty, + b_ty, + ), + ty::Variance::Bivariant => unreachable!("bivariant generalization"), + }?; + } + + Ok(()) + } + + pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.obligations.extend(obligations.into_iter()); + } + + pub fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.obligations.extend(obligations.into_iter().map(|to_pred| { + Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred) + })) + } +} + +pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { + /// Register obligations that must hold in order for this relation to hold + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); + + /// Register predicates that must hold in order for this relation to hold. Uses + /// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should + /// be used if control over the obligation causes is required. + fn register_predicates(&mut self, obligations: impl IntoIterator>); + + /// Register an obligation that both types must be related to each other according to + /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`] + fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + self.alias_relate_direction(), + ))]); + } + + /// Relation direction emitted for `AliasRelate` predicates, corresponding to the direction + /// of the relation. + fn alias_relate_direction(&self) -> ty::AliasRelationDirection; +} + +fn int_unification_error<'tcx>( + a_is_expected: bool, + v: (ty::IntVarValue, ty::IntVarValue), +) -> TypeError<'tcx> { + let (a, b) = v; + TypeError::IntMismatch(ExpectedFound::new(a_is_expected, a, b)) +} + +fn float_unification_error<'tcx>( + a_is_expected: bool, + v: (ty::FloatVarValue, ty::FloatVarValue), +) -> TypeError<'tcx> { + let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v; + TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b)) +} + +fn effect_unification_error<'tcx>( + _tcx: TyCtxt<'tcx>, + _a_is_expected: bool, + (_a, _b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>), +) -> TypeError<'tcx> { + bug!("unexpected effect unification error") +} diff --git a/compiler/rustc_infer/src/infer/relate/equate.rs b/compiler/rustc_infer/src/infer/relate/equate.rs new file mode 100644 index 00000000000..9943c638a91 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/equate.rs @@ -0,0 +1,188 @@ +use super::combine::{CombineFields, ObligationEmittingRelation}; +use crate::infer::{DefineOpaqueTypes, SubregionOrigin}; +use crate::traits::PredicateObligations; + +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::GenericArgsRef; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; + +use rustc_hir::def_id::DefId; + +/// Ensures `a` is made equal to `b`. Returns `a` on success. +pub struct Equate<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> { + pub fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Equate<'combine, 'infcx, 'tcx> { + Equate { fields, a_is_expected } + } +} + +impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Equate" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn relate_item_args( + &mut self, + _item_def_id: DefId, + a_arg: GenericArgsRef<'tcx>, + b_arg: GenericArgsRef<'tcx>, + ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { + // N.B., once we are equating types, we don't care about + // variance, so don't try to lookup the variance here. This + // also avoids some cycles (e.g., #41849) since looking up + // variance requires computing types which can require + // performing trait matching (which then performs equality + // unification). + + relate::relate_args_invariantly(self, a_arg, b_arg) + } + + fn relate_with_variance>( + &mut self, + _: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if a == b { + return Ok(a); + } + + trace!(a = ?a.kind(), b = ?b.kind()); + + let infcx = self.fields.infcx; + + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + + match (a.kind(), b.kind()) { + (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { + infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); + } + + (&ty::Infer(TyVar(a_id)), _) => { + self.fields.instantiate(b, ty::Invariant, a_id, self.a_is_expected)?; + } + + (_, &ty::Infer(TyVar(b_id))) => { + self.fields.instantiate(a, ty::Invariant, b_id, self.a_is_expected)?; + } + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => { + self.fields.infcx.super_combine_tys(self, a, b)?; + } + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if self.fields.define_opaque_types == DefineOpaqueTypes::Yes + && def_id.is_local() + && !self.fields.infcx.next_trait_solver() => + { + self.fields.obligations.extend( + infcx + .handle_opaque_type( + a, + b, + self.a_is_expected(), + &self.fields.trace.cause, + self.param_env(), + )? + .obligations, + ); + } + _ => { + self.fields.infcx.super_combine_tys(self, a, b)?; + } + } + + Ok(a) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + self.fields + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .make_eqregion(origin, a, b); + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // A binder is equal to itself if it's structurally equal to itself + if a == b { + return Ok(a); + } + + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; + self.fields.higher_ranked_sub(b, a, self.a_is_expected)?; + } else { + // Fast path for the common case. + self.relate(a.skip_binder(), b.skip_binder())?; + } + Ok(a) + } +} + +impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); + } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + ty::AliasRelationDirection::Equate + } +} diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs new file mode 100644 index 00000000000..66f7b08ee12 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -0,0 +1,555 @@ +use std::mem; + +use rustc_data_structures::sso::SsoHashMap; +use rustc_hir::def_id::DefId; +use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::MaxUniverse; +use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_span::Span; + +use crate::infer::nll_relate::TypeRelatingDelegate; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue}; +use crate::infer::{InferCtxt, RegionVariableOrigin}; + +/// Attempts to generalize `term` for the type variable `for_vid`. +/// This checks for cycles -- that is, whether the type `term` +/// references `for_vid`. +pub fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into> + Relate<'tcx>>( + infcx: &InferCtxt<'tcx>, + delegate: &mut D, + term: T, + for_vid: impl Into, + ambient_variance: ty::Variance, +) -> RelateResult<'tcx, Generalization> { + let (for_universe, root_vid) = match for_vid.into() { + ty::TermVid::Ty(ty_vid) => ( + infcx.probe_ty_var(ty_vid).unwrap_err(), + ty::TermVid::Ty(infcx.inner.borrow_mut().type_variables().sub_root_var(ty_vid)), + ), + ty::TermVid::Const(ct_vid) => ( + infcx.probe_const_var(ct_vid).unwrap_err(), + ty::TermVid::Const(infcx.inner.borrow_mut().const_unification_table().find(ct_vid).vid), + ), + }; + + let mut generalizer = Generalizer { + infcx, + delegate, + ambient_variance, + root_vid, + for_universe, + root_term: term.into(), + in_alias: false, + needs_wf: false, + cache: Default::default(), + }; + + assert!(!term.has_escaping_bound_vars()); + let value_may_be_infer = generalizer.relate(term, term)?; + let needs_wf = generalizer.needs_wf; + Ok(Generalization { value_may_be_infer, needs_wf }) +} + +/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking +/// in the generalizer code. +pub trait GeneralizerDelegate<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; + + fn forbid_inference_vars() -> bool; + + fn span(&self) -> Span; + + fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; +} + +pub struct CombineDelegate<'cx, 'tcx> { + pub infcx: &'cx InferCtxt<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, + pub span: Span, +} + +impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + + fn forbid_inference_vars() -> bool { + false + } + + fn span(&self) -> Span { + self.span + } + + fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { + // FIXME: This is non-ideal because we don't give a + // very descriptive origin for this region variable. + self.infcx + .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe) + } +} + +impl<'tcx, T> GeneralizerDelegate<'tcx> for T +where + T: TypeRelatingDelegate<'tcx>, +{ + fn param_env(&self) -> ty::ParamEnv<'tcx> { + >::param_env(self) + } + + fn forbid_inference_vars() -> bool { + >::forbid_inference_vars() + } + + fn span(&self) -> Span { + >::span(&self) + } + + fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { + >::generalize_existential(self, universe) + } +} + +/// The "generalizer" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the term `B` -- this replaces +/// all the lifetimes in the term `B` with fresh inference variables. +/// (You can read more about the strategy in this [blog post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// [blog post]: https://is.gd/0hKvIr +struct Generalizer<'me, 'tcx, D> { + infcx: &'me InferCtxt<'tcx>, + + /// This is used to abstract the behaviors of the three previous + /// generalizer-like implementations (`Generalizer`, `TypeGeneralizer`, + /// and `ConstInferUnifier`). See [`GeneralizerDelegate`] for more + /// information. + delegate: &'me mut D, + + /// After we generalize this type, we are going to relate it to + /// some other type. What will be the variance at this point? + ambient_variance: ty::Variance, + + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + root_vid: ty::TermVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. + for_universe: ty::UniverseIndex, + + /// The root term (const or type) we're generalizing. Used for cycle errors. + root_term: Term<'tcx>, + + cache: SsoHashMap, Ty<'tcx>>, + + /// This is set once we're generalizing the arguments of an alias. + /// + /// This is necessary to correctly handle + /// `::Assoc>::Assoc == ?0`. This equality can + /// hold by either normalizing the outer or the inner associated type. + in_alias: bool, + + /// See the field `needs_wf` in `Generalization`. + needs_wf: bool, +} + +impl<'tcx, D> Generalizer<'_, 'tcx, D> { + /// Create an error that corresponds to the term kind in `root_term` + fn cyclic_term_error(&self) -> TypeError<'tcx> { + match self.root_term.unpack() { + ty::TermKind::Ty(ty) => TypeError::CyclicTy(ty), + ty::TermKind::Const(ct) => TypeError::CyclicConst(ct), + } + } +} + +impl<'tcx, D> TypeRelation<'tcx> for Generalizer<'_, 'tcx, D> +where + D: GeneralizerDelegate<'tcx>, +{ + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.delegate.param_env() + } + + fn tag(&self) -> &'static str { + "Generalizer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_item_args( + &mut self, + item_def_id: DefId, + a_subst: ty::GenericArgsRef<'tcx>, + b_subst: ty::GenericArgsRef<'tcx>, + ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { + if self.ambient_variance == ty::Variance::Invariant { + // Avoid fetching the variance if we are in an invariant + // context; no need, and it can induce dependency cycles + // (e.g., #41849). + relate::relate_args_invariantly(self, a_subst, b_subst) + } else { + let tcx = self.tcx(); + let opt_variances = tcx.variances_of(item_def_id); + relate::relate_args_with_variances( + self, + item_def_id, + opt_variances, + a_subst, + b_subst, + false, + ) + } + } + + #[instrument(level = "debug", skip(self, variance, b), ret)] + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + debug!(?self.ambient_variance, "new ambient variance"); + let r = self.relate(a, b)?; + self.ambient_variance = old_ambient_variance; + Ok(r) + } + + #[instrument(level = "debug", skip(self, t2), ret)] + fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + if let Some(&result) = self.cache.get(&t) { + return Ok(result); + } + + // Check to see whether the type we are generalizing references + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. + let g = match *t.kind() { + ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) + if D::forbid_inference_vars() => + { + bug!("unexpected inference variable encountered in NLL generalization: {t}"); + } + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("unexpected infer type: {t}") + } + + ty::Infer(ty::TyVar(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let vid = inner.type_variables().root_var(vid); + let sub_vid = inner.type_variables().sub_root_var(vid); + + if ty::TermVid::Ty(sub_vid) == self.root_vid { + // If sub-roots are equal, then `root_vid` and + // `vid` are related via subtyping. + Err(self.cyclic_term_error()) + } else { + let probe = inner.type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe } => { + match self.ambient_variance { + // Invariant: no need to make a fresh type variable + // if we can name the universe. + ty::Invariant => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + } + + // Bivariant: make a fresh var, but we + // may need a WF predicate. See + // comment on `needs_wf` field for + // more info. + ty::Bivariant => self.needs_wf = true, + + // Co/contravariant: this will be + // sufficiently constrained later on. + ty::Covariant | ty::Contravariant => (), + } + + let origin = inner.type_variables().var_origin(vid); + let new_var_id = + inner.type_variables().new_var(self.for_universe, origin); + let u = Ty::new_var(self.tcx(), new_var_id); + + // Record that we replaced `vid` with `new_var_id` as part of a generalization + // operation. This is needed to detect cyclic types. To see why, see the + // docs in the `type_variables` module. + inner.type_variables().sub(vid, new_var_id); + debug!("replacing original vid={:?} with new={:?}", vid, u); + Ok(u) + } + } + } + } + + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(t) + } + + ty::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(t) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + + ty::Alias(kind, data) => { + // An occurs check failure inside of an alias does not mean + // that the types definitely don't unify. We may be able + // to normalize the alias after all. + // + // We handle this by lazily equating the alias and generalizing + // it to an inference variable. + let is_nested_alias = mem::replace(&mut self.in_alias, true); + let result = match self.relate(data, data) { + Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)), + Err(e) => { + if is_nested_alias { + return Err(e); + } else { + let mut visitor = MaxUniverse::new(); + t.visit_with(&mut visitor); + let infer_replacement_is_complete = + self.for_universe.can_name(visitor.max_universe()) + && !t.has_escaping_bound_vars(); + if !infer_replacement_is_complete { + warn!("may incompletely handle alias type: {t:?}"); + } + + debug!("generalization failure in alias"); + Ok(self.infcx.next_ty_var_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.delegate.span(), + }, + self.for_universe, + )) + } + } + }; + self.in_alias = is_nested_alias; + result + } + + _ => relate::structurally_relate_tys(self, t, t), + }?; + + self.cache.insert(t, g); + Ok(g) + } + + #[instrument(level = "debug", skip(self, r2), ret)] + fn regions( + &mut self, + r: ty::Region<'tcx>, + r2: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match *r { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + ty::ReBound(..) | ty::ReErased => { + return Ok(r); + } + + // It doesn't really matter for correctness if we generalize ReError, + // since we're already on a doomed compilation path. + ty::ReError(_) => { + return Ok(r); + } + + ty::RePlaceholder(..) + | ty::ReVar(..) + | ty::ReStatic + | ty::ReEarlyParam(..) + | ty::ReLateParam(..) => { + // see common code below + } + } + + // If we are in an invariant context, we can re-use the region + // as is, unless it happens to be in some universe that we + // can't name. + if let ty::Invariant = self.ambient_variance { + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } + } + + Ok(self.delegate.generalize_region(self.for_universe)) + } + + #[instrument(level = "debug", skip(self, c2), ret)] + fn consts( + &mut self, + c: ty::Const<'tcx>, + c2: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match c.kind() { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + bug!("unexpected inference variable encountered in NLL generalization: {:?}", c); + } + ty::ConstKind::Infer(InferConst::Var(vid)) => { + // If root const vids are equal, then `root_vid` and + // `vid` are related and we'd be inferring an infinitely + // deep const. + if ty::TermVid::Const( + self.infcx.inner.borrow_mut().const_unification_table().find(vid).vid, + ) == self.root_vid + { + return Err(self.cyclic_term_error()); + } + + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + let var_value = variable_table.probe_value(vid); + match var_value.val { + ConstVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + ConstVariableValue::Unknown { universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = variable_table + .new_key(ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { + universe: self.for_universe, + }, + }) + .vid; + Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty())) + } + } + } + } + ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c), + // FIXME: remove this branch once `structurally_relate_consts` is fully + // structural. + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { + let args = self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + args, + args, + )?; + Ok(ty::Const::new_unevaluated( + self.tcx(), + ty::UnevaluatedConst { def, args }, + c.ty(), + )) + } + ty::ConstKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(c) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + _ => relate::structurally_relate_consts(self, c, c), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + _: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + let result = self.relate(a.skip_binder(), a.skip_binder())?; + Ok(a.rebind(result)) + } +} + +/// Result from a generalization operation. This includes +/// not only the generalized type, but also a bool flag +/// indicating whether further WF checks are needed. +#[derive(Debug)] +pub struct Generalization { + /// When generalizing `::Assoc` or + /// `::Assoc>>::Assoc` + /// for `?0` generalization returns an inference + /// variable. + /// + /// This has to be handled wotj care as it can + /// otherwise very easily result in infinite + /// recursion. + pub value_may_be_infer: T, + + /// If true, then the generalized type may not be well-formed, + /// even if the source type is well-formed, so we should add an + /// additional check to enforce that it is. This arises in + /// particular around 'bivariant' type parameters that are only + /// constrained by a where-clause. As an example, imagine a type: + /// + /// struct Foo where A: Iterator { + /// data: A + /// } + /// + /// here, `A` will be covariant, but `B` is + /// unconstrained. However, whatever it is, for `Foo` to be WF, it + /// must be equal to `A::Item`. If we have an input `Foo`, + /// then after generalization we will wind up with a type like + /// `Foo`. When we enforce that `Foo <: Foo` (or `>:`), we will wind up with the requirement that `?A + /// <: ?C`, but no particular relationship between `?B` and `?D` + /// (after all, we do not know the variance of the normalized form + /// of `A::Item` with respect to `A`). If we do nothing else, this + /// may mean that `?D` goes unconstrained (as in #41677). So, in + /// this scenario where we create a new type variable in a + /// bivariant context, we set the `needs_wf` flag to true. This + /// will force the calling code to check that `WF(Foo)` + /// holds, which in turn implies that `?C::Item == ?D`. So once + /// `?C` is constrained, that should suffice to restrict `?D`. + pub needs_wf: bool, +} diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs new file mode 100644 index 00000000000..6a3413879c4 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -0,0 +1,153 @@ +//! Greatest lower bound. See [`lattice`]. + +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; + +use super::combine::{CombineFields, ObligationEmittingRelation}; +use super::lattice::{self, LatticeDir}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; +use crate::traits::{ObligationCause, PredicateObligations}; + +/// "Greatest lower bound" (common subtype) +pub struct Glb<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { + pub fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Glb<'combine, 'infcx, 'tcx> { + Glb { fields, a_is_expected } + } +} + +impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Glb" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), + ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + ty::Bivariant => Ok(a), + ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b), + } + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + lattice::super_lattice_tys(self, a, b) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 + Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( + self.tcx(), + origin, + a, + b, + )) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // GLB of a binder and itself is just itself + if a == b { + return Ok(a); + } + + debug!("binders(a={:?}, b={:?})", a, b); + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the GLB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + a, + b, + )?; + Ok(a) + } else { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'tcx> { + self.fields.infcx + } + + fn cause(&self) -> &ObligationCause<'tcx> { + &self.fields.trace.cause + } + + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let mut sub = self.fields.sub(self.a_is_expected); + sub.relate(v, a)?; + sub.relate(v, b)?; + Ok(()) + } + + fn define_opaque_types(&self) -> DefineOpaqueTypes { + self.fields.define_opaque_types + } +} + +impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); + } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + // FIXME(deferred_projection_equality): This isn't right, I think? + ty::AliasRelationDirection::Equate + } +} diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs new file mode 100644 index 00000000000..744e2dfa380 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -0,0 +1,128 @@ +//! # Lattice variables +//! +//! Generic code for operating on [lattices] of inference variables +//! that are characterized by an upper- and lower-bound. +//! +//! The code is defined quite generically so that it can be +//! applied both to type variables, which represent types being inferred, +//! and fn variables, which represent function types being inferred. +//! (It may eventually be applied to their types as well.) +//! In some cases, the functions are also generic with respect to the +//! operation on the lattice (GLB vs LUB). +//! +//! ## Note +//! +//! Although all the functions are generic, for simplicity, comments in the source code +//! generally refer to type variables and the LUB operation. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) + +use super::combine::ObligationEmittingRelation; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{DefineOpaqueTypes, InferCtxt}; +use crate::traits::ObligationCause; + +use rustc_middle::ty::relate::RelateResult; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty}; + +/// Trait for returning data about a lattice, and for abstracting +/// over the "direction" of the lattice operation (LUB/GLB). +/// +/// GLB moves "down" the lattice (to smaller values); LUB moves +/// "up" the lattice (to bigger values). +pub trait LatticeDir<'f, 'tcx>: ObligationEmittingRelation<'tcx> { + fn infcx(&self) -> &'f InferCtxt<'tcx>; + + fn cause(&self) -> &ObligationCause<'tcx>; + + fn define_opaque_types(&self) -> DefineOpaqueTypes; + + // Relates the type `v` to `a` and `b` such that `v` represents + // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unnecessary + // type variable obligations. See caller for details. + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; +} + +/// Relates two types using a given lattice. +#[instrument(skip(this), level = "debug")] +pub fn super_lattice_tys<'a, 'tcx: 'a, L>( + this: &mut L, + a: Ty<'tcx>, + b: Ty<'tcx>, +) -> RelateResult<'tcx, Ty<'tcx>> +where + L: LatticeDir<'a, 'tcx>, +{ + debug!("{}", this.tag()); + + if a == b { + return Ok(a); + } + + let infcx = this.infcx(); + + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + + match (a.kind(), b.kind()) { + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in particular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (&ty::Infer(TyVar(..)), _) => { + let v = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::LatticeVariable, + span: this.cause().span, + }); + this.relate_bound(v, b, a)?; + Ok(v) + } + (_, &ty::Infer(TyVar(..))) => { + let v = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::LatticeVariable, + span: this.cause().span, + }); + this.relate_bound(v, a, b)?; + Ok(v) + } + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b), + + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if this.define_opaque_types() == DefineOpaqueTypes::Yes + && def_id.is_local() + && !this.infcx().next_trait_solver() => + { + this.register_obligations( + infcx + .handle_opaque_type(a, b, this.a_is_expected(), this.cause(), this.param_env())? + .obligations, + ); + Ok(a) + } + + _ => infcx.super_combine_tys(this, a, b), + } +} diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs new file mode 100644 index 00000000000..41cd98ed0cf --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -0,0 +1,153 @@ +//! Least upper bound. See [`lattice`]. + +use super::combine::{CombineFields, ObligationEmittingRelation}; +use super::lattice::{self, LatticeDir}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; +use crate::traits::{ObligationCause, PredicateObligations}; + +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; + +/// "Least upper bound" (common supertype) +pub struct Lub<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { + pub fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Lub<'combine, 'infcx, 'tcx> { + Lub { fields, a_is_expected } + } +} + +impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Lub" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), + ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + ty::Bivariant => Ok(a), + ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), + } + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + lattice::super_lattice_tys(self, a, b) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 + Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( + self.tcx(), + origin, + a, + b, + )) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // LUB of a binder and itself is just itself + if a == b { + return Ok(a); + } + + debug!("binders(a={:?}, b={:?})", a, b); + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + a, + b, + )?; + Ok(a) + } else { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'tcx> { + self.fields.infcx + } + + fn cause(&self) -> &ObligationCause<'tcx> { + &self.fields.trace.cause + } + + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let mut sub = self.fields.sub(self.a_is_expected); + sub.relate(a, v)?; + sub.relate(b, v)?; + Ok(()) + } + + fn define_opaque_types(&self) -> DefineOpaqueTypes { + self.fields.define_opaque_types + } +} + +impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations) + } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + // FIXME(deferred_projection_equality): This isn't right, I think? + ty::AliasRelationDirection::Equate + } +} diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs new file mode 100644 index 00000000000..e431734996f --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -0,0 +1,11 @@ +//! This module contains the definitions of most `TypeRelation`s in the type system +//! (except for the NLL `TypeRelating`, and some relations used for diagnostics +//! and heuristics in the compiler). + +pub(super) mod combine; +pub(super) mod equate; +pub(super) mod generalize; +pub(super) mod glb; +pub(super) mod lattice; +pub(super) mod lub; +pub(super) mod sub; diff --git a/compiler/rustc_infer/src/infer/relate/sub.rs b/compiler/rustc_infer/src/infer/relate/sub.rs new file mode 100644 index 00000000000..5a623e48c93 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/sub.rs @@ -0,0 +1,217 @@ +use super::combine::CombineFields; +use crate::infer::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin}; +use crate::traits::{Obligation, PredicateObligations}; + +use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use std::mem; + +/// Ensures `a` is made a subtype of `b`. Returns `a` on success. +pub struct Sub<'combine, 'a, 'tcx> { + fields: &'combine mut CombineFields<'a, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> { + pub fn new( + f: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Sub<'combine, 'infcx, 'tcx> { + Sub { fields: f, a_is_expected } + } + + fn with_expected_switched R>(&mut self, f: F) -> R { + self.a_is_expected = !self.a_is_expected; + let result = f(self); + self.a_is_expected = !self.a_is_expected; + result + } +} + +impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Sub" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn with_cause(&mut self, cause: Cause, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + debug!("sub with_cause={:?}", cause); + let old_cause = mem::replace(&mut self.fields.cause, Some(cause)); + let r = f(self); + debug!("sub old_cause={:?}", old_cause); + self.fields.cause = old_cause; + r + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), + ty::Covariant => self.relate(a, b), + ty::Bivariant => Ok(a), + ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)), + } + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if a == b { + return Ok(a); + } + + let infcx = self.fields.infcx; + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + + match (a.kind(), b.kind()) { + (&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => { + // Shouldn't have any LBR here, so we can safely put + // this under a binder below without fear of accidental + // capture. + assert!(!a.has_escaping_bound_vars()); + assert!(!b.has_escaping_bound_vars()); + + // can't make progress on `A <: B` if both A and B are + // type variables, so record an obligation. + self.fields.obligations.push(Obligation::new( + self.tcx(), + self.fields.trace.cause.clone(), + self.fields.param_env, + ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a, + b, + })), + )); + + Ok(a) + } + (&ty::Infer(TyVar(a_id)), _) => { + self.fields.instantiate(b, ty::Contravariant, a_id, !self.a_is_expected)?; + Ok(a) + } + (_, &ty::Infer(TyVar(b_id))) => { + self.fields.instantiate(a, ty::Covariant, b_id, self.a_is_expected)?; + Ok(a) + } + + (&ty::Error(e), _) | (_, &ty::Error(e)) => { + infcx.set_tainted_by_errors(e); + Ok(Ty::new_error(self.tcx(), e)) + } + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => { + self.fields.infcx.super_combine_tys(self, a, b)?; + Ok(a) + } + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if self.fields.define_opaque_types == DefineOpaqueTypes::Yes + && def_id.is_local() + && !self.fields.infcx.next_trait_solver() => + { + self.fields.obligations.extend( + infcx + .handle_opaque_type( + a, + b, + self.a_is_expected, + &self.fields.trace.cause, + self.param_env(), + )? + .obligations, + ); + Ok(a) + } + _ => { + self.fields.infcx.super_combine_tys(self, a, b)?; + Ok(a) + } + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?}) self.cause={:?}", self.tag(), a, b, self.fields.cause); + + // FIXME -- we have more fine-grained information available + // from the "cause" field, we could perhaps give more tailored + // error messages. + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) + self.fields + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .make_subregion(origin, b, a); + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // A binder is always a subtype of itself if it's structurally equal to itself + if a == b { + return Ok(a); + } + + self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; + Ok(a) + } +} + +impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); + } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + ty::AliasRelationDirection::Subtype + } +} diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs deleted file mode 100644 index 0c3bb633b53..00000000000 --- a/compiler/rustc_infer/src/infer/sub.rs +++ /dev/null @@ -1,217 +0,0 @@ -use super::combine::CombineFields; -use super::{DefineOpaqueTypes, ObligationEmittingRelation, SubregionOrigin}; - -use crate::traits::{Obligation, PredicateObligations}; -use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::TyVar; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use std::mem; - -/// Ensures `a` is made a subtype of `b`. Returns `a` on success. -pub struct Sub<'combine, 'a, 'tcx> { - fields: &'combine mut CombineFields<'a, 'tcx>, - a_is_expected: bool, -} - -impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> { - pub fn new( - f: &'combine mut CombineFields<'infcx, 'tcx>, - a_is_expected: bool, - ) -> Sub<'combine, 'infcx, 'tcx> { - Sub { fields: f, a_is_expected } - } - - fn with_expected_switched R>(&mut self, f: F) -> R { - self.a_is_expected = !self.a_is_expected; - let result = f(self); - self.a_is_expected = !self.a_is_expected; - result - } -} - -impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { - fn tag(&self) -> &'static str { - "Sub" - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.fields.infcx.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.fields.param_env - } - - fn a_is_expected(&self) -> bool { - self.a_is_expected - } - - fn with_cause(&mut self, cause: Cause, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - debug!("sub with_cause={:?}", cause); - let old_cause = mem::replace(&mut self.fields.cause, Some(cause)); - let r = f(self); - debug!("sub old_cause={:?}", old_cause); - self.fields.cause = old_cause; - r - } - - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - match variance { - ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), - ty::Covariant => self.relate(a, b), - ty::Bivariant => Ok(a), - ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)), - } - } - - #[instrument(skip(self), level = "debug")] - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - if a == b { - return Ok(a); - } - - let infcx = self.fields.infcx; - let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); - - match (a.kind(), b.kind()) { - (&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => { - // Shouldn't have any LBR here, so we can safely put - // this under a binder below without fear of accidental - // capture. - assert!(!a.has_escaping_bound_vars()); - assert!(!b.has_escaping_bound_vars()); - - // can't make progress on `A <: B` if both A and B are - // type variables, so record an obligation. - self.fields.obligations.push(Obligation::new( - self.tcx(), - self.fields.trace.cause.clone(), - self.fields.param_env, - ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: self.a_is_expected, - a, - b, - })), - )); - - Ok(a) - } - (&ty::Infer(TyVar(a_id)), _) => { - self.fields.instantiate(b, ty::Contravariant, a_id, !self.a_is_expected)?; - Ok(a) - } - (_, &ty::Infer(TyVar(b_id))) => { - self.fields.instantiate(a, ty::Covariant, b_id, self.a_is_expected)?; - Ok(a) - } - - (&ty::Error(e), _) | (_, &ty::Error(e)) => { - infcx.set_tainted_by_errors(e); - Ok(Ty::new_error(self.tcx(), e)) - } - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id => { - self.fields.infcx.super_combine_tys(self, a, b)?; - Ok(a) - } - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if self.fields.define_opaque_types == DefineOpaqueTypes::Yes - && def_id.is_local() - && !self.fields.infcx.next_trait_solver() => - { - self.fields.obligations.extend( - infcx - .handle_opaque_type( - a, - b, - self.a_is_expected, - &self.fields.trace.cause, - self.param_env(), - )? - .obligations, - ); - Ok(a) - } - _ => { - self.fields.infcx.super_combine_tys(self, a, b)?; - Ok(a) - } - } - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?}) self.cause={:?}", self.tag(), a, b, self.fields.cause); - - // FIXME -- we have more fine-grained information available - // from the "cause" field, we could perhaps give more tailored - // error messages. - let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); - // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) - self.fields - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .make_subregion(origin, b, a); - - Ok(a) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - self.fields.infcx.super_combine_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // A binder is always a subtype of itself if it's structurally equal to itself - if a == b { - return Ok(a); - } - - self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; - Ok(a) - } -} - -impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> { - fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.fields.register_predicates(obligations); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.fields.register_obligations(obligations); - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - ty::AliasRelationDirection::Subtype - } -} diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index c2c951af0db..8e852ec796e 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,11 +1,11 @@ -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index 6b43b95fef3..c16a48d5f15 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -13,14 +13,14 @@ LL | #![feature(lazy_type_alias)] = note: see issue #112792 for more information = note: `#[warn(incomplete_features)]` on by default -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Weak, AliasTy { args: [RePlaceholder(!1_BoundRegion { var: 0, kind: BrNamed(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), 'a) }), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }) error[E0119]: conflicting implementations of trait `Overlap` for type `fn(_)` --> $DIR/issue-118950-root-region.rs:18:1 | -- cgit 1.4.1-3-g733a5 From 18bf0caa5af355a216c132a30feab40187a9502f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Dec 2023 15:56:05 +0000 Subject: Notify lcnr and errs when relations change --- triagebot.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 30f049c79d6..604d2eaee57 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -441,6 +441,14 @@ cc = ["@rust-lang/miri"] message = "Some changes occurred in need_type_info.rs" cc = ["@lcnr"] +[mentions."compiler/rustc_middle/src/ty/relate.rs"] +message = "Type relation code was changed" +cc = ["@compiler-errors", "@lcnr"] + +[mentions."compiler/rustc_infer/src/infer/relate"] +message = "Type relation code was changed" +cc = ["@compiler-errors", "@lcnr"] + [mentions."compiler/rustc_middle/src/mir/interpret"] message = "Some changes occurred to the CTFE / Miri engine" cc = ["@rust-lang/miri"] -- cgit 1.4.1-3-g733a5 From 807cd85dfaab718dc29be6d94dc545db01035ed1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Dec 2023 15:59:51 +0000 Subject: Add higher_ranked to relate submodule --- .../rustc_infer/src/infer/higher_ranked/mod.rs | 136 --------------------- compiler/rustc_infer/src/infer/mod.rs | 1 - .../rustc_infer/src/infer/relate/higher_ranked.rs | 136 +++++++++++++++++++++ compiler/rustc_infer/src/infer/relate/mod.rs | 11 +- .../occurs-check/associated-type.next.stderr | 16 +-- 5 files changed, 150 insertions(+), 150 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/higher_ranked/mod.rs create mode 100644 compiler/rustc_infer/src/infer/relate/higher_ranked.rs diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs deleted file mode 100644 index 0de988efaee..00000000000 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! Helper routines for higher-ranked things. See the `doc` module at -//! the end of the file for details. - -use super::{HigherRankedType, InferCtxt}; -use crate::infer::relate::combine::CombineFields; -use crate::infer::CombinedSnapshot; -use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable}; - -impl<'a, 'tcx> CombineFields<'a, 'tcx> { - /// Checks whether `for<..> sub <: for<..> sup` holds. - /// - /// For this to hold, **all** instantiations of the super type - /// have to be a super type of **at least one** instantiation of - /// the subtype. - /// - /// This is implemented by first entering a new universe. - /// We then replace all bound variables in `sup` with placeholders, - /// and all bound variables in `sub` with inference vars. - /// We can then just relate the two resulting types as normal. - /// - /// Note: this is a subtle algorithm. For a full explanation, please see - /// the [rustc dev guide][rd] - /// - /// [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html - #[instrument(skip(self), level = "debug")] - pub fn higher_ranked_sub( - &mut self, - sub: Binder<'tcx, T>, - sup: Binder<'tcx, T>, - sub_is_expected: bool, - ) -> RelateResult<'tcx, ()> - where - T: Relate<'tcx>, - { - let span = self.trace.cause.span; - // First, we instantiate each bound region in the supertype with a - // fresh placeholder region. Note that this automatically creates - // a new universe if needed. - let sup_prime = self.infcx.instantiate_binder_with_placeholders(sup); - - // Next, we instantiate each bound region in the subtype - // with a fresh region variable. These region variables -- - // but no other preexisting region variables -- can name - // the placeholders. - let sub_prime = self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub); - - debug!("a_prime={:?}", sub_prime); - debug!("b_prime={:?}", sup_prime); - - // Compare types now that bound regions have been replaced. - let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime)?; - - debug!("OK result={result:?}"); - // NOTE: returning the result here would be dangerous as it contains - // placeholders which **must not** be named afterwards. - Ok(()) - } -} - -impl<'tcx> InferCtxt<'tcx> { - /// Replaces all bound variables (lifetimes, types, and constants) bound by - /// `binder` with placeholder variables in a new universe. This means that the - /// new placeholders can only be named by inference variables created after - /// this method has been called. - /// - /// This is the first step of checking subtyping when higher-ranked things are involved. - /// For more details visit the relevant sections of the [rustc dev guide]. - /// - /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html - #[instrument(level = "debug", skip(self), ret)] - pub fn instantiate_binder_with_placeholders(&self, binder: ty::Binder<'tcx, T>) -> T - where - T: TypeFoldable> + Copy, - { - if let Some(inner) = binder.no_bound_vars() { - return inner; - } - - let next_universe = self.create_next_universe(); - - let delegate = FnMutDelegate { - regions: &mut |br: ty::BoundRegion| { - ty::Region::new_placeholder( - self.tcx, - ty::PlaceholderRegion { universe: next_universe, bound: br }, - ) - }, - types: &mut |bound_ty: ty::BoundTy| { - Ty::new_placeholder( - self.tcx, - ty::PlaceholderType { universe: next_universe, bound: bound_ty }, - ) - }, - consts: &mut |bound_var: ty::BoundVar, ty| { - ty::Const::new_placeholder( - self.tcx, - ty::PlaceholderConst { universe: next_universe, bound: bound_var }, - ty, - ) - }, - }; - - debug!(?next_universe); - self.tcx.replace_bound_vars_uncached(binder, delegate) - } - - /// See [RegionConstraintCollector::leak_check][1]. We only check placeholder - /// leaking into `outer_universe`, i.e. placeholders which cannot be named by that - /// universe. - /// - /// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check - pub fn leak_check( - &self, - outer_universe: ty::UniverseIndex, - only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>, - ) -> RelateResult<'tcx, ()> { - // If the user gave `-Zno-leak-check`, or we have been - // configured to skip the leak check, then skip the leak check - // completely. The leak check is deprecated. Any legitimate - // subtyping errors that it would have caught will now be - // caught later on, during region checking. However, we - // continue to use it for a transition period. - if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check { - return Ok(()); - } - - self.inner.borrow_mut().unwrap_region_constraints().leak_check( - self.tcx, - outer_universe, - self.universe(), - only_consider_snapshot, - ) - } -} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5aa9c82b3ca..88e4787848c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -59,7 +59,6 @@ pub mod error_reporting; pub mod free_regions; mod freshen; mod fudge; -mod higher_ranked; mod lexical_region_resolve; pub mod nll_relate; pub mod opaque_types; diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs new file mode 100644 index 00000000000..440df8c8936 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -0,0 +1,136 @@ +//! Helper routines for higher-ranked things. See the `doc` module at +//! the end of the file for details. + +use super::combine::CombineFields; +use crate::infer::CombinedSnapshot; +use crate::infer::{HigherRankedType, InferCtxt}; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable}; + +impl<'a, 'tcx> CombineFields<'a, 'tcx> { + /// Checks whether `for<..> sub <: for<..> sup` holds. + /// + /// For this to hold, **all** instantiations of the super type + /// have to be a super type of **at least one** instantiation of + /// the subtype. + /// + /// This is implemented by first entering a new universe. + /// We then replace all bound variables in `sup` with placeholders, + /// and all bound variables in `sub` with inference vars. + /// We can then just relate the two resulting types as normal. + /// + /// Note: this is a subtle algorithm. For a full explanation, please see + /// the [rustc dev guide][rd] + /// + /// [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html + #[instrument(skip(self), level = "debug")] + pub fn higher_ranked_sub( + &mut self, + sub: Binder<'tcx, T>, + sup: Binder<'tcx, T>, + sub_is_expected: bool, + ) -> RelateResult<'tcx, ()> + where + T: Relate<'tcx>, + { + let span = self.trace.cause.span; + // First, we instantiate each bound region in the supertype with a + // fresh placeholder region. Note that this automatically creates + // a new universe if needed. + let sup_prime = self.infcx.instantiate_binder_with_placeholders(sup); + + // Next, we instantiate each bound region in the subtype + // with a fresh region variable. These region variables -- + // but no other preexisting region variables -- can name + // the placeholders. + let sub_prime = self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub); + + debug!("a_prime={:?}", sub_prime); + debug!("b_prime={:?}", sup_prime); + + // Compare types now that bound regions have been replaced. + let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime)?; + + debug!("OK result={result:?}"); + // NOTE: returning the result here would be dangerous as it contains + // placeholders which **must not** be named afterwards. + Ok(()) + } +} + +impl<'tcx> InferCtxt<'tcx> { + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe. This means that the + /// new placeholders can only be named by inference variables created after + /// this method has been called. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self), ret)] + pub fn instantiate_binder_with_placeholders(&self, binder: ty::Binder<'tcx, T>) -> T + where + T: TypeFoldable> + Copy, + { + if let Some(inner) = binder.no_bound_vars() { + return inner; + } + + let next_universe = self.create_next_universe(); + + let delegate = FnMutDelegate { + regions: &mut |br: ty::BoundRegion| { + ty::Region::new_placeholder( + self.tcx, + ty::PlaceholderRegion { universe: next_universe, bound: br }, + ) + }, + types: &mut |bound_ty: ty::BoundTy| { + Ty::new_placeholder( + self.tcx, + ty::PlaceholderType { universe: next_universe, bound: bound_ty }, + ) + }, + consts: &mut |bound_var: ty::BoundVar, ty| { + ty::Const::new_placeholder( + self.tcx, + ty::PlaceholderConst { universe: next_universe, bound: bound_var }, + ty, + ) + }, + }; + + debug!(?next_universe); + self.tcx.replace_bound_vars_uncached(binder, delegate) + } + + /// See [RegionConstraintCollector::leak_check][1]. We only check placeholder + /// leaking into `outer_universe`, i.e. placeholders which cannot be named by that + /// universe. + /// + /// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check + pub fn leak_check( + &self, + outer_universe: ty::UniverseIndex, + only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>, + ) -> RelateResult<'tcx, ()> { + // If the user gave `-Zno-leak-check`, or we have been + // configured to skip the leak check, then skip the leak check + // completely. The leak check is deprecated. Any legitimate + // subtyping errors that it would have caught will now be + // caught later on, during region checking. However, we + // continue to use it for a transition period. + if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check { + return Ok(()); + } + + self.inner.borrow_mut().unwrap_region_constraints().leak_check( + self.tcx, + outer_universe, + self.universe(), + only_consider_snapshot, + ) + } +} diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index e431734996f..37cbc2de27e 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -3,9 +3,10 @@ //! and heuristics in the compiler). pub(super) mod combine; -pub(super) mod equate; +mod equate; pub(super) mod generalize; -pub(super) mod glb; -pub(super) mod lattice; -pub(super) mod lub; -pub(super) mod sub; +mod glb; +mod higher_ranked; +mod lattice; +mod lub; +mod sub; diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index 69f541cba05..f64e8b39798 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,11 +1,11 @@ -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) -WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:31:1 | -- cgit 1.4.1-3-g733a5 From 0184c7616e0c898a6c69d67d16febbb9124305d1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 15 Dec 2023 16:02:33 +0000 Subject: Move nll_relate to relate::nll submodule --- compiler/rustc_infer/src/infer/mod.rs | 2 +- compiler/rustc_infer/src/infer/nll_relate/mod.rs | 717 ----------------------- compiler/rustc_infer/src/infer/relate/mod.rs | 4 +- compiler/rustc_infer/src/infer/relate/nll.rs | 717 +++++++++++++++++++++++ 4 files changed, 720 insertions(+), 720 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/nll_relate/mod.rs create mode 100644 compiler/rustc_infer/src/infer/relate/nll.rs diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 88e4787848c..3de269da22d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -51,6 +51,7 @@ use self::region_constraints::{ RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, }; pub use self::relate::combine::CombineFields; +pub use self::relate::nll as nll_relate; use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; @@ -60,7 +61,6 @@ pub mod free_regions; mod freshen; mod fudge; mod lexical_region_resolve; -pub mod nll_relate; pub mod opaque_types; pub mod outlives; mod projection; diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs deleted file mode 100644 index a4af9c8f6b2..00000000000 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ /dev/null @@ -1,717 +0,0 @@ -//! This code is kind of an alternate way of doing subtyping, -//! supertyping, and type equating, distinct from the `combine.rs` -//! code but very similar in its effect and design. Eventually the two -//! ought to be merged. This code is intended for use in NLL and chalk. -//! -//! Here are the key differences: -//! -//! - This code may choose to bypass some checks (e.g., the occurs check) -//! in the case where we know that there are no unbound type inference -//! variables. This is the case for NLL, because at NLL time types are fully -//! inferred up-to regions. -//! - This code uses "universes" to handle higher-ranked regions and -//! not the leak-check. This is "more correct" than what rustc does -//! and we are generally migrating in this direction, but NLL had to -//! get there first. -//! -//! Also, this code assumes that there are no bound types at all, not even -//! free ones. This is ok because: -//! - we are not relating anything quantified over some type variable -//! - we will have instantiated all the bound type vars already (the one -//! thing we relate in chalk are basically domain goals and their -//! constituents) - -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; -use rustc_span::{Span, Symbol}; -use std::fmt::Debug; - -use super::relate::combine::ObligationEmittingRelation; -use super::relate::generalize::{self, Generalization}; -use crate::infer::InferCtxt; -use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::traits::{Obligation, PredicateObligations}; - -pub struct TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - infcx: &'me InferCtxt<'tcx>, - - /// Callback to use when we deduce an outlives relationship. - delegate: D, - - /// How are we relating `a` and `b`? - /// - /// - Covariant means `a <: b`. - /// - Contravariant means `b <: a`. - /// - Invariant means `a == b`. - /// - Bivariant means that it doesn't matter. - ambient_variance: ty::Variance, - - ambient_variance_info: ty::VarianceDiagInfo<'tcx>, -} - -pub trait TypeRelatingDelegate<'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx>; - fn span(&self) -> Span; - - /// Push a constraint `sup: sub` -- this constraint must be - /// satisfied for the two types to be related. `sub` and `sup` may - /// be regions from the type or new variables created through the - /// delegate. - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, - ); - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); - - /// Creates a new universe index. Used when instantiating placeholders. - fn create_next_universe(&mut self) -> ty::UniverseIndex; - - /// Creates a new region variable representing a higher-ranked - /// region that is instantiated existentially. This creates an - /// inference variable, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'a` with an - /// inference variable (though `'b` would be instantiated first, - /// as a placeholder). - fn next_existential_region_var( - &mut self, - was_placeholder: bool, - name: Option, - ) -> ty::Region<'tcx>; - - /// Creates a new region variable representing a - /// higher-ranked region that is instantiated universally. - /// This creates a new region placeholder, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'b` with a - /// placeholder region. - fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; - - /// Creates a new existential region in the given universe. This - /// is used when handling subtyping and type variables -- if we - /// have that `?X <: Foo<'a>`, for example, we would instantiate - /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh - /// existential variable created by this function. We would then - /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives - /// relation stating that `'?0: 'a`). - fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; - - /// Enables some optimizations if we do not expect inference variables - /// in the RHS of the relation. - fn forbid_inference_vars() -> bool; -} - -impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - pub fn new(infcx: &'me InferCtxt<'tcx>, delegate: D, ambient_variance: ty::Variance) -> Self { - Self { - infcx, - delegate, - ambient_variance, - ambient_variance_info: ty::VarianceDiagInfo::default(), - } - } - - fn ambient_covariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Covariant | ty::Variance::Invariant => true, - ty::Variance::Contravariant | ty::Variance::Bivariant => false, - } - } - - fn ambient_contravariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Contravariant | ty::Variance::Invariant => true, - ty::Variance::Covariant | ty::Variance::Bivariant => false, - } - } - - /// Push a new outlives requirement into our output set of - /// constraints. - fn push_outlives( - &mut self, - sup: ty::Region<'tcx>, - sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, - ) { - debug!("push_outlives({:?}: {:?})", sup, sub); - - self.delegate.push_outlives(sup, sub, info); - } - - /// Relate a type inference variable with a value type. This works - /// by creating a "generalization" G of the value where all the - /// lifetimes are replaced with fresh inference values. This - /// generalization G becomes the value of the inference variable, - /// and is then related in turn to the value. So e.g. if you had - /// `vid = ?0` and `value = &'a u32`, we might first instantiate - /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, - /// and then relate `&'0 u32` with `&'a u32` (resulting in - /// relations between `'0` and `'a`). - /// - /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` - /// -- in other words, it is always an (unresolved) inference - /// variable `vid` and a type `ty` that are being related, but the - /// vid may appear either as the "a" type or the "b" type, - /// depending on where it appears in the tuple. The trait - /// `VidValuePair` lets us work with the vid/type while preserving - /// the "sidedness" when necessary -- the sidedness is relevant in - /// particular for the variance and set of in-scope things. - fn relate_ty_var>( - &mut self, - pair: PAIR, - ) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("relate_ty_var({:?})", pair); - - let vid = pair.vid(); - let value_ty = pair.value_ty(); - - // FIXME(invariance) -- this logic assumes invariance, but that is wrong. - // This only presently applies to chalk integration, as NLL - // doesn't permit type variables to appear on both sides (and - // doesn't use lazy norm). - match *value_ty.kind() { - ty::Infer(ty::TyVar(value_vid)) => { - // Two type variables: just equate them. - self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); - return Ok(value_ty); - } - - _ => (), - } - - let generalized_ty = self.generalize(value_ty, vid)?; - debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); - - if D::forbid_inference_vars() { - // In NLL, we don't have type inference variables - // floating around, so we can do this rather imprecise - // variant of the occurs-check. - assert!(!generalized_ty.has_non_region_infer()); - } - - self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); - - // Relate the generalized kind to the original one. - let result = pair.relate_generalized_ty(self, generalized_ty); - - debug!("relate_ty_var: complete, result = {:?}", result); - result - } - - fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> { - let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize( - self.infcx, - &mut self.delegate, - ty, - for_vid, - self.ambient_variance, - )?; - - if ty.is_ty_var() { - span_bug!(self.delegate.span(), "occurs check failure in MIR typeck"); - } - Ok(ty) - } - - fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; - let mut generalize = |ty, ty_is_expected| { - let var = self.infcx.next_ty_var_id_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.delegate.span(), - }, - ty::UniverseIndex::ROOT, - ); - if ty_is_expected { - self.relate_ty_var((ty, var)) - } else { - self.relate_ty_var((var, ty)) - } - }; - let (a, b) = match (a.kind(), b.kind()) { - (&ty::Alias(ty::Opaque, ..), _) => (a, generalize(b, false)?), - (_, &ty::Alias(ty::Opaque, ..)) => (generalize(a, true)?, b), - _ => unreachable!( - "expected at least one opaque type in `relate_opaques`, got {a} and {b}." - ), - }; - let cause = ObligationCause::dummy_with_span(self.delegate.span()); - let obligations = self - .infcx - .handle_opaque_type(a, b, true, &cause, self.delegate.param_env())? - .obligations; - self.delegate.register_obligations(obligations); - trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated"); - Ok(a) - } - - #[instrument(skip(self), level = "debug")] - fn instantiate_binder_with_placeholders(&mut self, binder: ty::Binder<'tcx, T>) -> T - where - T: ty::TypeFoldable> + Copy, - { - if let Some(inner) = binder.no_bound_vars() { - return inner; - } - - let mut next_region = { - let nll_delegate = &mut self.delegate; - let mut lazy_universe = None; - - move |br: ty::BoundRegion| { - // The first time this closure is called, create a - // new universe for the placeholders we will make - // from here out. - let universe = lazy_universe.unwrap_or_else(|| { - let universe = nll_delegate.create_next_universe(); - lazy_universe = Some(universe); - universe - }); - - let placeholder = ty::PlaceholderRegion { universe, bound: br }; - debug!(?placeholder); - let placeholder_reg = nll_delegate.next_placeholder_region(placeholder); - debug!(?placeholder_reg); - - placeholder_reg - } - }; - - let delegate = FnMutDelegate { - regions: &mut next_region, - types: &mut |_bound_ty: ty::BoundTy| { - unreachable!("we only replace regions in nll_relate, not types") - }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { - unreachable!("we only replace regions in nll_relate, not consts") - }, - }; - - let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate); - debug!(?replaced); - - replaced - } - - #[instrument(skip(self), level = "debug")] - fn instantiate_binder_with_existentials(&mut self, binder: ty::Binder<'tcx, T>) -> T - where - T: ty::TypeFoldable> + Copy, - { - if let Some(inner) = binder.no_bound_vars() { - return inner; - } - - let mut next_region = { - let nll_delegate = &mut self.delegate; - let mut reg_map = FxHashMap::default(); - - move |br: ty::BoundRegion| { - if let Some(ex_reg_var) = reg_map.get(&br) { - return *ex_reg_var; - } else { - let ex_reg_var = - nll_delegate.next_existential_region_var(true, br.kind.get_name()); - debug!(?ex_reg_var); - reg_map.insert(br, ex_reg_var); - - ex_reg_var - } - } - }; - - let delegate = FnMutDelegate { - regions: &mut next_region, - types: &mut |_bound_ty: ty::BoundTy| { - unreachable!("we only replace regions in nll_relate, not types") - }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { - unreachable!("we only replace regions in nll_relate, not consts") - }, - }; - - let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate); - debug!(?replaced); - - replaced - } -} - -/// When we instantiate an inference variable with a value in -/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, -/// but the ordering may vary (depending on whether the inference -/// variable was found on the `a` or `b` sides). Therefore, this trait -/// allows us to factor out common code, while preserving the order -/// when needed. -trait VidValuePair<'tcx>: Debug { - /// Extract the inference variable (which could be either the - /// first or second part of the tuple). - fn vid(&self) -> ty::TyVid; - - /// Extract the value it is being related to (which will be the - /// opposite part of the tuple from the vid). - fn value_ty(&self) -> Ty<'tcx>; - - /// Given a generalized type G that should replace the vid, relate - /// G to the value, putting G on whichever side the vid would have - /// appeared. - fn relate_generalized_ty( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>; -} - -impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { - fn vid(&self) -> ty::TyVid { - self.0 - } - - fn value_ty(&self) -> Ty<'tcx> { - self.1 - } - - fn relate_generalized_ty( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>, - { - relate.relate(generalized_ty, self.value_ty()) - } -} - -// In this case, the "vid" is the "b" type. -impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { - fn vid(&self) -> ty::TyVid { - self.1 - } - - fn value_ty(&self) -> Ty<'tcx> { - self.0 - } - - fn relate_generalized_ty( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>, - { - relate.relate(self.value_ty(), generalized_ty) - } -} - -impl<'tcx, D> TypeRelation<'tcx> for TypeRelating<'_, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.delegate.param_env() - } - - fn tag(&self) -> &'static str { - "nll::subtype" - } - - fn a_is_expected(&self) -> bool { - true - } - - #[instrument(skip(self, info), level = "trace", ret)] - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - self.ambient_variance_info = self.ambient_variance_info.xform(info); - - debug!(?self.ambient_variance); - // In a bivariant context this always succeeds. - let r = - if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? }; - - self.ambient_variance = old_ambient_variance; - - Ok(r) - } - - #[instrument(skip(self), level = "debug")] - fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let infcx = self.infcx; - - let a = self.infcx.shallow_resolve(a); - - if !D::forbid_inference_vars() { - b = self.infcx.shallow_resolve(b); - } - - if a == b { - return Ok(a); - } - - match (a.kind(), b.kind()) { - (_, &ty::Infer(ty::TyVar(vid))) => { - if D::forbid_inference_vars() { - // Forbid inference variables in the RHS. - bug!("unexpected inference var {:?}", b) - } else { - self.relate_ty_var((a, vid)) - } - } - - (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), - - ( - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), - &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id || infcx.next_trait_solver() => { - infcx.super_combine_tys(self, a, b).or_else(|err| { - // This behavior is only there for the old solver, the new solver - // shouldn't ever fail. Instead, it unconditionally emits an - // alias-relate goal. - assert!(!self.infcx.next_trait_solver()); - self.tcx().sess.span_delayed_bug( - self.delegate.span(), - "failure to relate an opaque to itself should result in an error later on", - ); - if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } - }) - } - (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) - | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if def_id.is_local() && !self.infcx.next_trait_solver() => - { - self.relate_opaques(a, b) - } - - _ => { - debug!(?a, ?b, ?self.ambient_variance); - - // Will also handle unification of `IntVar` and `FloatVar`. - self.infcx.super_combine_tys(self, a, b) - } - } - } - - #[instrument(skip(self), level = "trace")] - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!(?self.ambient_variance); - - if self.ambient_covariance() { - // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. - self.push_outlives(a, b, self.ambient_variance_info); - } - - if self.ambient_contravariance() { - // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. - self.push_outlives(b, a, self.ambient_variance_info); - } - - Ok(a) - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - mut b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let a = self.infcx.shallow_resolve(a); - - if !D::forbid_inference_vars() { - b = self.infcx.shallow_resolve(b); - } - - match b.kind() { - ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { - // Forbid inference variables in the RHS. - self.infcx.tcx.sess.span_delayed_bug( - self.delegate.span(), - format!("unexpected inference var {b:?}",), - ); - Ok(a) - } - // FIXME(invariance): see the related FIXME above. - _ => self.infcx.super_combine_consts(self, a, b), - } - } - - #[instrument(skip(self), level = "trace")] - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - // We want that - // - // ``` - // for<'a> fn(&'a u32) -> &'a u32 <: - // fn(&'b u32) -> &'b u32 - // ``` - // - // but not - // - // ``` - // fn(&'a u32) -> &'a u32 <: - // for<'b> fn(&'b u32) -> &'b u32 - // ``` - // - // We therefore proceed as follows: - // - // - Instantiate binders on `b` universally, yielding a universe U1. - // - Instantiate binders on `a` existentially in U1. - - debug!(?self.ambient_variance); - - if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { - // Fast path for the common case. - self.relate(a, b)?; - return Ok(ty::Binder::dummy(a)); - } - - if self.ambient_covariance() { - // Covariance, so we want `for<..> A <: for<..> B` -- - // therefore we compare any instantiation of A (i.e., A - // instantiated with existentials) against every - // instantiation of B (i.e., B instantiated with - // universals). - - // Reset the ambient variance to covariant. This is needed - // to correctly handle cases like - // - // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) - // - // Somewhat surprisingly, these two types are actually - // **equal**, even though the one on the right looks more - // polymorphic. The reason is due to subtyping. To see it, - // consider that each function can call the other: - // - // - The left function can call the right with `'b` and - // `'c` both equal to `'a` - // - // - The right function can call the left with `'a` set to - // `{P}`, where P is the point in the CFG where the call - // itself occurs. Note that `'b` and `'c` must both - // include P. At the point, the call works because of - // subtyping (i.e., `&'b u32 <: &{P} u32`). - let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); - - // Note: the order here is important. Create the placeholders first, otherwise - // we assign the wrong universe to the existential! - let b_replaced = self.instantiate_binder_with_placeholders(b); - let a_replaced = self.instantiate_binder_with_existentials(a); - - self.relate(a_replaced, b_replaced)?; - - self.ambient_variance = variance; - } - - if self.ambient_contravariance() { - // Contravariance, so we want `for<..> A :> for<..> B` - // -- therefore we compare every instantiation of A (i.e., - // A instantiated with universals) against any - // instantiation of B (i.e., B instantiated with - // existentials). Opposite of above. - - // Reset ambient variance to contravariance. See the - // covariant case above for an explanation. - let variance = - std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); - - let a_replaced = self.instantiate_binder_with_placeholders(a); - let b_replaced = self.instantiate_binder_with_existentials(b); - - self.relate(a_replaced, b_replaced)?; - - self.ambient_variance = variance; - } - - Ok(a) - } -} - -impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn register_predicates(&mut self, obligations: impl IntoIterator>) { - self.delegate.register_obligations( - obligations - .into_iter() - .map(|to_pred| { - Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) - }) - .collect(), - ); - } - - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.delegate.register_obligations(obligations); - } - - fn alias_relate_direction(&self) -> ty::AliasRelationDirection { - unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance") - } - - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { - self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Subtype, - ), - // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( - b.into(), - a.into(), - ty::AliasRelationDirection::Subtype, - ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( - a.into(), - b.into(), - ty::AliasRelationDirection::Equate, - ), - // FIXME(deferred_projection_equality): Implement this when we trigger it. - // Probably just need to do nothing here. - ty::Variance::Bivariant => { - unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") - } - })]); - } -} diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 37cbc2de27e..f688c2b74a6 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -1,6 +1,5 @@ //! This module contains the definitions of most `TypeRelation`s in the type system -//! (except for the NLL `TypeRelating`, and some relations used for diagnostics -//! and heuristics in the compiler). +//! (except for some relations used for diagnostics and heuristics in the compiler). pub(super) mod combine; mod equate; @@ -9,4 +8,5 @@ mod glb; mod higher_ranked; mod lattice; mod lub; +pub mod nll; mod sub; diff --git a/compiler/rustc_infer/src/infer/relate/nll.rs b/compiler/rustc_infer/src/infer/relate/nll.rs new file mode 100644 index 00000000000..afc2a8b2f62 --- /dev/null +++ b/compiler/rustc_infer/src/infer/relate/nll.rs @@ -0,0 +1,717 @@ +//! This code is kind of an alternate way of doing subtyping, +//! supertyping, and type equating, distinct from the `combine.rs` +//! code but very similar in its effect and design. Eventually the two +//! ought to be merged. This code is intended for use in NLL and chalk. +//! +//! Here are the key differences: +//! +//! - This code may choose to bypass some checks (e.g., the occurs check) +//! in the case where we know that there are no unbound type inference +//! variables. This is the case for NLL, because at NLL time types are fully +//! inferred up-to regions. +//! - This code uses "universes" to handle higher-ranked regions and +//! not the leak-check. This is "more correct" than what rustc does +//! and we are generally migrating in this direction, but NLL had to +//! get there first. +//! +//! Also, this code assumes that there are no bound types at all, not even +//! free ones. This is ok because: +//! - we are not relating anything quantified over some type variable +//! - we will have instantiated all the bound type vars already (the one +//! thing we relate in chalk are basically domain goals and their +//! constituents) + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; +use rustc_span::{Span, Symbol}; +use std::fmt::Debug; + +use super::combine::ObligationEmittingRelation; +use super::generalize::{self, Generalization}; +use crate::infer::InferCtxt; +use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::traits::{Obligation, PredicateObligations}; + +pub struct TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + infcx: &'me InferCtxt<'tcx>, + + /// Callback to use when we deduce an outlives relationship. + delegate: D, + + /// How are we relating `a` and `b`? + /// + /// - Covariant means `a <: b`. + /// - Contravariant means `b <: a`. + /// - Invariant means `a == b`. + /// - Bivariant means that it doesn't matter. + ambient_variance: ty::Variance, + + ambient_variance_info: ty::VarianceDiagInfo<'tcx>, +} + +pub trait TypeRelatingDelegate<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; + fn span(&self) -> Span; + + /// Push a constraint `sup: sub` -- this constraint must be + /// satisfied for the two types to be related. `sub` and `sup` may + /// be regions from the type or new variables created through the + /// delegate. + fn push_outlives( + &mut self, + sup: ty::Region<'tcx>, + sub: ty::Region<'tcx>, + info: ty::VarianceDiagInfo<'tcx>, + ); + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); + + /// Creates a new universe index. Used when instantiating placeholders. + fn create_next_universe(&mut self) -> ty::UniverseIndex; + + /// Creates a new region variable representing a higher-ranked + /// region that is instantiated existentially. This creates an + /// inference variable, typically. + /// + /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then + /// we will invoke this method to instantiate `'a` with an + /// inference variable (though `'b` would be instantiated first, + /// as a placeholder). + fn next_existential_region_var( + &mut self, + was_placeholder: bool, + name: Option, + ) -> ty::Region<'tcx>; + + /// Creates a new region variable representing a + /// higher-ranked region that is instantiated universally. + /// This creates a new region placeholder, typically. + /// + /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then + /// we will invoke this method to instantiate `'b` with a + /// placeholder region. + fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; + + /// Creates a new existential region in the given universe. This + /// is used when handling subtyping and type variables -- if we + /// have that `?X <: Foo<'a>`, for example, we would instantiate + /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh + /// existential variable created by this function. We would then + /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives + /// relation stating that `'?0: 'a`). + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; + + /// Enables some optimizations if we do not expect inference variables + /// in the RHS of the relation. + fn forbid_inference_vars() -> bool; +} + +impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + pub fn new(infcx: &'me InferCtxt<'tcx>, delegate: D, ambient_variance: ty::Variance) -> Self { + Self { + infcx, + delegate, + ambient_variance, + ambient_variance_info: ty::VarianceDiagInfo::default(), + } + } + + fn ambient_covariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Covariant | ty::Variance::Invariant => true, + ty::Variance::Contravariant | ty::Variance::Bivariant => false, + } + } + + fn ambient_contravariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Contravariant | ty::Variance::Invariant => true, + ty::Variance::Covariant | ty::Variance::Bivariant => false, + } + } + + /// Push a new outlives requirement into our output set of + /// constraints. + fn push_outlives( + &mut self, + sup: ty::Region<'tcx>, + sub: ty::Region<'tcx>, + info: ty::VarianceDiagInfo<'tcx>, + ) { + debug!("push_outlives({:?}: {:?})", sup, sub); + + self.delegate.push_outlives(sup, sub, info); + } + + /// Relate a type inference variable with a value type. This works + /// by creating a "generalization" G of the value where all the + /// lifetimes are replaced with fresh inference values. This + /// generalization G becomes the value of the inference variable, + /// and is then related in turn to the value. So e.g. if you had + /// `vid = ?0` and `value = &'a u32`, we might first instantiate + /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, + /// and then relate `&'0 u32` with `&'a u32` (resulting in + /// relations between `'0` and `'a`). + /// + /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` + /// -- in other words, it is always an (unresolved) inference + /// variable `vid` and a type `ty` that are being related, but the + /// vid may appear either as the "a" type or the "b" type, + /// depending on where it appears in the tuple. The trait + /// `VidValuePair` lets us work with the vid/type while preserving + /// the "sidedness" when necessary -- the sidedness is relevant in + /// particular for the variance and set of in-scope things. + fn relate_ty_var>( + &mut self, + pair: PAIR, + ) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("relate_ty_var({:?})", pair); + + let vid = pair.vid(); + let value_ty = pair.value_ty(); + + // FIXME(invariance) -- this logic assumes invariance, but that is wrong. + // This only presently applies to chalk integration, as NLL + // doesn't permit type variables to appear on both sides (and + // doesn't use lazy norm). + match *value_ty.kind() { + ty::Infer(ty::TyVar(value_vid)) => { + // Two type variables: just equate them. + self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); + return Ok(value_ty); + } + + _ => (), + } + + let generalized_ty = self.generalize(value_ty, vid)?; + debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); + + if D::forbid_inference_vars() { + // In NLL, we don't have type inference variables + // floating around, so we can do this rather imprecise + // variant of the occurs-check. + assert!(!generalized_ty.has_non_region_infer()); + } + + self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); + + // Relate the generalized kind to the original one. + let result = pair.relate_generalized_ty(self, generalized_ty); + + debug!("relate_ty_var: complete, result = {:?}", result); + result + } + + fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> { + let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize( + self.infcx, + &mut self.delegate, + ty, + for_vid, + self.ambient_variance, + )?; + + if ty.is_ty_var() { + span_bug!(self.delegate.span(), "occurs check failure in MIR typeck"); + } + Ok(ty) + } + + fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + let mut generalize = |ty, ty_is_expected| { + let var = self.infcx.next_ty_var_id_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.delegate.span(), + }, + ty::UniverseIndex::ROOT, + ); + if ty_is_expected { + self.relate_ty_var((ty, var)) + } else { + self.relate_ty_var((var, ty)) + } + }; + let (a, b) = match (a.kind(), b.kind()) { + (&ty::Alias(ty::Opaque, ..), _) => (a, generalize(b, false)?), + (_, &ty::Alias(ty::Opaque, ..)) => (generalize(a, true)?, b), + _ => unreachable!( + "expected at least one opaque type in `relate_opaques`, got {a} and {b}." + ), + }; + let cause = ObligationCause::dummy_with_span(self.delegate.span()); + let obligations = self + .infcx + .handle_opaque_type(a, b, true, &cause, self.delegate.param_env())? + .obligations; + self.delegate.register_obligations(obligations); + trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated"); + Ok(a) + } + + #[instrument(skip(self), level = "debug")] + fn instantiate_binder_with_placeholders(&mut self, binder: ty::Binder<'tcx, T>) -> T + where + T: ty::TypeFoldable> + Copy, + { + if let Some(inner) = binder.no_bound_vars() { + return inner; + } + + let mut next_region = { + let nll_delegate = &mut self.delegate; + let mut lazy_universe = None; + + move |br: ty::BoundRegion| { + // The first time this closure is called, create a + // new universe for the placeholders we will make + // from here out. + let universe = lazy_universe.unwrap_or_else(|| { + let universe = nll_delegate.create_next_universe(); + lazy_universe = Some(universe); + universe + }); + + let placeholder = ty::PlaceholderRegion { universe, bound: br }; + debug!(?placeholder); + let placeholder_reg = nll_delegate.next_placeholder_region(placeholder); + debug!(?placeholder_reg); + + placeholder_reg + } + }; + + let delegate = FnMutDelegate { + regions: &mut next_region, + types: &mut |_bound_ty: ty::BoundTy| { + unreachable!("we only replace regions in nll_relate, not types") + }, + consts: &mut |_bound_var: ty::BoundVar, _ty| { + unreachable!("we only replace regions in nll_relate, not consts") + }, + }; + + let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate); + debug!(?replaced); + + replaced + } + + #[instrument(skip(self), level = "debug")] + fn instantiate_binder_with_existentials(&mut self, binder: ty::Binder<'tcx, T>) -> T + where + T: ty::TypeFoldable> + Copy, + { + if let Some(inner) = binder.no_bound_vars() { + return inner; + } + + let mut next_region = { + let nll_delegate = &mut self.delegate; + let mut reg_map = FxHashMap::default(); + + move |br: ty::BoundRegion| { + if let Some(ex_reg_var) = reg_map.get(&br) { + return *ex_reg_var; + } else { + let ex_reg_var = + nll_delegate.next_existential_region_var(true, br.kind.get_name()); + debug!(?ex_reg_var); + reg_map.insert(br, ex_reg_var); + + ex_reg_var + } + } + }; + + let delegate = FnMutDelegate { + regions: &mut next_region, + types: &mut |_bound_ty: ty::BoundTy| { + unreachable!("we only replace regions in nll_relate, not types") + }, + consts: &mut |_bound_var: ty::BoundVar, _ty| { + unreachable!("we only replace regions in nll_relate, not consts") + }, + }; + + let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate); + debug!(?replaced); + + replaced + } +} + +/// When we instantiate an inference variable with a value in +/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, +/// but the ordering may vary (depending on whether the inference +/// variable was found on the `a` or `b` sides). Therefore, this trait +/// allows us to factor out common code, while preserving the order +/// when needed. +trait VidValuePair<'tcx>: Debug { + /// Extract the inference variable (which could be either the + /// first or second part of the tuple). + fn vid(&self) -> ty::TyVid; + + /// Extract the value it is being related to (which will be the + /// opposite part of the tuple from the vid). + fn value_ty(&self) -> Ty<'tcx>; + + /// Given a generalized type G that should replace the vid, relate + /// G to the value, putting G on whichever side the vid would have + /// appeared. + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>; +} + +impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { + fn vid(&self) -> ty::TyVid { + self.0 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.1 + } + + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(generalized_ty, self.value_ty()) + } +} + +// In this case, the "vid" is the "b" type. +impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { + fn vid(&self) -> ty::TyVid { + self.1 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.0 + } + + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(self.value_ty(), generalized_ty) + } +} + +impl<'tcx, D> TypeRelation<'tcx> for TypeRelating<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.delegate.param_env() + } + + fn tag(&self) -> &'static str { + "nll::subtype" + } + + fn a_is_expected(&self) -> bool { + true + } + + #[instrument(skip(self, info), level = "trace", ret)] + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.ambient_variance_info = self.ambient_variance_info.xform(info); + + debug!(?self.ambient_variance); + // In a bivariant context this always succeeds. + let r = + if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? }; + + self.ambient_variance = old_ambient_variance; + + Ok(r) + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let infcx = self.infcx; + + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + if a == b { + return Ok(a); + } + + match (a.kind(), b.kind()) { + (_, &ty::Infer(ty::TyVar(vid))) => { + if D::forbid_inference_vars() { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } else { + self.relate_ty_var((a, vid)) + } + } + + (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), + + ( + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), + &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id || infcx.next_trait_solver() => { + infcx.super_combine_tys(self, a, b).or_else(|err| { + // This behavior is only there for the old solver, the new solver + // shouldn't ever fail. Instead, it unconditionally emits an + // alias-relate goal. + assert!(!self.infcx.next_trait_solver()); + self.tcx().sess.span_delayed_bug( + self.delegate.span(), + "failure to relate an opaque to itself should result in an error later on", + ); + if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) } + }) + } + (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) + | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) + if def_id.is_local() && !self.infcx.next_trait_solver() => + { + self.relate_opaques(a, b) + } + + _ => { + debug!(?a, ?b, ?self.ambient_variance); + + // Will also handle unification of `IntVar` and `FloatVar`. + self.infcx.super_combine_tys(self, a, b) + } + } + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!(?self.ambient_variance); + + if self.ambient_covariance() { + // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`. + self.push_outlives(a, b, self.ambient_variance_info); + } + + if self.ambient_contravariance() { + // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`. + self.push_outlives(b, a, self.ambient_variance_info); + } + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + mut b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + match b.kind() { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + // Forbid inference variables in the RHS. + self.infcx.tcx.sess.span_delayed_bug( + self.delegate.span(), + format!("unexpected inference var {b:?}",), + ); + Ok(a) + } + // FIXME(invariance): see the related FIXME above. + _ => self.infcx.super_combine_consts(self, a, b), + } + } + + #[instrument(skip(self), level = "trace")] + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // We want that + // + // ``` + // for<'a> fn(&'a u32) -> &'a u32 <: + // fn(&'b u32) -> &'b u32 + // ``` + // + // but not + // + // ``` + // fn(&'a u32) -> &'a u32 <: + // for<'b> fn(&'b u32) -> &'b u32 + // ``` + // + // We therefore proceed as follows: + // + // - Instantiate binders on `b` universally, yielding a universe U1. + // - Instantiate binders on `a` existentially in U1. + + debug!(?self.ambient_variance); + + if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { + // Fast path for the common case. + self.relate(a, b)?; + return Ok(ty::Binder::dummy(a)); + } + + if self.ambient_covariance() { + // Covariance, so we want `for<..> A <: for<..> B` -- + // therefore we compare any instantiation of A (i.e., A + // instantiated with existentials) against every + // instantiation of B (i.e., B instantiated with + // universals). + + // Reset the ambient variance to covariant. This is needed + // to correctly handle cases like + // + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) + // + // Somewhat surprisingly, these two types are actually + // **equal**, even though the one on the right looks more + // polymorphic. The reason is due to subtyping. To see it, + // consider that each function can call the other: + // + // - The left function can call the right with `'b` and + // `'c` both equal to `'a` + // + // - The right function can call the left with `'a` set to + // `{P}`, where P is the point in the CFG where the call + // itself occurs. Note that `'b` and `'c` must both + // include P. At the point, the call works because of + // subtyping (i.e., `&'b u32 <: &{P} u32`). + let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); + + // Note: the order here is important. Create the placeholders first, otherwise + // we assign the wrong universe to the existential! + let b_replaced = self.instantiate_binder_with_placeholders(b); + let a_replaced = self.instantiate_binder_with_existentials(a); + + self.relate(a_replaced, b_replaced)?; + + self.ambient_variance = variance; + } + + if self.ambient_contravariance() { + // Contravariance, so we want `for<..> A :> for<..> B` + // -- therefore we compare every instantiation of A (i.e., + // A instantiated with universals) against any + // instantiation of B (i.e., B instantiated with + // existentials). Opposite of above. + + // Reset ambient variance to contravariance. See the + // covariant case above for an explanation. + let variance = + std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); + + let a_replaced = self.instantiate_binder_with_placeholders(a); + let b_replaced = self.instantiate_binder_with_existentials(b); + + self.relate(a_replaced, b_replaced)?; + + self.ambient_variance = variance; + } + + Ok(a) + } +} + +impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn register_predicates(&mut self, obligations: impl IntoIterator>) { + self.delegate.register_obligations( + obligations + .into_iter() + .map(|to_pred| { + Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) + }) + .collect(), + ); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.delegate.register_obligations(obligations); + } + + fn alias_relate_direction(&self) -> ty::AliasRelationDirection { + unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance") + } + + fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + self.register_predicates([ty::Binder::dummy(match self.ambient_variance { + ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Subtype, + ), + // a :> b is b <: a + ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + b.into(), + a.into(), + ty::AliasRelationDirection::Subtype, + ), + ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ), + // FIXME(deferred_projection_equality): Implement this when we trigger it. + // Probably just need to do nothing here. + ty::Variance::Bivariant => { + unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") + } + })]); + } +} -- cgit 1.4.1-3-g733a5