rust/compiler
bors 878c8a2a62 Auto merge of #118247 - spastorino:type-equality-subtyping, r=lcnr
change equate for binders to not rely on subtyping

*summary by `@spastorino` and `@lcnr*`

### Context

The following code:

```rust
type One = for<'a> fn(&'a (), &'a ());
type Two = for<'a, 'b> fn(&'a (), &'b ());

mod my_api {
    use std::any::Any;
    use std::marker::PhantomData;

    pub struct Foo<T: 'static> {
        a: &'static dyn Any,
        _p: PhantomData<*mut T>, // invariant, the type of the `dyn Any`
    }

    impl<T: 'static> Foo<T> {
        pub fn deref(&self) -> &'static T {
            match self.a.downcast_ref::<T>() {
                None => unsafe { std::hint::unreachable_unchecked() },
                Some(a) => a,
            }
        }

        pub fn new(a: T) -> Foo<T> {
           Foo::<T> {
                a: Box::leak(Box::new(a)),
                _p: PhantomData,
            }
        }
    }
}

use my_api::*;

fn main() {
    let foo = Foo::<One>::new((|_, _| ()) as One);
    foo.deref();
    let foo: Foo<Two> = foo;
    foo.deref();
}
```

has UB from hitting the `unreachable_unchecked`. This happens because `TypeId::of::<One>()` is not the same as `TypeId::of::<Two>()` despite them being considered the same types by the type checker.

Currently the type checker considers binders to be equal if subtyping succeeds in both directions: `for<'a> T<'a> eq for<'b> U<'b>` holds if `for<'a> exists<'b> T<'b> <: T'<a> AND for<'b> exists<'a> T<'a> <: T<'b>` holds. This results in `for<'a> fn(&'a (), &'a ())` and `for<'a, 'b> fn(&'a (), &'b ())` being equal in the type system.

`TypeId` is computed by looking at the *structure* of a type. Even though these types are semantically equal, they have a different *structure* resulting in them having different `TypeId`. This can break invariants of unsafe code at runtime and is unsound when happening at compile time, e.g. when using const generics.

So as seen in `main`, we can assign a value of type `Foo::<One>` to a binding of type `Foo<Two>` given those are considered the same type but then when we call `deref`, it calls `downcast_ref` that relies on `TypeId` and we would hit the `None` arm as these have different `TypeId`s.

As stated in https://github.com/rust-lang/rust/issues/97156#issuecomment-1879030033, this causes the API of existing crates to be unsound.

## What should we do about this

The same type resulting in different `TypeId`s  is a significant footgun, breaking a very reasonable assumptions by authors of unsafe code. It will also be unsound by itself once they are usable in generic contexts with const generics.

There are two options going forward here:
- change how the *structure* of a type is computed before relying on it. i.e. continue considering `for<'a> fn(&'a (), &'a ())` and `for<'a, 'b> fn(&'a (), &'b ())` to be equal, but normalize them to a common representation so that their `TypeId` are also the same.
- change how the semantic equality of binders to match the way we compute the structure of types. i.e. `for<'a> fn(&'a (), &'a ())` and `for<'a, 'b> fn(&'a (), &'b ())` still have different `TypeId`s but are now also considered to not be semantically equal.

---

Advantages of the first approach:
- with the second approach some higher ranked types stop being equal, even though they are subtypes of each other

General thoughts:
- changing the approach in the future will be breaking
    - going from first to second may break ordinary type checking, as types which were previously equal are now distinct
    - going from second to first may break coherence, because previously disjoint impls overlap as the used types are now equal
    - both of these are quite unlikely. This PR did not result in any crater failures, so this should not matter too much

Advantages of the second approach:
- the soundness of the first approach requires more non-local reasoning. We have to make sure that changes to subtyping do not cause the representative computation to diverge from semantic equality
    - e.g. we intend to consider higher ranked implied bounds when subtyping to [fix] https://github.com/rust-lang/rust/issues/25860, I don't know how this will interact and don't feel confident making any prediction here.
- computing a representative type is non-trivial and soundness critical, therefore adding complexity to the "core type system"

---

This PR goes with the second approach. A crater run did not result in any regressions. I am personally very hesitant about trying the first approach due to the above reasons. It feels like there are more unknowns when going that route.

### Changing the way we equate binders

Relating bound variables from different depths already results in a universe error in equate. We therefore only need to make sure that there is 1-to-1 correspondence between bound variables when relating binders. This results in concrete types being structurally equal after anonymizing their bound variables.

We implement this by instantiating one of the binder with placeholders and the other with inference variables and then equating the instantiated types. We do so in both directions.

More formally, we change the typing rules as follows:

```
for<'r0, .., 'rn> exists<'l0, .., 'ln> LHS<'l0, .., 'ln> <: RHS<'r0, .., 'rn>
for<'l0, .., 'ln> exists<'r0, .., 'rn> RHS<'r0, .., 'rn> <: LHS<'l0, .., 'ln>
--------------------------------------------------------------------------
for<'l0, .., 'ln> LHS<'l0, .., 'ln> eq for<'r0, .., 'rn> RHS<'r0, .., 'rn>
```

to
```
for<'r0, .., 'rn> exists<'l0, .., 'ln> LHS<'l0, .., 'ln> eq RHS<'r0, .., 'rn>
for<'l0, .., 'ln> exists<'r0, .., 'rn> RHS<'r0, .., 'rn> eq LHS<'l0, .., 'ln>
--------------------------------------------------------------------------
for<'l0, .., 'ln> LHS<'l0, .., 'ln> eq for<'r0, .., 'rn> RHS<'r0, .., 'rn>
```

---

Fixes #97156

r? `@lcnr`
2024-02-29 19:18:41 +00:00
..
rustc Clean up rustc_*/Cargo.toml. 2023-10-30 08:46:02 +11:00
rustc_abi fix some references to no-longer-existing ReprOptions.layout_seed 2024-02-26 10:40:48 +01:00
rustc_arena rename ptr::invalid -> ptr::without_provenance 2024-02-21 20:15:52 +01:00
rustc_ast Split rustc_type_ir to avoid rustc_ast from depending on it 2024-02-27 18:11:23 +00:00
rustc_ast_ir Split rustc_type_ir to avoid rustc_ast from depending on it 2024-02-27 18:11:23 +00:00
rustc_ast_lowering Rename DiagnosticArgFromDisplay as DiagArgFromDisplay. 2024-02-28 08:55:37 +11:00
rustc_ast_passes Rename DiagnosticBuilder as Diag. 2024-02-28 08:55:35 +11:00
rustc_ast_pretty Add ErrorGuaranteed to ast::ExprKind::Err 2024-02-25 22:24:31 +01:00
rustc_attr Rename DiagnosticBuilder as Diag. 2024-02-28 08:55:35 +11:00
rustc_baked_icu_data Bump cfg(bootstrap)s 2023-11-15 19:41:28 -05:00
rustc_borrowck Make nll higher ranked equate use bidirectional subtyping in invariant context 2024-02-29 15:27:59 -03:00
rustc_builtin_macros Remove unused diagnostic struct 2024-02-29 14:14:21 +08:00
rustc_codegen_cranelift Auto merge of #121635 - 823984418:remove_archive_builder_lifetime_a, r=nnethercote 2024-02-27 03:27:48 +00:00
rustc_codegen_gcc Rename DiagnosticArg{,Map,Name,Value} as DiagArg{,Map,Name,Value}. 2024-02-28 08:55:37 +11:00
rustc_codegen_llvm Rename DiagnosticBuilder as Diag. 2024-02-28 08:55:35 +11:00
rustc_codegen_ssa Rename DiagCtxt::with_emitter as DiagCtxt::new. 2024-02-29 16:30:12 +11:00
rustc_const_eval Auto merge of #121489 - nnethercote:diag-renaming, r=davidtwco 2024-02-28 20:39:38 +00:00
rustc_data_structures compiler: use addr_of! 2024-02-24 18:53:48 +03:00
rustc_driver Bump cfg(bootstrap)s 2023-11-15 19:41:28 -05:00
rustc_driver_impl Inline and remove HumanEmitter::stderr. 2024-02-29 17:50:23 +11:00
rustc_error_codes remove platform-intrinsics ABI; make SIMD intrinsics be regular intrinsics 2024-02-25 08:14:52 +01:00
rustc_error_messages errors: only eagerly translate subdiagnostics 2024-02-15 10:34:41 +00:00
rustc_errors Rollup merge of #121783 - nnethercote:emitter-cleanups, r=oli-obk 2024-02-29 17:08:38 +01:00
rustc_expand Rename DiagCtxt::with_emitter as DiagCtxt::new. 2024-02-29 16:30:12 +11:00
rustc_feature Rollup merge of #121740 - surechen:change_attribute_to_local_20240228, r=lcnr 2024-02-29 05:25:27 -05:00
rustc_fluent_macro Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_fs_util Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_graphviz Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_hir rename RPITIT from opaque to synthetic 2024-02-27 17:43:40 +00:00
rustc_hir_analysis Auto merge of #118247 - spastorino:type-equality-subtyping, r=lcnr 2024-02-29 19:18:41 +00:00
rustc_hir_pretty Lower anonymous structs or unions to HIR 2024-02-12 12:47:23 +08:00
rustc_hir_typeck Overhaul how stashed diagnostics work, again. 2024-02-29 11:08:27 +11:00
rustc_incremental Adjust the has_errors* methods. 2024-02-22 08:03:47 +11:00
rustc_index Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_index_macros Step all bootstrap cfgs forward 2024-02-08 07:44:34 -05:00
rustc_infer Auto merge of #118247 - spastorino:type-equality-subtyping, r=lcnr 2024-02-29 19:18:41 +00:00
rustc_interface Rename Diagnostic as DiagInner. 2024-02-28 08:33:25 +11:00
rustc_lexer Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_lint Mark some once-again-unreachable paths as unreachable. 2024-02-29 11:08:29 +11:00
rustc_lint_defs Rename DiagnosticBuilder as Diag. 2024-02-28 08:55:35 +11:00
rustc_llvm Rollup merge of #121389 - klensy:llvm-warn-fix, r=nikic 2024-02-26 16:06:02 +01:00
rustc_log Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_macros Rename DiagnosticBuilder as Diag. 2024-02-28 08:55:35 +11:00
rustc_metadata Rename DiagnosticArg{,Map,Name,Value} as DiagArg{,Map,Name,Value}. 2024-02-28 08:55:37 +11:00
rustc_middle Rollup merge of #121669 - nnethercote:count-stashed-errs-again, r=estebank 2024-02-29 17:08:38 +01:00
rustc_mir_build Rollup merge of #121376 - Nadrieril:mir-half-ranges, r=pnkfelix 2024-02-29 17:08:37 +01:00
rustc_mir_dataflow Auto merge of #120500 - oli-obk:intrinsics2.0, r=WaffleLapkin 2024-02-16 09:53:01 +00:00
rustc_mir_transform Rollup merge of #121654 - compiler-errors:async-fn-for-fn-def, r=oli-obk 2024-02-29 14:33:50 +01:00
rustc_monomorphize Rename DiagnosticBuilder as Diag. 2024-02-28 08:55:35 +11:00
rustc_next_trait_solver always emit AliasRelate goals when relating aliases 2024-02-26 10:17:43 +01:00
rustc_parse Rollup merge of #121669 - nnethercote:count-stashed-errs-again, r=estebank 2024-02-29 17:08:38 +01:00
rustc_parse_format remove a couple of redundant clones 2024-02-17 12:46:18 +01:00
rustc_passes Rollup merge of #121669 - nnethercote:count-stashed-errs-again, r=estebank 2024-02-29 17:08:38 +01:00
rustc_pattern_analysis Rollup merge of #121000 - Nadrieril:keep_all_fields, r=compiler-errors 2024-02-29 17:08:37 +01:00
rustc_privacy Mark some once-again-unreachable paths as unreachable. 2024-02-29 11:08:29 +11:00
rustc_query_impl Rename Diagnostic as DiagInner. 2024-02-28 08:33:25 +11:00
rustc_query_system Overhaul how stashed diagnostics work, again. 2024-02-29 11:08:27 +11:00
rustc_resolve Rollup merge of #121792 - GuillaumeGomez:improve-suggestion, r=michaelwoerister 2024-02-29 14:33:53 +01:00
rustc_serialize Replace NonZero::<_>::new with NonZero::new. 2024-02-15 08:09:42 +01:00
rustc_session Rollup merge of #121783 - nnethercote:emitter-cleanups, r=oli-obk 2024-02-29 17:08:38 +01:00
rustc_smir remove platform-intrinsics ABI; make SIMD intrinsics be regular intrinsics 2024-02-25 08:14:52 +01:00
rustc_span Rollup merge of #121686 - compiler-errors:rpitit-printing, r=lcnr 2024-02-28 16:04:52 +01:00
rustc_symbol_mangling Rollup merge of #121700 - rcvalle:rust-cfi-dont-compress-user-defined-builtin-types, r=compiler-errors 2024-02-29 14:33:51 +01:00
rustc_target Rollup merge of #120820 - CKingX:cpu-base-minimum, r=petrochenkov,ChrisDenton 2024-02-29 17:08:36 +01:00
rustc_trait_selection Rollup merge of #121669 - nnethercote:count-stashed-errs-again, r=estebank 2024-02-29 17:08:38 +01:00
rustc_traits Rename some normalization-related items 2024-02-20 17:30:49 +01:00
rustc_transmute Invert diagnostic lints. 2024-02-06 13:12:33 +11:00
rustc_ty_utils remove platform-intrinsics ABI; make SIMD intrinsics be regular intrinsics 2024-02-25 08:14:52 +01:00
rustc_type_ir Split rustc_type_ir to avoid rustc_ast from depending on it 2024-02-27 18:11:23 +00:00
stable_mir remove platform-intrinsics ABI; make SIMD intrinsics be regular intrinsics 2024-02-25 08:14:52 +01:00