|
|
Descriptiongo spec: clarify semantics of range clause
This CL proposes some subtle language changes
in an attempt to clarify the semantics of range
clauses and simplify uses of maps.
- nil maps behave like empty maps; but attempting
to set a value in a nil map causes a run-time panic
- nil channels are never ready for communication;
sending or reading from a nil channel blocks forever
- if there is only one index iteration variable in a
range clause and len(range expression) would be a constant,
the range expression is not evaluated.
(was discrepancy with len/cap before)
- the notion of what is a constant expression len(x)
for (pointer to) arrays x has been generalized and
simplified (can still be syntactically decided)
(before: more restrictive syntactic rule that was not
consistently implemented)
Fixes issue 1713.
Patch Set 1 #
Total comments: 3
Patch Set 2 : diff -r 786bf832db82 https://go.googlecode.com/hg/ #
Total comments: 1
Patch Set 3 : diff -r 1c953954868a https://go.googlecode.com/hg/ #Patch Set 4 : diff -r 1c953954868a https://go.googlecode.com/hg/ #
Total comments: 7
Patch Set 5 : diff -r 9c0e844716a6 https://go.googlecode.com/hg/ #
Total comments: 4
Patch Set 6 : diff -r 96744c61ffcf https://go.googlecode.com/hg/ #
Total comments: 3
Patch Set 7 : diff -r 96744c61ffcf https://go.googlecode.com/hg/ #
Total comments: 5
Patch Set 8 : diff -r 3211aa1f1e32 https://go.googlecode.com/hg/ #Patch Set 9 : diff -r ca45c67d28cf https://go.googlecode.com/hg/ #Patch Set 10 : diff -r ca45c67d28cf https://go.googlecode.com/hg/ #Patch Set 11 : diff -r ca45c67d28cf https://go.googlecode.com/hg/ #Patch Set 12 : diff -r 29f6e2e230a3 https://go.googlecode.com/hg/ #Patch Set 13 : diff -r 29f6e2e230a3 https://go.googlecode.com/hg/ #Patch Set 14 : diff -r 9d365f06c675 https://go.googlecode.com/hg/ #Patch Set 15 : diff -r ddb1ff38c7c5 https://go.googlecode.com/hg/ #Patch Set 16 : diff -r ddb1ff38c7c5 https://go.googlecode.com/hg/ #
Total comments: 6
Patch Set 17 : diff -r f39664db2e15 https://go.googlecode.com/hg/ #
Total comments: 1
Patch Set 18 : diff -r 1f61a34b6082 https://go.googlecode.com/hg/ #MessagesTotal messages: 29
Hello r, rsc, iant, ken2, (cc: golang-dev@googlegroups.com), I'd like you to review this change to https://go.googlecode.com/hg/
Sign in to reply to this message.
http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html#newcode3990 doc/go_spec.html:3990: occurs. A <code>nil</code> slice acts like an empty slice. Within a range clause, a nil slice is equivalent to an empty slice. http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html#newcode4010 doc/go_spec.html:4010: map acts like an empty map. ditto http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html#newcode4024 doc/go_spec.html:4024: also immediately obvious when it should panic and when it shouldn't. i think len should crash too. the only valid thing to do with a nil channel is cancel a select clause.
Sign in to reply to this message.
PTAL. I will make the language change (len(nil channel) -> panic) if everybody agrees. - gri On Tue, Apr 19, 2011 at 2:08 PM, <r@golang.org> wrote: > > http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html > File doc/go_spec.html (right): > > http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html#newcode3990 > doc/go_spec.html:3990: occurs. A <code>nil</code> slice acts like an > empty slice. > Within a range clause, a nil slice is equivalent to an empty slice. > > http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html#newcode4010 > doc/go_spec.html:4010: map acts like an empty map. > ditto > > http://codereview.appspot.com/4444050/diff/1/doc/go_spec.html#newcode4024 > doc/go_spec.html:4024: also immediately obvious when it should panic and > when it shouldn't. > i think len should crash too. > the only valid thing to do with a nil channel is cancel a select clause. > > http://codereview.appspot.com/4444050/ >
Sign in to reply to this message.
I think this is very very murky. I really have no idea what the semantics should be. The zero value for a slice is a real value, a 0-element slice, so in that one case I am sure that it should never panic, whether used in len(x), cap(x), range x, append, etc. The zero value for an array pointer is a nil reference, not a usable array. In this case it makes sense for len(x) never to panic, because len(x) is a compile-time constant. Here a range over a nil *[0]int seems fine, since it will never try to index the array; a range over a nil *[n]int for n > 0 should crash, since there is no element to obtain. This is consistent with the obvious expansion for i := 0; i < len(x); i++ etc. Note that given var x *[5]int = nil len(x) == 5, not 0, which makes this case different from the other three cases. The trickiest part is the three different forms: for i = range x for i, _ = range x for i, v = range x Arguably the first loop should just count to 5 and the last one should definitely panic, but I don't know what to do about the middle one. The zero value for a map is a nil reference, not a usable map. We defined that len(x) == 0 as a special case, and consequently range does not panic either, but I am not really sure why. If you say v = x[k] or v, ok = x[k] then you do get a panic, which seems inconsistent with len(x) not panicking. However, if len(x) == 0 does not panic, then it seems like the range loop should never try to execute, in which case it will not panic either. That is, len(x) should panic if and only if range x does. The zero value for a channel is a nil reference, not a usable channel. I think the situation is the same as for map: whatever we decide for len applies to range.
Sign in to reply to this message.
i agree it's murky, but i think that's largely because it's a set of unimportant semantic corner cases that never arise and aren't based on fundamentals. we just made some decisions. we can fight over the details if you want, but honestly i think it's fine just to codify what we have now and revisit the decision elementwise if there are genuinely important reasons to change. -rob
Sign in to reply to this message.
I'm happy with len(nil channel) being a panic. I'm more interested in specifying what these cases mean than in arguing about what they should mean. I guess it's a bit analogous to gofmt: decide and move on. But we do need a decision, and if having len(nil chan) panic helps us decide, fine. -rob
Sign in to reply to this message.
http://codereview.appspot.com/4444050/diff/4001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/4001/doc/go_spec.html#newcode3973 doc/go_spec.html:3973: is equivalent to an empty slice or map. In a range expression, a nil slice or map is equivalent to an empty slice or map.
Sign in to reply to this message.
On Tue, Apr 19, 2011 at 3:14 PM, Rob 'Commander' Pike <r@google.com> wrote: > i agree it's murky, but i think that's largely because it's a set of > unimportant semantic corner cases that never arise fwiw, I've run into these cases a number of times. A few times I've ran to the spec looking for guidance, a few times I've added unnecessary(?) defensive checks, a few times I just blindly carried on, figuring I'll get a panic if I messed up. Clarifying it one way or another would be enough but if we're going to err in one clueless direction now, I'd err towards panicing where we're unsure or in debate. It's easier to relax the rules in the future.
Sign in to reply to this message.
PTAL. I have made an attempt to fix the situation; and thus the newly uploaded version contains clarifications and some language changes for corner cases. The CL description is updated accordingly: http://codereview.appspot.com/4444050/ To address Russ' comments: On Tue, Apr 19, 2011 at 2:20 PM, <rsc@golang.org> wrote: > I think this is very very murky. > I really have no idea what the semantics should be. > > The zero value for a slice is a real value, a 0-element slice, > so in that one case I am sure that it should never panic, > whether used in len(x), cap(x), range x, append, etc. Agreed and codified. No change here. > > The zero value for an array pointer is a nil reference, > not a usable array. In this case it makes sense for len(x) > never to panic, because len(x) is a compile-time constant. > Here a range over a nil *[0]int seems fine, since it will never > try to index the array; a range over a nil *[n]int for n > 0 > should crash, since there is no element to obtain. > This is consistent with the obvious expansion > for i := 0; i < len(x); i++ etc. Note that given > var x *[5]int = nil > len(x) == 5, not 0, which makes this case different from > the other three cases. The trickiest part is the three different forms: > for i = range x > for i, _ = range x > for i, v = range x > Arguably the first loop should just count to 5 and the last > one should definitely panic, but I don't know what to do > about the middle one. > This ties in subtly with when len(x) is a constant expression if x is a (pointer to an) array type. The old spec had a couple of very specific cases only. I tried to generalize it a bit (the implementations allowed more general cases already) but it should still be syntactically possible to decide when len(x) is a constant expression if x is of array or pointer to array type. To make this consistent, corresponding range expressions do not evaluate the range expression in this case, unless there is a 2nd iteration variable. I think that's not too complicated (and I'm not sure that we need to consider the 0 array length case) but still preserves the idea. Also, the case "i, _ := range" (2nd iteration variable is _) should be treated like the case "i := range" in my mind because one might simplify this case (gofmt -s does this already). > The zero value for a map is a nil reference, not a usable map. > We defined that len(x) == 0 as a special case, and > consequently range does not panic either, but I am > not really sure why. If you say v = x[k] or v, ok = x[k] > then you do get a panic, which seems inconsistent with > len(x) not panicking. However, if len(x) == 0 does not > panic, then it seems like the range loop should never > try to execute, in which case it will not panic either. > That is, len(x) should panic if and only if range x does. Agreed. Changed so that len(nil map) and range over nil maps does panic now. > > The zero value for a channel is a nil reference, not a usable channel. > I think the situation is the same as for map: whatever we > decide for len applies to range. Agreed: Changed like for maps. - gri > > > > http://codereview.appspot.com/4444050/ >
Sign in to reply to this message.
http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode3972 doc/go_spec.html:3972: except for arrays where it may not be evaluated at all (see below). need punctuation and clarity. except if the expression is an array, in which case it may not be evaluated at all (see below). http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode3993 doc/go_spec.html:3993: operations, or function calls (i.e., <code>len(x)</code> is a constant), s/, or/ or/ s/i.e./that is,/ that said, this is a big and complex piece of prose. it might be worth rewriting for clarity. the 'special case' sounds like a lot of special cases. http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode4465 doc/go_spec.html:4465: a <code>nil</code> slice is 0. At any time the following relationship holds: s/is/are/ http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode4482 doc/go_spec.html:4482: any index or channel operations, or function calls; in this case <code>s</code> s/,,/
Sign in to reply to this message.
PTAL http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode3972 doc/go_spec.html:3972: except for arrays where it may not be evaluated at all (see below). On 2011/04/20 23:17:16, r wrote: > need punctuation and clarity. > > except if the expression is an array, in which case it may not be evaluated at > all (see below). Done. http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode4465 doc/go_spec.html:4465: a <code>nil</code> slice is 0. At any time the following relationship holds: On 2011/04/20 23:17:16, r wrote: > s/is/are/ Done. I believe you, and I had written it using "are" originally, but it sounds funny to my ears. But English is foreign to me... http://codereview.appspot.com/4444050/diff/12001/doc/go_spec.html#newcode4482 doc/go_spec.html:4482: any index or channel operations, or function calls; in this case <code>s</code> On 2011/04/20 23:17:16, r wrote: > s/,,/ Done. Rephrased. The only channel operation in an expression is a receive (sends are statements now).
Sign in to reply to this message.
http://codereview.appspot.com/4444050/diff/4006/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/4006/doc/go_spec.html#newcode3974 doc/go_spec.html:3974: except if the expression is an array, in which case it may not be hmmm. still not right. in which case, depending on the expression, it might not be evaluated (see below). http://codereview.appspot.com/4444050/diff/4006/doc/go_spec.html#newcode3994 doc/go_spec.html:3994: <code>x</code> does not contain index operations, channel receices, or function s/receices/receives/
Sign in to reply to this message.
PTAL http://codereview.appspot.com/4444050/diff/4006/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/4006/doc/go_spec.html#newcode3974 doc/go_spec.html:3974: except if the expression is an array, in which case it may not be On 2011/04/21 00:07:17, r wrote: > hmmm. still not right. > in which case, depending on the expression, it might not be evaluated (see > below). Done. http://codereview.appspot.com/4444050/diff/4006/doc/go_spec.html#newcode3994 doc/go_spec.html:3994: <code>x</code> does not contain index operations, channel receices, or function On 2011/04/21 00:07:17, r wrote: > s/receices/receives/ Done.
Sign in to reply to this message.
http://codereview.appspot.com/4444050/diff/16001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/16001/doc/go_spec.html#newcode3969 doc/go_spec.html:3969: it can always be omitted without changing the meaning of the range clause. change this line to: the range clause is equivalent to the same clause with only the first variable present. (see below) http://codereview.appspot.com/4444050/diff/16001/doc/go_spec.html#newcode3993 doc/go_spec.html:3993: case, if only the first iteration variable is present and the range expression this gets clearer if you make the change above.
Sign in to reply to this message.
and uploaded http://codereview.appspot.com/4444050/diff/16001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/16001/doc/go_spec.html#newcode3993 doc/go_spec.html:3993: case, if only the first iteration variable is present and the range expression On 2011/04/21 17:12:00, r wrote: > this gets clearer if you make the change above. Done.
Sign in to reply to this message.
Not before the next weekly release. I am not convinced that the nil map and channel should panic. If anything we have been moving toward not panicking for things like that. For example m[k] used to be a panic for a map m without a key k, but now it evaluates to the zero value. The specialness of the nil channel in a select compared to other operations was problematic when I wrote the select optimizations. For example select { case c <- 1: } is equivalent to c <- 1 except when c == nil, so you can't actually convert between them. You have to convert the former into if c == nil { select {} } else { c <- 1 } One way to resolve both the range confusion and the select peculiarity is to define that a nil channel is one with length and capacity zero that is never ready for send or receive. Then select keeps working as it does now, and the rest of the language is consistent. We would then be able to rewrite the select above into the single send statement, range on nil channel would be well defined and consistent with select, and so on. That's a good property for program transformations. Similarly I would lean toward defining the nil map to be for which m[k] always returns the zero value and trying to set m[k] causes a panic. I think being able to read from a nil map would simplify code almost as much as being able to do m[k] without the ,ok has. http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html#newcode3994 doc/go_spec.html:3994: <code>x</code> does not contain index operations, channel receives, or function Why index operations? Presumably because you can see the out of bounds error? But then why not any pointer dereference too?
Sign in to reply to this message.
On Thu, Apr 21, 2011 at 12:45 PM, <rsc@golang.org> wrote: > Not before the next weekly release. sure - there is no rush here > > I am not convinced that the nil map and channel should panic. > If anything we have been moving toward not panicking for > things like that. For example m[k] used to be a panic for > a map m without a key k, but now it evaluates to the zero value. that is fine; I don't have strong feelings here, but I'd like it to be consistent (if len(nil-map) panics, the range should panic; if len(nil-map) doesn't panic, the range shouldn't; and probably similarly for channels > > The specialness of the nil channel in a select compared to > other operations was problematic when I wrote the select optimizations. > For example > > select { > case c <- 1: > } > > is equivalent to > > c <- 1 > > except when c == nil, so you can't actually convert between them. > You have to convert the former into > > if c == nil { > select {} > } else { > c <- 1 > } > > One way to resolve both the range confusion and the select > peculiarity is to define that a nil channel is one with length > and capacity zero that is never ready for send or receive. > Then select keeps working as it does now, and the rest of > the language is consistent. We would then be able to > rewrite the select above into the single send statement, > range on nil channel would be well defined and consistent > with select, and so on. That's a good property for > program transformations. ok > > Similarly I would lean toward defining the nil map to be > for which m[k] always returns the zero value and trying > to set m[k] causes a panic. I think being able to read > from a nil map would simplify code almost as much as > being able to do m[k] without the ,ok has. worth a try > > > http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html > File doc/go_spec.html (right): > > http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html#newcode3994 > doc/go_spec.html:3994: <code>x</code> does not contain index operations, > channel receives, or function > Why index operations? > Presumably because you can see the out of bounds error? > But then why not any pointer dereference too? No, because one cannot syntactically distinguish array indices from map indices, and - in the current write-up - nil maps are not "usable" (and panic (= side-effect) when accessed). I think if we make nil map lookups return the zero value, I'd be fine with permitting indices here. Of course nil-pointer access has the same problem (panic), but we allow them already and it may be useful to have them. > > http://codereview.appspot.com/4444050/ > I am waiting for other feedback before rewriting it correspondingly. - Robert
Sign in to reply to this message.
http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html#newcode3996 doc/go_spec.html:3996: is a constant). I see what you are trying to do but to me it seems much simpler to just say that the range expression is always evaluated. That seems easier to understand, and I don't think it ever makes a difference to real programs. And then the changes above are not required. http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html#newcode4456 doc/go_spec.html:4456: map[K]T map length (number of defined keys) s != nil I suppose I think that len applied to a nil map should return 0. That seems like it might simplify some programs and I don't see that it does any harm. If you want to argue that we should therefore permit range on a nil map, I'm OK with that, although I'm personally also OK with the reverse. http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html#newcode4476 doc/go_spec.html:4476: map or channel causes a <a href="#Run_time_panics">run-time panic</a>. So I'm saying that this clause should not mention map. I agree that len/cap on a nil channel can be a panic. It seems very unlikely that permitting len on a nil channel would make any program simpler. http://codereview.appspot.com/4444050/diff/5002/doc/go_spec.html#newcode4487 doc/go_spec.html:4487: constant and <code>s</code> is evaluated. I think we should take "index operations" out of that list. It doesn't make sense to put index operations on the list unless we also put pointer indirections on the list, and we don't want to do that.
Sign in to reply to this message.
On 21 April 2011 20:45, <rsc@golang.org> wrote: > Not before the next weekly release. > > I am not convinced that the nil map and channel should panic. > If anything we have been moving toward not panicking for > things like that. For example m[k] used to be a panic for > a map m without a key k, but now it evaluates to the zero value. > > The specialness of the nil channel in a select compared to > other operations was problematic when I wrote the select optimizations. > For example > > select { > case c <- 1: > } > > is equivalent to > > c <- 1 > > except when c == nil, so you can't actually convert between them. > You have to convert the former into > > if c == nil { > select {} > } else { > c <- 1 > } > > One way to resolve both the range confusion and the select > peculiarity is to define that a nil channel is one with length > and capacity zero that is never ready for send or receive. > Then select keeps working as it does now, and the rest of > the language is consistent. We would then be able to > rewrite the select above into the single send statement, > range on nil channel would be well defined and consistent > with select, and so on. That's a good property for > program transformations. i quite like this idea, but i'd be concerned that as a consequence using an nil channel outside select would hang up rather than panic. i think that failing early in that common case is best. > Similarly I would lean toward defining the nil map to be > for which m[k] always returns the zero value and trying > to set m[k] causes a panic. I think being able to read > from a nil map would simplify code almost as much as > being able to do m[k] without the ,ok has. +1
Sign in to reply to this message.
PTAL. - nil maps behave like empty maps - nil channels are never ready for communication - slight generalization of what is a constant len() expression - respective adjustments for range clauses - gri
Sign in to reply to this message.
Hello r, rsc, iant, ken2, r2, bradfitz, rog (cc: golang-dev@googlegroups.com), I'd like you to review this change to https://go.googlecode.com/hg/
Sign in to reply to this message.
LGTM http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html#newcode2432 doc/go_spec.html:2432: Similarly, if an assignment to a map has the special form s/to a map/& element/ http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html#newcode2447 doc/go_spec.html:2447: Assigning to a <code>nil</code> map causes a Assigning to an element of a just to be extra clear http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html#newcode4012 doc/go_spec.html:4012: case, if only the first iteration variable is present and the range expression Instead of listing this condition both here and in the discussion of when len(x) is a constant, can we write something that just refers to that other section? For example, As a special case, if only the first iteration variable is present, the range loop produces iteration values from 0 up to len(x) and does not index into the array itself.
Sign in to reply to this message.
i'll do a careful review later but it looks good on quick scan probably want to add an implementation restriction at the bottom explaining the gap between implementation and spec.
Sign in to reply to this message.
PTAL. Also added more text to the section on implementation differences. http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html#newcode2432 doc/go_spec.html:2432: Similarly, if an assignment to a map has the special form On 2011/05/10 17:57:41, rsc wrote: > s/to a map/& element/ Done. http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html#newcode2447 doc/go_spec.html:2447: Assigning to a <code>nil</code> map causes a On 2011/05/10 17:57:41, rsc wrote: > Assigning to an element of a > > just to be extra clear Done. http://codereview.appspot.com/4444050/diff/30001/doc/go_spec.html#newcode4012 doc/go_spec.html:4012: case, if only the first iteration variable is present and the range expression On 2011/05/10 17:57:41, rsc wrote: > Instead of listing this condition both here and in the discussion > of when len(x) is a constant, can we write something that just > refers to that other section? For example, > > As a special case, if only the first iteration variable is present, the range > loop produces iteration values from 0 up to len(x) and does not index into the > array itself. > Done. Folded slice case back into this paragraph. Now matches the table above again.
Sign in to reply to this message.
LGTM
Sign in to reply to this message.
LGTM
Sign in to reply to this message.
LGTM http://codereview.appspot.com/4444050/diff/30002/doc/go_spec.html File doc/go_spec.html (right): http://codereview.appspot.com/4444050/diff/30002/doc/go_spec.html#newcode1178 doc/go_spec.html:1178: A <code>nil</code> map behaves like an empty map but no elements s/behaves like/is equivalent to/ s/but/except that/ (stronger, more complete statement)
Sign in to reply to this message.
*** Submitted as http://code.google.com/p/go/source/detail?r=c85198087f01 *** go spec: clarify semantics of range clause This CL proposes some subtle language changes in an attempt to clarify the semantics of range clauses and simplify uses of maps. - nil maps behave like empty maps; but attempting to set a value in a nil map causes a run-time panic - nil channels are never ready for communication; sending or reading from a nil channel blocks forever - if there is only one index iteration variable in a range clause and len(range expression) would be a constant, the range expression is not evaluated. (was discrepancy with len/cap before) - the notion of what is a constant expression len(x) for (pointer to) arrays x has been generalized and simplified (can still be syntactically decided) (before: more restrictive syntactic rule that was not consistently implemented) Fixes issue 1713. R=r, rsc, iant, ken2, r2, bradfitz, rog CC=golang-dev http://codereview.appspot.com/4444050
Sign in to reply to this message.
|