Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stricter hidden type wf-check [based on #115008] #121679

Merged
merged 5 commits into from
Mar 6, 2024

Conversation

lcnr
Copy link
Contributor

@lcnr lcnr commented Feb 27, 2024

Original work by @aliemjay in #115008. A huge thanks to them for originally figuring out this approach ❤️

Fixes #114728
Fixes #114572

Instead of adding the WellFormed obligations when relating opaque types, we now always emit such an obligation when defining the hidden type.

This causes nested opaque types which aren't wf to error, see the comment below for the described impact. I believe this change to be desirable as it significantly reduces complexity by removing special-cases.

It also caused an issue with RPITIT: in defaulted trait methods, we add a Projection(synthetic_assoc, rpit_of_trait_method) clause to the param_env. This clause is not added to the ParamEnv of the nested coroutines. This caused a normalization failure in fn check_coroutine_obligations with the new solver. I fixed that by using the env of the typeck root instead.

r? @oli-obk

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 27, 2024
@lcnr lcnr marked this pull request as ready for review February 27, 2024 16:30
Comment on lines -421 to -422
hir::OpaqueTyOrigin::TyAlias { .. }
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this, cc #106038 (comment) @aliemjay

I do not think this special-case is worth it and it's fairly fragile, as we cannot check that nested opaque types are well formed while in the defining scope. While I am still positive that the previous behavior was sound, having to allow types which aren't well-formed adds complexity which I don't think is worth it here.

@lcnr lcnr changed the title stricter hidden type wf-check [rebase of #115008] stricter hidden type wf-check [based on #115008] Feb 27, 2024
Copy link
Contributor

@oli-obk oli-obk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

broke my brain for 20 mins, but makes sense now

@aliemjay
Copy link
Member

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Feb 29, 2024
@bors
Copy link
Contributor

bors commented Feb 29, 2024

⌛ Trying commit af0d508 with merge 1659600...

bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 29, 2024
stricter hidden type wf-check [based on rust-lang#115008]

Original work by `@aliemjay` in rust-lang#115008. A huge thanks to them for originally figuring out this approach ❤️

Fixes rust-lang#114728
Fixes rust-lang#114572

Instead of adding the `WellFormed` obligations when relating opaque types, I add always emit such an obligation when defining the hidden type.

This causes nested opaque types which aren't wf to error, see the comment below for the described impact. I believe this change to be desirable as it significantly reduces complexity by removing special-cases.

It also caused an issue with RPITIT: in defaulted trait methods, we add a `Projection(synthetic_assoc, rpit_of_trait_method)` clause to the `param_env`. This clause is not added to the `ParamEnv` of the nested coroutines. This caused a normalization failure in `fn check_coroutine_obligations` with the new solver. I fixed that by using the env of the typeck root instead.

r? `@oli-obk`
@aliemjay
Copy link
Member

aliemjay commented Feb 29, 2024

Context: we're discussing whether the following code is valid despite the fact that the hidden type impl Sized is not technically well-formed because T: 'static is not known in test fn. However, if we consider the full context where the nested opaque type is defined impl Trait<&'static T, Assoc = impl Sized>, I believe we can safely assume that T: 'static holds.

trait Id<X> { type Ty; }
impl<X> Id<X> for () { type Ty = X; }
fn test<T>() -> impl Id<&'static T, Ty = impl Sized> {}

This was accepted as a valid defining use in #106038 and will be rejected with with this PR with the concern of increased complexity.

This causes nested opaque types which aren't wf to error, see the comment below for the described impact. I believe this change to be desirable as it significantly reduces complexity by removing special-cases.

This breaking change is not necessary for the soundness fix here and thus I prefer to at least have it in a separate PR.

Regarding the complexity concern, I believe we can keep supporting this behavior in a less complicated way than in #115008. The previous implementation in #115008 was unnecessarily complex because I wanted to have a perf baseline by emitting the least possible number of WF obligations. Moreover, I think that nested opaque types are special enough to have their own hir::OpaqueTyOrigin variant and this would arguably make the added complexity more acceptable.

@aliemjay
Copy link
Member

This causes nested opaque types which aren't wf to error, see the comment below for the described impact. I believe this change to be desirable as it significantly reduces complexity by removing special-cases.

Oh, this would also disallow a more natural pattern that is much more likely to appear in the wild:

// This is rejected currently because of #96146 but even when this is fixed
// it would still be rejected by this PR with an error that requires `T: 'static` bound in `test`
fn id<T>(s: &T) -> &T { s }
fn test<T>() -> impl Fn(&T) -> impl Sized {
  id
}

@bors
Copy link
Contributor

bors commented Feb 29, 2024

☀️ Try build successful - checks-actions
Build commit: 1659600 (1659600f7b9206c1651294a59c681822cd8ba6c7)

@rust-timer

This comment has been minimized.

@lcnr
Copy link
Contributor Author

lcnr commented Feb 29, 2024

I have strong feelings here and believe we should not support this behavior.


Regarding the complexity. My main concern is not the behavior of check_opaque_meets_bounds. I strongly believe that the following statement should be true:

We should always be able to assume, and verify, that all types are well-formed in the current context.

Allowing these patterns breaks this assumption at the point where we define the nested opaque type. This is fragile in two ways:

We will have to be careful when emitting WF constraints to avoid this breakage going forward. As shown by this PR. It is very subtle why my approach broke the wf-nested.rs tests while your approach in #115008 did not. Your approach only emits WellFormed obligations when relating opaques. We therefore avoid checking well-formedness of nested opaques as we eagerly replaced the nested opaque with an inference variable in project_and_unify_type:

// For an example where this is necessary see tests/ui/impl-trait/nested-return-type2.rs
// This allows users to omit re-mentioning all bounds on an associated type and just use an
// `impl Trait` for the assoc type to add more bounds.
let InferOk { value: actual, obligations: new } =
selcx.infcx.replace_opaque_types_with_inference_vars(
actual,
obligation.cause.body_id,
obligation.cause.span,
obligation.param_env,
);
obligations.extend(new);

This is not done in the new solver. We'd have to work around it there to continue to support this, for example by also eagerly replacing opaques with inference variables.

Even more importantly, we have to worry about accidentally leaking the non-wf type to somewhere which accepts well-formed types. We already have multiple unsoundnesses caused by a lack of WF checks, e.g. #98117. I do not want to make it more difficult to fix such issues as that may end up forcing nested opaques to be well-formed.


Supporting this pattern by amending the environment while defining nested opaque types seems a lot more acceptable. However, I believe that doing so is far from trivial.


Finally, I don't think this behavior is (very) desirable or consistent with the rest of Rust. I feel like "this was accepted as a valid defining use in #106038" is mostly irrelevant as the concern in #106038 (comment) ended up mostly slipping through and there hasn't been any official team approval of this behavior.This diverges from the behavior of where-clauses where such reasoning is not supported:

trait Id<X> { type Ty; }
impl<X> Id<X> for () { type Ty = X; }

fn test<T>() 
where
    (): Id<&'static T>,
    // (): Id<&'static T, Ty = &'static T>
    // both of these bounds error
{}

I do agree that supporting the following is generally desirable:

fn id<T>(s: &T) -> &T { s }
fn test<T>() -> impl Fn(&T) -> impl Sized {
  id
}

However, even this is imo not a strong argument to not land this PR as is. We currently do not support defining higher-ranked opaque types at all. Trying to do so always errors. I will push against any such attempts until the new solver is exclusively used as I am very much unsure how to soundly handle this when using semantic lookup for the opaque type storage.

Once implied bounds are explicitly handled and checked (necessary to fix #25860), we can support the above pattern without needing to support non-wf types as the type would now only be used inside of the Binder with the right bounds.


This breaking change is not necessary for the soundness fix here and thus I prefer to at least have it in a separate PR.

I don't. I consider the breakage for RPIT to have already been FCP'd as part of #115008.

However this is still a regression:

trait Id<X> { type Ty; }
impl<X> Id<X> for () { type Ty = X; }
fn test<T>() -> impl Id<&'static T, Ty = impl Sized> {}

We should probably ignore the wf-check nested RPIT, similar to nested TAIT.

We could separate the removal of the nested TAIT special-case into a separate PR, but given that the added WF-checks during borrowck reduce the breakage caused by that I don't think such a split is desirable. I personally don't have the energy and motivation to first land the approach of #115008 and then move the WF-checks, breaking wf-nested.rs in a separate PR.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (1659600): comparison URL.

Overall result: ❌ regressions - ACTION NEEDED

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please indicate this with @rustbot label: +perf-regression-triaged along with sufficient written justification. If you cannot justify the regressions please fix the regressions and do another perf run. If the next run shows neutral or positive results, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
3.8% [3.8%, 3.8%] 1
Regressions ❌
(secondary)
0.3% [0.3%, 0.3%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 3.8% [3.8%, 3.8%] 1

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.0% [2.0%, 2.0%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.2% [-3.2%, -3.2%] 1
All ❌✅ (primary) - - 0

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
4.0% [4.0%, 4.0%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 4.0% [4.0%, 4.0%] 1

Binary size

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
0.7% [0.7%, 0.7%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.7% [0.7%, 0.7%] 1

Bootstrap: 650.304s -> 650.995s (0.11%)
Artifact size: 311.18 MiB -> 311.16 MiB (-0.01%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Feb 29, 2024
@oli-obk
Copy link
Contributor

oli-obk commented Feb 29, 2024

The two regressions have similar up and down fluctuations over the last few weeks.

@rustbot label: +perf-regression-triaged

@rustbot rustbot added the perf-regression-triaged The performance regression has been triaged. label Feb 29, 2024
@oli-obk
Copy link
Contributor

oli-obk commented Mar 5, 2024

@bors r+

This may be stricter than necessary, but we can relax it later if we realize this is supportable in the new solver

@bors
Copy link
Contributor

bors commented Mar 5, 2024

📌 Commit af0d508 has been approved by oli-obk

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 5, 2024
@bors
Copy link
Contributor

bors commented Mar 6, 2024

⌛ Testing commit af0d508 with merge 09bc67b...

@bors
Copy link
Contributor

bors commented Mar 6, 2024

☀️ Test successful - checks-actions
Approved by: oli-obk
Pushing 09bc67b to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Mar 6, 2024
@bors bors merged commit 09bc67b into rust-lang:master Mar 6, 2024
23 checks passed
@rustbot rustbot added this to the 1.78.0 milestone Mar 6, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (09bc67b): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-2.8% [-3.5%, -2.2%] 5
All ❌✅ (primary) - - 0

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.7% [1.7%, 1.7%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 647.585s -> 646.206s (-0.21%)
Artifact size: 175.07 MiB -> 175.06 MiB (-0.01%)

@rustbot rustbot removed the perf-regression Performance regression. label Mar 6, 2024
@lcnr lcnr deleted the opaque-wf-check-2 branch March 7, 2024 10:55
@lcnr lcnr added the relnotes Marks issues that should be documented in the release notes of the next release. label Apr 2, 2024
@lcnr
Copy link
Contributor Author

lcnr commented Apr 2, 2024

This checks that RPIT are well-formed in more cases, fixing a soundness issue.

@lcnr lcnr added disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. T-types Relevant to the types team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 11, 2024
wip-sync pushed a commit to NetBSD/pkgsrc-wip that referenced this pull request May 4, 2024
Pkgsrc changes:
 * Adapt checksums and patches, some have beene intregrated upstream.

Upstream chnages:

Version 1.78.0 (2024-05-02)
===========================

Language
--------
- [Stabilize `#[cfg(target_abi = ...)]`]
  (rust-lang/rust#119590)
- [Stabilize the `#[diagnostic]` namespace and
  `#[diagnostic::on_unimplemented]` attribute]
  (rust-lang/rust#119888)
- [Make async-fn-in-trait implementable with concrete signatures]
  (rust-lang/rust#120103)
- [Make matching on NaN a hard error, and remove the rest of
  `illegal_floating_point_literal_pattern`]
  (rust-lang/rust#116284)
- [static mut: allow mutable reference to arbitrary types, not just
  slices and arrays]
  (rust-lang/rust#117614)
- [Extend `invalid_reference_casting` to include references casting
  to bigger memory layout]
  (rust-lang/rust#118983)
- [Add `non_contiguous_range_endpoints` lint for singleton gaps
  after exclusive ranges]
  (rust-lang/rust#118879)
- [Add `wasm_c_abi` lint for use of older wasm-bindgen versions]
  (rust-lang/rust#117918)
  This lint currently only works when using Cargo.
- [Update `indirect_structural_match` and `pointer_structural_match`
  lints to match RFC]
  (rust-lang/rust#120423)
- [Make non-`PartialEq`-typed consts as patterns a hard error]
  (rust-lang/rust#120805)
- [Split `refining_impl_trait` lint into `_reachable`, `_internal` variants]
  (rust-lang/rust#121720)
- [Remove unnecessary type inference when using associated types
  inside of higher ranked `where`-bounds]
  (rust-lang/rust#119849)
- [Weaken eager detection of cyclic types during type inference]
  (rust-lang/rust#119989)
- [`trait Trait: Auto {}`: allow upcasting from `dyn Trait` to `dyn Auto`]
  (rust-lang/rust#119338)

Compiler
--------

- [Made `INVALID_DOC_ATTRIBUTES` lint deny by default]
  (rust-lang/rust#111505)
- [Increase accuracy of redundant `use` checking]
  (rust-lang/rust#117772)
- [Suggest moving definition if non-found macro_rules! is defined later]
  (rust-lang/rust#121130)
- [Lower transmutes from int to pointer type as gep on null]
  (rust-lang/rust#121282)

Target changes:

- [Windows tier 1 targets now require at least Windows 10]
  (rust-lang/rust#115141)
 - [Enable CMPXCHG16B, SSE3, SAHF/LAHF and 128-bit Atomics in tier 1 Windows]
  (rust-lang/rust#120820)
- [Add `wasm32-wasip1` tier 2 (without host tools) target]
  (rust-lang/rust#120468)
- [Add `wasm32-wasip2` tier 3 target]
  (rust-lang/rust#119616)
- [Rename `wasm32-wasi-preview1-threads` to `wasm32-wasip1-threads`]
  (rust-lang/rust#122170)
- [Add `arm64ec-pc-windows-msvc` tier 3 target]
  (rust-lang/rust#119199)
- [Add `armv8r-none-eabihf` tier 3 target for the Cortex-R52]
  (rust-lang/rust#110482)
- [Add `loongarch64-unknown-linux-musl` tier 3 target]
  (rust-lang/rust#121832)

Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.

Libraries
---------

- [Bump Unicode to version 15.1.0, regenerate tables]
  (rust-lang/rust#120777)
- [Make align_offset, align_to well-behaved in all cases]
  (rust-lang/rust#121201)
- [PartialEq, PartialOrd: document expectations for transitive chains]
  (rust-lang/rust#115386)
- [Optimize away poison guards when std is built with panic=abort]
  (rust-lang/rust#100603)
- [Replace pthread `RwLock` with custom implementation]
  (rust-lang/rust#110211)
- [Implement unwind safety for Condvar on all platforms]
  (rust-lang/rust#121768)
- [Add ASCII fast-path for `char::is_grapheme_extended`]
  (rust-lang/rust#121138)

Stabilized APIs
---------------

- [`impl Read for &Stdin`]
  (https://doc.rust-lang.org/stable/std/io/struct.Stdin.html#impl-Read-for-%26Stdin)
- [Accept non `'static` lifetimes for several `std::error::Error`
  related implementations] (rust-lang/rust#113833)
- [Make `impl<Fd: AsFd>` impl take `?Sized`]
  (rust-lang/rust#114655)
- [`impl From<TryReserveError> for io::Error`]
  (https://doc.rust-lang.org/stable/std/io/struct.Error.html#impl-From%3CTryReserveError%3E-for-Error)

These APIs are now stable in const contexts:

- [`Barrier::new()`]
  (https://doc.rust-lang.org/stable/std/sync/struct.Barrier.html#method.new)

Cargo
-----

- [Stabilize lockfile v4](rust-lang/cargo#12852)
- [Respect `rust-version` when generating lockfile]
  (rust-lang/cargo#12861)
- [Control `--charset` via auto-detecting config value]
  (rust-lang/cargo#13337)
- [Support `target.<triple>.rustdocflags` officially]
  (rust-lang/cargo#13197)
- [Stabilize global cache data tracking]
  (rust-lang/cargo#13492)

Misc
----

- [rustdoc: add `--test-builder-wrapper` arg to support wrappers
  such as RUSTC_WRAPPER when building doctests]
  (rust-lang/rust#114651)

Compatibility Notes
-------------------

- [Many unsafe precondition checks now run for user code with debug
  assertions enabled] (rust-lang/rust#120594)
  This change helps users catch undefined behavior in their code,
  though the details of how much is checked are generally not
  stable.
- [riscv only supports split_debuginfo=off for now]
  (rust-lang/rust#120518)
- [Consistently check bounds on hidden types of `impl Trait`]
  (rust-lang/rust#121679)
- [Change equality of higher ranked types to not rely on subtyping]
  (rust-lang/rust#118247)
- [When called, additionally check bounds on normalized function return type]
  (rust-lang/rust#118882)
- [Expand coverage for `arithmetic_overflow` lint]
  (rust-lang/rust#119432)

Internal Changes
----------------

These changes do not affect any public interfaces of Rust, but they represent
significant improvements to the performance or internals of rustc and related
tools.

- [Update to LLVM 18](rust-lang/rust#120055)
- [Build `rustc` with 1CGU on `x86_64-pc-windows-msvc`]
  (rust-lang/rust#112267)
- [Build `rustc` with 1CGU on `x86_64-apple-darwin`]
  (rust-lang/rust#112268)
- [Introduce `run-make` V2 infrastructure, a `run_make_support`
  library and port over 2 tests as example]
  (rust-lang/rust#113026)
- [Windows: Implement condvar, mutex and rwlock using futex]
  (rust-lang/rust#121956)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. merged-by-bors This PR was explicitly merged by bors. perf-regression-triaged The performance regression has been triaged. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RPIT hidden types can be ill-formed Incorrect lifetime bound check in async + impl_trait_in_assoc_type
7 participants