Stream: t-compiler/wg-diagnostics

Topic: #57431


Saleem Jaffer (Mar 21 2019 at 19:10, on Zulip):

Hey @Santiago Pastorino let's use this thread to discuss

Santiago Pastorino (Mar 21 2019 at 20:53, on Zulip):

hey @Saleem Jaffer

Santiago Pastorino (Mar 21 2019 at 20:53, on Zulip):

sure

Santiago Pastorino (Mar 21 2019 at 20:53, on Zulip):

as I told you, I've practically no time until march 31st

Santiago Pastorino (Mar 21 2019 at 20:53, on Zulip):

anyway ...

Santiago Pastorino (Mar 21 2019 at 20:53, on Zulip):

have you read the issue and understood it properly?

Santiago Pastorino (Mar 21 2019 at 20:54, on Zulip):

do you have a test case?

Santiago Pastorino (Mar 21 2019 at 20:54, on Zulip):

I think I had even some code on a branch here

Santiago Pastorino (Mar 21 2019 at 20:54, on Zulip):

and a test too

Santiago Pastorino (Mar 21 2019 at 20:54, on Zulip):

as I told you, I've practically no time until march 31st

regardless of this, I'm going to try to help you solving this issue

Santiago Pastorino (Mar 21 2019 at 20:54, on Zulip):

but unfortunately I may be very unresponsive

Saleem Jaffer (Mar 22 2019 at 03:39, on Zulip):

have you read the issue and understood it properly?

https://github.com/rust-lang/rust/issues/57431#issuecomment-475298180 is my understanding of the issue.

do you have a test case?

I have used a sample case in the above mentioned link.

I am a rust noob, just fixed a couple of PRs in the rust-lang repo. So I'm not really sure how to get started with this issue. But I'm willing to spend time to sort this one out.

but unfortunately I may be very unresponsive

We'll do our best :)

Saleem Jaffer (Mar 22 2019 at 09:24, on Zulip):

I think librustc_mir/borrow_check/mutability_errors.rs is where I should be looking at?

Saleem Jaffer (Apr 03 2019 at 03:00, on Zulip):

@Santiago Pastorino https://github.com/rust-lang/rust/issues/57431#issuecomment-477004455 This is as far as I have been able to fund. I can figure out hacky solutions to fix this for &mut mut, but can't find a clean way. Any pointers as to what I should be doing?

Saleem Jaffer (Apr 04 2019 at 16:51, on Zulip):

https://github.com/rust-lang/rust/pull/59699

Santiago Pastorino (Apr 04 2019 at 17:22, on Zulip):

@Saleem Jaffer I'm back

Santiago Pastorino (Apr 04 2019 at 17:22, on Zulip):

we need to improve the PR

Santiago Pastorino (Apr 04 2019 at 17:22, on Zulip):

let me give you some pointers ...

Santiago Pastorino (Apr 04 2019 at 17:22, on Zulip):

sorry for the long delay

Santiago Pastorino (Apr 04 2019 at 17:22, on Zulip):

I'm starting to come back again :)

Santiago Pastorino (Apr 04 2019 at 19:12, on Zulip):

so @Saleem Jaffer, thanks for your work on this

Santiago Pastorino (Apr 04 2019 at 19:12, on Zulip):

and sorry for not being around

Santiago Pastorino (Apr 04 2019 at 19:13, on Zulip):

about the PR, we don't want to emit a help dialog and then search for the &mut text to get rid of it :)

Santiago Pastorino (Apr 04 2019 at 19:15, on Zulip):

so from this code

Santiago Pastorino (Apr 04 2019 at 19:15, on Zulip):
struct X;
impl X {
    fn mutate(&mut self) {}
}

fn foo(c: bool) {
    let mut term = X;
    let ref_term = if c {
        &mut term
    } else {
        &X
    };
    ref_term.mutate();
}
Santiago Pastorino (Apr 04 2019 at 19:16, on Zulip):

the then part of the expression &mut term is coercing to a &*

Santiago Pastorino (Apr 04 2019 at 19:18, on Zulip):

so it can match the type of the else part &X

Santiago Pastorino (Apr 04 2019 at 19:19, on Zulip):

so our code thinks that you're holding an immutable borrow but the code aimed to be a mutable borrow

Santiago Pastorino (Apr 04 2019 at 19:19, on Zulip):

so it suggest to add mutability to something that is already mutable and that's the useless tip that the compiler is giving

Santiago Pastorino (Apr 04 2019 at 19:19, on Zulip):

so the idea is to track those autogenerated coercions and in the code you're changing that and not emit the help

Santiago Pastorino (Apr 04 2019 at 19:25, on Zulip):

maybe we can add a flag here https://github.com/rust-lang/rust/blob/52980d0fb39134a26f73b39b384407e010fc3af5/src/librustc_mir/hair/cx/expr.rs#L134-L139

Saleem Jaffer (Apr 05 2019 at 07:33, on Zulip):

Should I change rustc::mir::ExprKind::Borrow from:

Borrow {
        borrow_kind: BorrowKind,
        arg: ExprRef<'tcx>,
    }

to

Borrow {
        borrow_kind: BorrowKind,
        arg: ExprRef<'tcx>,
        coerced_mut_immut: Bool
    },
Santiago Pastorino (Apr 05 2019 at 14:24, on Zulip):

yes, maybe use a auto_gen: bool field

Saleem Jaffer (Apr 05 2019 at 14:31, on Zulip):

The auto-gen has any special meaning? Or does it denote a coercion only?

Santiago Pastorino (Apr 05 2019 at 14:32, on Zulip):

no no, was suggesting a name

Santiago Pastorino (Apr 05 2019 at 14:32, on Zulip):

or maybe just coerced

Saleem Jaffer (Apr 05 2019 at 14:34, on Zulip):

Cool, I'll have to make the change to all the places where ExprKind::Borrow is created. Will get back to you if I am stuck.

pnkfelix (Apr 05 2019 at 14:37, on Zulip):

You may want to double-check whether the field belogs on ExprKind::Borrow itself, or if it should be attached to the BorrowKind variant(s). I haven't followed the conversation 100%; I just know that we have the allow_two_phase field on BorrowKind::Mut, and that is somewhat similar (in that it is meant to track where the borrow came from, in a somewhat abstract manner)

pnkfelix (Apr 05 2019 at 14:39, on Zulip):

also: are you sure you need to add the field? Why not just stop issuing the suggestion when the borrow_kind is already a BorrowKind::Mut?

pnkfelix (Apr 05 2019 at 14:39, on Zulip):

(just thinking out loud. There may be an obvious reason that doesn't work that I haven't noticed.)

Saleem Jaffer (Apr 05 2019 at 14:55, on Zulip):

The borrow_kind turns out to be a BorrowKind::Shared in the case of a coercion from &mut term to & term. Maybe that's why we can't use it?

Santiago Pastorino (Apr 05 2019 at 14:57, on Zulip):

@pnkfelix exactly, the borrowkind is shared

Santiago Pastorino (Apr 05 2019 at 14:58, on Zulip):

@Saleem Jaffer play with that and check it out just in case because I kind of forgot about it :)

Santiago Pastorino (Apr 05 2019 at 14:58, on Zulip):

would be nice to be 100% sure about it

Santiago Pastorino (Apr 05 2019 at 14:58, on Zulip):

I was looking briefly into this ~ 2 months ago and I'm not 100% sure anymore

pnkfelix (Apr 05 2019 at 15:00, on Zulip):

Hmm. I was assuming that the code was looking at whatever borrow corresponded to the &mut term.

pnkfelix (Apr 05 2019 at 15:01, on Zulip):

But obviously its not looking at that. It might be good to double-check whether the thing corresponding to &mut term is actually available for you to inspectr.

pnkfelix (Apr 05 2019 at 15:02, on Zulip):

(because we are clearly somehow tracking down &mut term, somewhere, eventually, in order to generate the undesirable message...)

pnkfelix (Apr 05 2019 at 15:02, on Zulip):

but in any case, this does answer my question about why you aren't doing "the obvious thing"

Santiago Pastorino (Apr 05 2019 at 15:04, on Zulip):

Isn't the problem of this that at mir level you really have an inmutable borrow?

Santiago Pastorino (Apr 05 2019 at 15:04, on Zulip):

because of the coercion

pnkfelix (Apr 05 2019 at 15:04, on Zulip):

oh, maybe

Santiago Pastorino (Apr 05 2019 at 15:05, on Zulip):

actually it gets the &mut part in the suggestion from the code

pnkfelix (Apr 05 2019 at 15:05, on Zulip):

ugh I forgot

pnkfelix (Apr 05 2019 at 15:05, on Zulip):

:sad: :panda:

Saleem Jaffer (Apr 05 2019 at 15:05, on Zulip):

Yeah it does not know about BorrowKind, it just knows the span

Santiago Pastorino (Apr 05 2019 at 15:06, on Zulip):

actually it gets the &mut part in the suggestion from the code

this is why if you do & /* something */ mut you would get that into the suggestion :)

pnkfelix (Apr 05 2019 at 15:07, on Zulip):

okay then. I'm still wary of plugging this into MIR, but I also do not have an immediate alternative suggestion

Saleem Jaffer (Apr 05 2019 at 15:52, on Zulip):

In order to figure out that it is a coercion I will have to look into hir_expr which is a hir::Expr because only that structure has original type.

Santiago Pastorino (Apr 05 2019 at 16:54, on Zulip):

In order to figure out that it is a coercion I will have to look into hir_expr which is a hir::Expr because only that structure has original type.

won't the place I told you be enough? if not why?

Santiago Pastorino (Apr 05 2019 at 16:55, on Zulip):

https://github.com/rust-lang/rust/blob/52980d0fb39134a26f73b39b384407e010fc3af5/src/librustc_mir/hair/cx/expr.rs#L134-L139

Santiago Pastorino (Apr 05 2019 at 16:55, on Zulip):

there you know that is an auto borrow

Saleem Jaffer (Apr 05 2019 at 20:11, on Zulip):

Does an AutoBorrow always imply a coercion from &mut to &*?

Matthew Jasper (Apr 05 2019 at 20:20, on Zulip):

no, you'll have to check m and expr.ty

Matthew Jasper (Apr 05 2019 at 20:20, on Zulip):

Or check the types later when the diagnostic is emitted

Saleem Jaffer (Apr 05 2019 at 20:23, on Zulip):

In this case m is Immutable and expr.ty is X (struct name)

Saleem Jaffer (Apr 05 2019 at 20:24, on Zulip):

So, m seems to the coerced type.

Matthew Jasper (Apr 05 2019 at 20:40, on Zulip):

OK, I think that you should just mark all auto borrows here and do the rest in the diagnostic. Getting the original reference type is enough hassle that I don't think that it should be in the HAIR lowering.

Saleem Jaffer (Apr 06 2019 at 07:32, on Zulip):

(deleted)

Saleem Jaffer (Apr 06 2019 at 07:38, on Zulip):

https://github.com/saleemjaffer/rust/blob/3752b3d3a56bf3eabb588b7d595cd1f8accc0286/src/librustc_mir/borrow_check/mod.rs#L1775-L1783

This is the function which calls report_mutability_errors, which then actually emits the help diagnostic.

The function does not have access to hair::Exprwhich has the ExprKind. So I think we should be setting the is_coerced flag at a rustc::mir::Borrowkind. BorrowKind can be either a Shared, Shallow, Unique or Mut. I'm not really sure when Shallow and Unique are applicable. But for now I am changing Shared to a

Shared {is_coerced: bool}

Finally in

fn check_access_permissions(
        &mut self,
        (place, span): (&Place<'tcx>, Span),
        kind: ReadOrWrite,
        is_local_mutation_allowed: LocalMutationIsAllowed,
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
        location: Location,
    ) -> bool {

we can check if kind is a Borrow, and check if BorrowKind has is_coerced set to true. If it is true we will not emit the help diagnostic.

How does this sound?

Saleem Jaffer (Apr 08 2019 at 11:37, on Zulip):

@Santiago Pastorino What do you think of the above idea?

Saleem Jaffer (Apr 08 2019 at 13:11, on Zulip):

I am actually a bit confused.

This is the function which emits the error as well as the suggestion for &mut mut.

            Place::Projection(box Projection {
                base: Place::Base(PlaceBase::Local(local)),
                elem: ProjectionElem::Deref,
            }) if self.mir.local_decls[*local].is_user_variable.is_some() =>
            {
                let local_decl = &self.mir.local_decls[*local];
                dbg!(&local_decl);
                let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
                    ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_)) => {
                        Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
                    }

                    ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
                        binding_mode: ty::BindingMode::BindByValue(_),
                        opt_ty_info,
                        ..
                    })) => Some(suggest_ampmut(
                        self.infcx.tcx,
                        self.mir,
                        *local,
                        local_decl,
                        *opt_ty_info,
                    )),

suggest_ampmut suggests the code to be changed as &mut mut term.

The only way I can think of fixing this is by changing binding_mode in

ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
                        binding_mode: ty::BindingMode::BindByValue(_),
                        opt_ty_info,x

binding_mode holds a Mutability defined in hir::Mutability

We were thinking of changing the BorrowKind in mir, but looks like we have to change hir::Mutability.

Am I right?

Santiago Pastorino (Apr 08 2019 at 13:59, on Zulip):

I guess we would need something like ....

Santiago Pastorino (Apr 08 2019 at 14:00, on Zulip):
pub enum BindingMode {
    BindByReference(Mutability),
    BindByValue {
        auto_gen: bool,
        mutability: Mutability,
    }
}
Santiago Pastorino (Apr 08 2019 at 14:00, on Zulip):

or something like that

Santiago Pastorino (Apr 08 2019 at 14:00, on Zulip):

but at the same time doesn't sound great

Saleem Jaffer (Apr 08 2019 at 14:00, on Zulip):

Yup

Saleem Jaffer (Apr 08 2019 at 14:00, on Zulip):

We would be changing hir

Santiago Pastorino (Apr 08 2019 at 14:00, on Zulip):

and it will make BindingMode size be increase the size

Santiago Pastorino (Apr 08 2019 at 14:00, on Zulip):

/cc @nikomatsakis

Saleem Jaffer (Apr 08 2019 at 14:16, on Zulip):

Is the size increase by adding a bool an issue? Noob doubt.

Santiago Pastorino (Apr 08 2019 at 14:27, on Zulip):

yes, the size of that enum uses 1 byte for the tag (to define which variant are we talking about), then both of them use Mutability, so the size of that right now is the byte for the tag and the size of Mutability and you need to consider padding

Santiago Pastorino (Apr 08 2019 at 14:28, on Zulip):

let me check exactly what is Mutability to consider the padding

Santiago Pastorino (Apr 08 2019 at 14:42, on Zulip):

hey back

Santiago Pastorino (Apr 08 2019 at 14:42, on Zulip):

so in the first case the size is 2 bytes and if you add that bool is 4 bytes

Santiago Pastorino (Apr 08 2019 at 14:42, on Zulip):

https://doc.rust-lang.org/std/mem/fn.size_of.html

Santiago Pastorino (Apr 08 2019 at 14:43, on Zulip):

https://doc.rust-lang.org/std/mem/fn.align_of.html

Santiago Pastorino (Apr 08 2019 at 14:43, on Zulip):

@Saleem Jaffer was looking for some resources for you to check :)

Santiago Pastorino (Apr 08 2019 at 14:44, on Zulip):

but is basically 1 byte for tag of the main enum and 1 byte for tag for the inner enum

Santiago Pastorino (Apr 08 2019 at 14:44, on Zulip):

and when you add a bool you have another byte and also one more as padding

Saleem Jaffer (Apr 08 2019 at 14:45, on Zulip):

Thanks :)

Saleem Jaffer (Apr 08 2019 at 14:46, on Zulip):

Coming from web development background a few extra bytes in a struct don’t make a difference for web apps :D

Saleem Jaffer (Apr 08 2019 at 14:47, on Zulip):

So how do we proceed with this? Do we wait for @nikomatsakis to suggest an approach?

Santiago Pastorino (Apr 08 2019 at 15:48, on Zulip):

I'd start by trying to add that field and make the thing work

Santiago Pastorino (Apr 08 2019 at 15:48, on Zulip):

then we can discuss on the PR

Santiago Pastorino (Apr 08 2019 at 15:48, on Zulip):

but based on the actual code would be better I guess

Saleem Jaffer (Apr 08 2019 at 15:58, on Zulip):

Cool! I’ll start making the changes.

Santiago Pastorino (Apr 08 2019 at 17:20, on Zulip):

Coming from web development background a few extra bytes in a struct don’t make a difference for web apps :D

yep, I'm not sure in this case if it will make a difference, would need to see where is BindingMode used and how impacts the size of the things that use it and then see if that ends adding a significant amount of memory to the overall process

nikomatsakis (Apr 09 2019 at 15:13, on Zulip):

Oh hi @Saleem Jaffer and @Santiago Pastorino --

nikomatsakis (Apr 09 2019 at 15:13, on Zulip):

@Santiago Pastorino is definitely right that increasing the size of BindingMode will increase memory usage, though it's a bit hard to say by how much

Saleem Jaffer (Apr 09 2019 at 15:14, on Zulip):

Hello @nikomatsakis

Santiago Pastorino (Apr 09 2019 at 15:15, on Zulip):

@nikomatsakis I wonder if we can just place that boolean field somewhere else with less undesirable effects

nikomatsakis (Apr 09 2019 at 15:16, on Zulip):

Well, I'm not really clear on that

nikomatsakis (Apr 09 2019 at 15:16, on Zulip):

What does this boolean represent exactly?

nikomatsakis (Apr 09 2019 at 15:17, on Zulip):

Anyway, maybe there is another place for it, but I guess I wouldn't stress about it too much

Santiago Pastorino (Apr 09 2019 at 15:20, on Zulip):

so ... it is saying that the borrow was auto generated

Santiago Pastorino (Apr 09 2019 at 15:20, on Zulip):

passing this https://github.com/rust-lang/rust/blob/52980d0fb39134a26f73b39b384407e010fc3af5/src/librustc_mir/hair/cx/expr.rs#L134-L139 info down I'd say :)

Santiago Pastorino (Apr 09 2019 at 15:21, on Zulip):

so for more extra context, you wrote something like &mut something

Santiago Pastorino (Apr 09 2019 at 15:22, on Zulip):

but you have &*(&mut something)

Saleem Jaffer (Apr 11 2019 at 05:29, on Zulip):

This what what I have been able to understand so far after some debugging.

For a match statement, we go through each of the arms and try to coerce each one. The expected_type is _ for each of the arms initially. Finally we infer the right type(not sure how). All This seems to happen in typeck::check::coercion.

What I am stuck at is: how do I revisit the arm and marked coerced as true?

Saleem Jaffer (Apr 11 2019 at 07:02, on Zulip):

What I am stuck at is: how do I revisit the arm and marked coerced as true?

Saleem Jaffer (Apr 11 2019 at 08:06, on Zulip):

(deleted)

Saleem Jaffer (Apr 11 2019 at 09:39, on Zulip):

https://github.com/rust-lang/rust/blob/d21bebe18d6851f52ddcc404e875260560599521/src/librustc_typeck/check/mod.rs#L2244

Looks like this is where we should be setting coerced to true.

Saleem Jaffer (Apr 11 2019 at 09:46, on Zulip):

This calls the above function.

Saleem Jaffer (Apr 11 2019 at 09:54, on Zulip):

Somehow I feel I am on the right track, but am not able to figure out the last bit :thinking:

Saleem Jaffer (Apr 11 2019 at 10:15, on Zulip):
pub fn apply_adjustments(&self, expr: &hir::Expr, adj: Vec<Adjustment<'tcx>>) {
        debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);

        if adj.is_empty() {
            return;
        }

        match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) {
            Entry::Vacant(entry) => { entry.insert(adj); },
            Entry::Occupied(mut entry) => {
                debug!(" - composing on top of {:?}", entry.get());
                match (&entry.get()[..], &adj[..]) {
                    // Applying any adjustment on top of a NeverToAny
                    // is a valid NeverToAny adjustment, because it can't
                    // be reached.
                    (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
                    (&[
                        Adjustment { kind: Adjust::Deref(_), .. },
                        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
                    ], &[
                        Adjustment { kind: Adjust::Deref(_), .. },
                        .. // Any following adjustments are allowed.
                    ]) => {
                        // A reborrow has no effect before a dereference.
                    }
                    // FIXME: currently we never try to compose autoderefs
                    // and ReifyFnPointer/UnsafeFnPointer, but we could.
                    _ =>
                        bug!("while adjusting {:?}, can't compose {:?} and {:?}",
                             expr, entry.get(), adj)
                };
                *entry.get_mut() = adj;
            }
        }
    }

Should we change the adj in apply_adjustments to a mut Vec<Adjustment<'tcx>> and then change the Adjustment?

Saleem Jaffer (Apr 11 2019 at 10:46, on Zulip):

the adj for &mut term is

[
    Deref(None) -> X,
    Borrow(Ref('_#5r, Immutable)) -> &X
]

I think we have to change:

pub enum AutoBorrowMutability {
    Mutable { allow_two_phase_borrow: AllowTwoPhase },
    Immutable,
}

to

pub enum AutoBorrowMutability {
    Mutable { allow_two_phase_borrow: AllowTwoPhase },
    Immutable {coerced: bool},
}
Saleem Jaffer (Apr 11 2019 at 11:01, on Zulip):

The only problem with this is that the function that actually reports the error and the help suggestion does not have access to Adjustment.

Santiago Pastorino (Apr 11 2019 at 13:11, on Zulip):

I'd need to investigate this, /cc @nikomatsakis ^^^

Saleem Jaffer (Apr 12 2019 at 13:29, on Zulip):

Do let me know if you need some work from my end.

Saleem Jaffer (Apr 12 2019 at 17:48, on Zulip):

Do let me know if you need some work from my end.

Santiago Pastorino (Apr 15 2019 at 14:01, on Zulip):

@nikomatsakis can you review this when you have a couple of minutes? ^^^

nikomatsakis (Apr 15 2019 at 16:35, on Zulip):

OK, I'm around

nikomatsakis (Apr 15 2019 at 16:45, on Zulip):

Looks like this is where we should be setting coerced to true.

this is correct

nikomatsakis (Apr 15 2019 at 17:11, on Zulip):

OK, so I spent some time catching up with the issue

nikomatsakis (Apr 15 2019 at 17:11, on Zulip):

and I think I remember a bit what is happening here

nikomatsakis (Apr 15 2019 at 17:12, on Zulip):

but I'm still not 100% sure what approach @Saleem Jaffer is taking here

nikomatsakis (Apr 15 2019 at 17:12, on Zulip):

we're trying to avoid the help message, I see

nikomatsakis (Apr 15 2019 at 17:15, on Zulip):

I'm not 100% clear on why adjustments / coercions are part of this

nikomatsakis (Apr 15 2019 at 17:16, on Zulip):

Ah, I guess it's because we want to know whether a given & in MIR is actually "synthetic" or corresponds to something the user actually wrote?

nikomatsakis (Apr 15 2019 at 17:16, on Zulip):

@Santiago Pastorino -- where in the code is the help text getting generated exactly?

Saleem Jaffer (Apr 15 2019 at 17:32, on Zulip):

Santiago Pastorino -- where in the code is the help text getting generated exactly?

https://github.com/rust-lang/rust/blob/9217fe0e2f04d61dd29c9aaebee2c993705e1d26/src/librustc_mir/borrow_check/mutability_errors.rs#L385-L395

Saleem Jaffer (Apr 15 2019 at 17:33, on Zulip):

Ah, I guess it's because we want to know whether a given & in MIR is actually "synthetic" or corresponds to something the user actually wrote?

@nikomatsakis Yes, we need to know if the & is actually a &, or a &mut coerced to a &.

We need to know this so that we can suppress the help text.

If we know that we are dealing with a &mut coerced to a &, we can stop emitting the help text.

Saleem Jaffer (Apr 16 2019 at 14:07, on Zulip):

@nikomatsakis @Santiago Pastorino Are the details I have mentioned clear, or should I be more elaborate?

Santiago Pastorino (Apr 16 2019 at 14:17, on Zulip):

I think what you've said is clear, @nikomatsakis have probably not seen your message yet :)

Last update: Nov 17 2019 at 07:10UTC