5176 Commits

Author SHA1 Message Date
bors
8d18c32b61 Auto merge of #111570 - compiler-errors:ct-err, r=BoxyUwU
Rename const error methods for consistency

renames `ty::Const`'s methods for creating a `ConstKind::Error` to be in the same naming style as `ty::Ty`'s equivalent methods.

r? `@BoxyUwU`
2023-05-15 01:03:29 +00:00
Tomasz Miąsko
67f455afe1 Suppress "erroneous constant used" for constants tainted by errors
When constant evaluation fails because its MIR is tainted by errors,
suppress note indicating that erroneous constant was used, since those
errors have to be fixed regardless of the constant being used or not.
2023-05-15 00:00:00 +00:00
Michael Goulet
bd31d9ee9c Erase ReError properly 2023-05-14 23:22:30 +00:00
Michael Goulet
8e163f9dce Rename const error methods for consistency 2023-05-14 20:32:51 +00:00
bors
8e8116cfe5 Auto merge of #108638 - Zoxc:erase-query-values-map, r=cjgillot
Use dynamic dispatch for queries

This replaces most concrete query values `V` with `MaybeUninit<[u8; { size_of::<V>() }]>` reducing the code instantiated by queries. The compile time of `rustc_query_impl` is reduced by 27%. It is an alternative to https://github.com/rust-lang/rust/pull/107937 which uses unstable const generics while this uses a `EraseType` trait which maps query values to their erased variant.

This is achieved by introducing an `Erased` type which does sanity check with `cfg(debug_assertions)`. The query caches gets instantiated with these erased types leaving the code in `rustc_query_system` unaware of them. `rustc_query_system` is changed to use instances of `QueryConfig` so that `rustc_query_impl` can pass in `DynamicConfig` which holds a pointer to a virtual table.

<table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check</td><td align="right">1.7055s</td><td align="right">1.6949s</td><td align="right"> -0.62%</td></tr><tr><td>🟣 <b>hyper</b>:check</td><td align="right">0.2547s</td><td align="right">0.2528s</td><td align="right"> -0.73%</td></tr><tr><td>🟣 <b>regex</b>:check</td><td align="right">0.9590s</td><td align="right">0.9553s</td><td align="right"> -0.39%</td></tr><tr><td>🟣 <b>syn</b>:check</td><td align="right">1.5457s</td><td align="right">1.5440s</td><td align="right"> -0.11%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check</td><td align="right">5.9092s</td><td align="right">5.9009s</td><td align="right"> -0.14%</td></tr><tr><td>Total</td><td align="right">10.3741s</td><td align="right">10.3479s</td><td align="right"> -0.25%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9960s</td><td align="right"> -0.40%</td></tr></table>

<table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:initial</td><td align="right">2.0605s</td><td align="right">2.0575s</td><td align="right"> -0.15%</td></tr><tr><td>🟣 <b>hyper</b>:check:initial</td><td align="right">0.3218s</td><td align="right">0.3216s</td><td align="right"> -0.07%</td></tr><tr><td>🟣 <b>regex</b>:check:initial</td><td align="right">1.1848s</td><td align="right">1.1839s</td><td align="right"> -0.07%</td></tr><tr><td>🟣 <b>syn</b>:check:initial</td><td align="right">1.9409s</td><td align="right">1.9376s</td><td align="right"> -0.17%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:initial</td><td align="right">7.3105s</td><td align="right">7.2928s</td><td align="right"> -0.24%</td></tr><tr><td>Total</td><td align="right">12.8185s</td><td align="right">12.7935s</td><td align="right"> -0.20%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9986s</td><td align="right"> -0.14%</td></tr></table>

<table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:unchanged</td><td align="right">0.4606s</td><td align="right">0.4617s</td><td align="right"> 0.24%</td></tr><tr><td>🟣 <b>hyper</b>:check:unchanged</td><td align="right">0.1335s</td><td align="right">0.1336s</td><td align="right"> 0.08%</td></tr><tr><td>🟣 <b>regex</b>:check:unchanged</td><td align="right">0.3324s</td><td align="right">0.3346s</td><td align="right"> 0.65%</td></tr><tr><td>🟣 <b>syn</b>:check:unchanged</td><td align="right">0.6268s</td><td align="right">0.6307s</td><td align="right"> 0.64%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:unchanged</td><td align="right">1.8248s</td><td align="right">1.8508s</td><td align="right">💔  1.43%</td></tr><tr><td>Total</td><td align="right">3.3779s</td><td align="right">3.4113s</td><td align="right"> 0.99%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">1.0061s</td><td align="right"> 0.61%</td></tr></table>

It's based on https://github.com/rust-lang/rust/pull/108167.

r? `@cjgillot`
2023-05-14 13:47:01 +00:00
bors
bc888958c9 Auto merge of #111440 - cjgillot:refprop-debuginfo, r=oli-obk
Allow MIR debuginfo to point to a variable's address

MIR optimizations currently do not to operate on borrowed locals.

When enabling #106285, many borrows will be left as-is because they are used in debuginfo. This pass allows to replace this pattern directly in MIR debuginfo:
```rust
a => _1
_1 = &raw? mut? _2
```
becomes
```rust
a => &_2
// No statement to borrow _2.
```

This pass is implemented as a drive-by in ReferencePropagation MIR pass.

This transformation allows following following MIR opts to treat _2 as an unborrowed local, and optimize it as such, even in builds with debuginfo.

In codegen, when encountering `a => &..&_2`, we create a list of allocas:
```llvm
store ptr %_2.dbg.spill, ptr %a.ref0.dbg.spill
store ptr %a.ref0.dbg.spill, ptr %a.ref1.dbg.spill
...
call void `@llvm.dbg.declare(metadata` ptr %a.ref{n}.dbg.spill, /* ... */)
```

Caveat: this transformation looses the exact type, we do not differentiate `a` as a immutable, mutable reference or a raw pointer. Everything is declared to `*mut` to codegen. I'm not convinced this is a blocker.
2023-05-14 05:31:10 +00:00
bors
ad6ab11234 Auto merge of #111425 - Bryanskiy:privacy_ef, r=petrochenkov
Populate effective visibilities in `rustc_privacy` (take 2)

Same as https://github.com/rust-lang/rust/pull/110907 + regressions fixes.
Fixes https://github.com/rust-lang/rust/issues/111359.

r? `@petrochenkov`
2023-05-14 02:53:52 +00:00
clubby789
f77971e221 Handle error body when in generator layout 2023-05-13 16:45:19 +01:00
bors
dd8ec9c88d Auto merge of #107586 - SparrowLii:parallel-query, r=cjgillot
Introduce `DynSend` and `DynSync` auto trait for parallel compiler

part of parallel-rustc #101566

This PR introduces `DynSend / DynSync` trait and `FromDyn / IntoDyn` structure in rustc_data_structure::marker. `FromDyn` can dynamically check data structures for thread safety when switching to parallel environments (such as calling `par_for_each_in`). This happens only when `-Z threads > 1` so it doesn't affect single-threaded mode's compile efficiency.

r? `@cjgillot`
2023-05-13 13:47:53 +00:00
Camille GILLOT
7de9aac4fb Support ConstantIndex in debuginfo. 2023-05-13 10:12:15 +00:00
Camille GILLOT
2ec0071913 Implement references VarDebugInfo. 2023-05-13 10:12:14 +00:00
Dylan DPC
05ca3e31df
Rollup merge of #111451 - compiler-errors:note-cast-origin, r=b-naber
Note user-facing types of coercion failure

When coercing, for example, `Box<A>` into `Box<dyn B>`, make sure that any failure notes mention *those* specific types, rather than mentioning inner types, like "the cast from `A` to `dyn B`".

I expect end-users are often confused when we skip layers of types and only mention the "innermost" part of a coercion, especially when other notes point at HIR, e.g. #111406.
2023-05-13 11:05:33 +05:30
Dylan DPC
6cb13585d0
Rollup merge of #110454 - oli-obk:limited_impl_trait_in_assoc_type, r=compiler-errors
Require impl Trait in associated types to appear in method signatures

This implements the limited version of TAIT that was proposed in https://github.com/rust-lang/rust/issues/107645#issuecomment-1477899536

Similar to `impl Trait` in return types, `impl Trait` in associated types may only be used within the impl block which it is a part of. To make everything simpler and forward compatible to getting desugared to a plain type alias impl trait in the future, we're requiring that any associated functions or constants that want to register hidden types must be using the associated type in their signature (type of the constant or argument/return type of the associated method. Where bounds mentioning the associated type are ignored).

We have preexisting tests checking that this works transitively across multiple associated types in situations like

```rust
impl Foo for Bar {
    type A = impl Trait;
    type B = impl Iterator<Item = Self::A>;
    fn foo() -> Self::B { ...... }
}
```
2023-05-13 11:05:32 +05:30
bors
077fc26f0a Auto merge of #109732 - Urgau:uplift_drop_forget_ref_lints, r=davidtwco
Uplift `clippy::{drop,forget}_{ref,copy}` lints

This PR aims at uplifting the `clippy::drop_ref`, `clippy::drop_copy`, `clippy::forget_ref` and `clippy::forget_copy` lints.

Those lints are/were declared in the correctness category of clippy because they lint on useless and most probably is not what the developer wanted.

## `drop_ref` and `forget_ref`

The `drop_ref` and `forget_ref` lint checks for calls to `std::mem::drop` or `std::mem::forget` with a reference instead of an owned value.

### Example

```rust
let mut lock_guard = mutex.lock();
std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
// still locked
operation_that_requires_mutex_to_be_unlocked();
```

### Explanation

Calling `drop` or `forget` on a reference will only drop the reference itself, which is a no-op. It will not call the `drop` or `forget` method on the underlying referenced value, which is likely what was intended.

## `drop_copy` and `forget_copy`

The `drop_copy` and `forget_copy` lint checks for calls to `std::mem::forget` or `std::mem::drop` with a value that derives the Copy trait.

### Example

```rust
let x: i32 = 42; // i32 implements Copy
std::mem::forget(x) // A copy of x is passed to the function, leaving the
                    // original unaffected
```

### Explanation

Calling `std::mem::forget` [does nothing for types that implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the value will be copied and moved into the function on invocation.

-----

Followed the instructions for uplift a clippy describe here: https://github.com/rust-lang/rust/pull/99696#pullrequestreview-1134072751

cc `@m-ou-se` (as T-libs-api leader because the uplifting was discussed in a recent meeting)
2023-05-12 12:04:32 +00:00
Oli Scherer
4e92f761fe Use the opaque_types_defined_by query to cheaply check for whether a hidden type may be registered for an opaque type 2023-05-12 10:26:50 +00:00
lcnr
6ae803eedf add query opaque_types_defined_by 2023-05-12 10:26:07 +00:00
Oli Scherer
e2daccc4ac Add a convenience function 2023-05-12 10:25:34 +00:00
Oli Scherer
84a43f0e3a Rewrite nested if conditions into a single match 2023-05-12 10:25:06 +00:00
Oli Scherer
31ae7c46bd Invert IgnoreRegions to CheckRegions 2023-05-12 10:24:17 +00:00
Oli Scherer
f08b517597 Require impl Trait in associated types to appear in method signatures 2023-05-12 10:24:03 +00:00
bors
0b795044c6 Auto merge of #111493 - matthiaskrgr:rollup-iw1z59b, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #111179 (Fix instrument-coverage tests by using Python to sort instantiation groups)
 - #111393 (bump windows crate 0.46 -> 0.48)
 - #111441 (Verify copies of mutable pointers in 2 stages in ReferencePropagation)
 - #111456 (Update cargo)
 - #111490 (Don't ICE in layout computation for placeholder types)
 - #111492 (use by ref TokenTree iterator to avoid a few clones)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
2023-05-12 07:31:18 +00:00
Michael Goulet
3009cb3f6b Don't ICE in layout computation for placeholder types 2023-05-12 00:58:06 +00:00
Michael Goulet
6641b49cdd
Rollup merge of #111460 - clubby789:lowercase-box-self, r=compiler-errors
Improve suggestion for `self: Box<self>`

Fixes #110642
2023-05-11 17:43:09 -07:00
Michael Goulet
41ab8e6b87
Rollup merge of #111366 - obeis:ascribe-user-type-variance, r=lcnr
Make `NonUseContext::AscribeUserTy` carry `ty::Variance`

Close #108267
2023-05-11 17:43:07 -07:00
Michael Goulet
14bf909e71 Note base types of coercion 2023-05-12 00:10:52 +00:00
clubby789
3851a4bb91 Improve error for self: Box<self> 2023-05-11 13:21:10 +01:00
Bryanskiy
670f5b134e Populate effective visibilities in rustc_privacy 2023-05-11 14:51:01 +03:00
bors
f8d8ffa2eb Auto merge of #111029 - Nilstrieb:when-the-errs-are-too-big, r=petrochenkov
Shrink `SelectionError` a lot

`SelectionError` used to be 80 bytes (on 64 bit). That's quite big. Especially because the selection cache contained `Result<_, SelectionError>. The Ok type is only 32 bytes, so the 80 bytes significantly inflate the size of the cache.

Most variants of the `SelectionError` seem to be hard errors, only `Unimplemented` shows up in practice (for cranelift-codegen, it occupies 23.4% of all cache entries). We can just box away the biggest variant, `OutputTypeParameterMismatch`, to get the size down to 16 bytes, well within the size of the Ok type inside the cache.
2023-05-11 08:43:38 +00:00
Matthias Krüger
40d933a19a
Rollup merge of #108705 - clubby789:refutable-let-closure-borrow, r=cjgillot
Prevent ICE with broken borrow in closure

r? `@Nilstrieb`
Fixes #108683

This solution isn't ideal, I'm hoping to find a way to continue compilation without ICEing.
2023-05-11 07:05:26 +02:00
Michael Goulet
6f27876f62 Use OpaqueTypeKey in query response 2023-05-10 23:41:06 +00:00
Urgau
d36e390d81 Remove and fix useless drop of reference 2023-05-10 19:36:01 +02:00
Obei Sideg
2198faeee2 Make NonUseContext::AscribeUserTy carry ty::Variance 2023-05-10 09:54:56 +03:00
Matthias Krüger
70d5bf7fae
Rollup merge of #111410 - kylematsuda:earlybinder-abstract-const, r=BoxyUwU
Switch to `EarlyBinder` for `thir_abstract_const` query

Part of the work to finish https://github.com/rust-lang/rust/issues/105779.

This PR adds `EarlyBinder` to the return type of the `thir_abstract_const` query and removes `bound_abstract_const`.

r? `@compiler-errors`
2023-05-10 06:12:15 +02:00
Kyle Matsuda
26dc139b37 add EarlyBinder to thir_abstract_const; remove tcx.bound_abstract_const 2023-05-09 16:22:50 -06:00
bors
50dff955a9 Auto merge of #106285 - cjgillot:refprop-ssa, r=JakobDegen
Implement SSA-based reference propagation

Rust has a tendency to create a lot of short-lived borrows, in particular for method calls. This PR aims to remove those short-lived borrows with a const-propagation dedicated to pointers to local places.

This pass aims to transform the following pattern:
```
  _1 = &raw? mut? PLACE;
  _3 = *_1;
  _4 = &raw? mut? *_1;
```

Into
```
  _1 = &raw? mut? PLACE;
  _3 = PLACE;
  _4 = &raw? mut? PLACE;
```

where `PLACE` is a direct or an indirect place expression.

By removing indirection, this pass should help both dest-prop and const-prop to handle more cases.
This optimization is distinct from const-prop and dataflow const-prop since the borrow-reborrow patterns needs to preserve borrowck invariants, especially the uniqueness property of mutable references.

The pointed-to places are computed using a SSA analysis. We suppose that removable borrows are typically temporaries from autoref, so they are by construction assigned only once, and a SSA analysis is enough to catch them. For each local, we store both where and how it is used, in order to efficiently compute the all-or-nothing property. Thanks to `Derefer`, we only have to track locals, not places in general.

---

There are 3 properties that need to be upheld for this transformation to be legal:
- place constness: `PLACE` must refer to the same memory wherever it appears;
- pointer liveness: we must not introduce dereferences of dangling pointers;
- `&mut` borrow uniqueness.

## Constness

If `PLACE` is an indirect projection, if its of the form `(*LOCAL).PROJECTIONS` where:
- `LOCAL` is SSA;
- all projections in `PROJECTIONS` are constant (no dereference and no indexing).

If `PLACE` is a direct projection of a local, we consider it as constant if:
- the local is always live, or it has a single `StorageLive` that dominates all uses;
- all projections are constant.

# Liveness

When performing a substitution, we must take care not to introduce uses of dangling locals.

Using a dangling borrow is UB. Therefore, we assume that for any use of `*x`, where `x` is a borrow, the pointed-to memory is live.

Limitations:
- occurrences of `*x` in an `&raw mut? *x` are accepted;
- raw pointers are allowed to be dangling.

In those 2 case, we do not substitute anything, to be on the safe side.

**Open question:** we do not differentiate borrows of ZST and non-ZST. The UB rules may be
different depending on the layout. Having a different treatment would effectively prevent this
pass from running on polymorphic MIR, which defeats the purpose of MIR opts.

## Uniqueness

For `&mut` borrows, we also need to preserve the uniqueness property:
we must avoid creating a state where we interleave uses of `*_1` and `_2`.
To do it, we only perform full substitution of mutable borrows:
we replace either all or none of the occurrences of `*_1`.

Some care has to be taken when `_1` is copied in other locals.
```
   _1 = &raw? mut? _2;
   _3 = *_1;
   _4 = _1
   _5 = *_4
```
In such cases, fully substituting `_1` means fully substituting all of the copies.

For immutable borrows, we do not need to preserve such uniqueness property,
so we perform all the possible substitutions without removing the `_1 = &_2` statement.
2023-05-09 21:54:34 +00:00
Camille GILLOT
3490375570 Implement SSA-based reference propagation. 2023-05-09 17:59:34 +00:00
bors
3a37c2f052 Auto merge of #111371 - compiler-errors:revert-110907, r=petrochenkov
Revert "Populate effective visibilities in `rustc_privacy`"

This reverts commit cff85f22f5030fbe7266d272da74a9e76160523c, cc #110907. It needs to be fixed, but there are too many issues being reported that I wanted to put up a revert until a proper fix can be committed.

Fixes a ton of issues where private but still reachable impls were missing during codegen:
Fixes #111320
Fixes #111321
Fixes #111334
Fixes #111357
Fixes #111368
Fixes #111373
Fixes #111377
Fixes #111386
Fixes #111387

`@bors` p=1

r? `@petrochenkov`
2023-05-09 15:16:17 +00:00
Nilstrieb
41a9cbeb64 Shrink SelectionError a lot
`SelectionError` used to be 80 bytes (on 64 bit). That's quite big.
Especially because the selection cache contained `Result<_,
SelectionError>. The Ok type is only 32 bytes, so the 80 bytes
significantly inflate the size of the cache.

Most variants of the `SelectionError` seem to be hard errors, only
`Unimplemented` shows up in practice (for cranelift-codegen, it occupies
23.4% of all cache entries). We can just box away the biggest variant,
`OutputTypeParameterMismatch`, to get the size down to 16 bytes, well
within the size of the Ok type inside the cache.
2023-05-09 07:10:47 +00:00
Dylan DPC
f748bb1402
Rollup merge of #111252 - matthewjasper:min-spec-improvements, r=compiler-errors
Min specialization improvements

- Don't allow specialization impls with no items, such implementations are probably not correct and only occur as mistakes in the compiler and standard library
- Fix a missing normalization call
- Adds spans for lifetime errors from overly general specializations

Closes #79457
Closes #109815
2023-05-09 12:33:46 +05:30
Michael Goulet
5fcf2e6edc Revert "Populate effective visibilities in rustc_privacy"
This reverts commit cff85f22f5030fbe7266d272da74a9e76160523c.
2023-05-08 21:47:44 +00:00
Michael Goulet
29ac429c9b
Rollup merge of #109410 - fmease:iat-alias-kind-inherent, r=compiler-errors
Introduce `AliasKind::Inherent` for inherent associated types

Allows us to check (possibly generic) inherent associated types for well-formedness.
Type inference now also works properly.

Follow-up to #105961. Supersedes #108430.
Fixes #106722.
Fixes #108957.
Fixes #109768.
Fixes #109789.
Fixes #109790.

~Not to be merged before #108860 (`AliasKind::Weak`).~

CC `@jackh726`
r? `@compiler-errors`

`@rustbot` label T-types F-inherent_associated_types
2023-05-08 09:30:21 -07:00
Dylan DPC
c75543d648
Rollup merge of #111022 - Nilstrieb:smaller-bitflags, r=compiler-errors
Use smaller ints for bitflags

Free shrinking!
2023-05-08 11:39:21 +05:30
Dylan DPC
e04c9019f0
Rollup merge of #110827 - compiler-errors:issue-110761-followup, r=cjgillot
Fix lifetime suggestion for type aliases with objects in them

Fixes an issue identified in https://github.com/rust-lang/rust/issues/110761#issuecomment-1520678479

This suggestion, like many other borrowck suggestions, are very fragile and there are other ways to trigger strange behavior even after this PR, so this is just a small improvement and not a total rework 💀
2023-05-08 11:39:20 +05:30
Dylan DPC
71a1ac2c9a
Rollup merge of #110297 - kylematsuda:earlybinder_tcx_subst, r=BoxyUwU
Make `(try_)subst_and_normalize_erasing_regions` take `EarlyBinder`

Changes `subst_and_normalize_erasing_regions` and `try_subst_and_normalize_erasing_regions` to take  `EarlyBinder<T>` instead of `T`.

(related to #105779)

This was suggested by `@BoxyUwU` in https://github.com/rust-lang/rust/pull/107753#discussion_r1105828139. After changing `type_of` to return `EarlyBinder`, there were several places where the binder was immediately skipped to call `tcx.subst_and_normalize_erasing_regions`, only for the binder to be reconstructed inside of that method.

r? `@BoxyUwU`
2023-05-08 11:39:20 +05:30
Nilstrieb
f2645776dc Use smaller ints for bitflags 2023-05-07 18:24:46 +02:00
bors
0dddad0dc5 Auto merge of #111161 - compiler-errors:rtn-super, r=cjgillot
Support return-type bounds on associated methods from supertraits

Support `T: Trait<method(): Bound>` when `method` comes from a supertrait, aligning it with the behavior of associated type bounds (both equality and trait bounds).

The only wrinkle is that I have to extend `super_predicates_that_define_assoc_type` to look for *all* items, not just `AssocKind::Ty`. This will also be needed to support `feature(associated_const_equality)` as well, which is subtly broken when it comes to supertraits, though this PR does not fix those yet. There's a slight chance there's a perf regression here, in which case I guess I could split it out into a separate query.
2023-05-07 11:18:22 +00:00
Kyle Matsuda
d27f40175f changes from review: add FIXME to clippy and change subst_identity to skip_binder in mir subst methods 2023-05-06 23:36:04 -06:00
Kyle Matsuda
e4f6b8b43b make subst_mir take EarlyBinder 2023-05-06 22:32:39 -06:00
Kyle Matsuda
82f57c16b7 use EarlyBinder in tcx.(try_)subst_mir_and_normalize_erasing_regions 2023-05-06 22:32:39 -06:00
Kyle Matsuda
e5d10cdbc3 make (try_)subst_and_normalize_erasing_regions take EarlyBinder 2023-05-06 22:32:39 -06:00