|
|
Created:
6 years, 5 months ago by gbracha Modified:
6 years, 5 months ago CC:
reviews_dartlang.org Visibility:
Public. |
DescriptionChange rules so prefixes obey lexical scope. Do this by basing member access on objects uniformly.
R=lrn@google.com
Committed: https://code.google.com/p/dart/source/detail?r=38518
Patch Set 1 #Patch Set 2 : #
Total comments: 26
Patch Set 3 : #Patch Set 4 : #
Total comments: 8
Patch Set 5 : #
Total comments: 5
Patch Set 6 : #Patch Set 7 : #Patch Set 8 : #Patch Set 9 : #
Total comments: 10
Patch Set 10 : #Messages
Total messages: 14 (0 generated)
I've shared the PDF with a more detailed explanation.
https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:547: We say that a function $f_1$ {\em forwards} to another function $f_2$ iff invoking $f_1$ causes $f_2$ to be executed with the same arguments and/or receiver as $f_1$, and returns the result of executing $f_2$ to the caller of $f_1$, unless $f_2$ throws an exception, in which case $f_1$ throws the same exception. The "iff" is too strong. This sounds like it's defining an (equivalence) relation on functions, and it would technically make `f1() { print("hello world"); return f2(); }` mean that f1 forwards to f2. How about: "When we say that a function $f_1$ {\em forwards} to another function $f_2$, we mean that ... ", and maybe say that we use this only to *introduce* a new synthetic $f_1$ function. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:894: A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. You say above that the forwarding has the same arguments and/or receiver, but here it seem like it doesn't have the same receiver. Will a static method be able to use "this" to refer to the Type object? I.e., does it actually have the same receiver (and therefore have a receiver), or should the text in the definition above not mention the receiver? https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:894: A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. This means that the class of the "Type object for class C" is not just Type. It must be some anonymous subclass. What does it's runtimeType getter return? What is the result of `reflect(C).type`? https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3299: \item $T$ is \code{Type}, $e$ is a constant type literal and the class corresponding to $e$ has a static getter named $m$. Is this just to get around the problem that the static type of a constant type literal is just plain Type, and not the subtype of Type with the instance members that forwards to the static members? https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3354: \item \code{im.namedArguments} evaluates to the value of \code{\CONST{} \{\}}. Why this change? https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3398: First, the expression $e$ is evaluated to an object $o$. Then, the getter function (\ref{getters}) $m$ is looked up (\ref{getterAndSetterLookup}) in $o$ with respect to the current library. If $o$ is an instance of \code{Type} but $e$ is not a constant type literal, then if $m$ is a getter that forwards (\ref{functionDeclarations}) to a static getter, method getter fails. Otherwise, the body of $m$ is executed with \THIS{} bound to $o$. The value of the getter invocation expression is the result returned by the call to the getter function. "method getter fails" -> "getter lookup fails"? https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:5120: Next, if $I$ includes a prefix clause of the form \AS{} $p$, let $NS = NS_n \cup \{p: prefixObject(NS_n)\}$ where $prefixObject(NS_n)$ is a {\em prefix object} for the namespace $NS_n$, which is an object that has the following members: If it is an object, can it be reflected on using `reflect(p)`? Does it extend Object (so it has runtimeType/toString/noSuchMethod/hashCode/operator==)?
I like the general idea, but it needs to specify - whether the prefix object extends Object and inherits, e.g., hashCode. Simply saying that it is "an object" carries a lot of baggage :) - what is the class of the Type object of a type literal and how does it interact with reflection. Are the forwarding methods available through reflection? Is the spec is really just adding extra members on an instance of the plain Type class (which is unprecedented). https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:894: A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. Never mind the "this" part - it's a compile time error to have "this" in a static method, so it doesn't matter if it has a receiver or not. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3255: If $n < h$, or $n > m$, the method lookup has failed. Furthermore, each $x_i, n+1 \le i \le n+k$, must have a corresponding named parameter in the set $\{p_{m+1}, \ldots, p_{h+l}\}$ or the method lookup also fails. If $v_o$ is an instance of \code{Type} but $o$ is not a constant type literal, then if $m$ is a method that forwards (\ref{functionDeclarations}) to a static method, method lookup fails. Otherwise method lookup has succeeded. Does "instance of \code{Type}" mean that it is an instance of that exact type, or can it be a subtype (which it probably will be, since we are adding different methods to different Type objects of type literals). This is where it's important that "forwards" isn't just a relation based on on the behavior, but only applies to the functions that we have explicitly introduced using "forwards" notation. A user may write a class that extends/implements Type and which has a method that delegates to a static method, like: class D extends Type { int p(x) => int.parse(x); } We don't want "new D().p(42)" to fail lookup (even if it's stupid). Alternatively, don't allow user code to implement Type. If the spec has parts that test whether something is an instance of Type, it easily gets fragile. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3354: \item \code{im.namedArguments} evaluates to the value of \code{\CONST{} \{\}}. That is: I don't particularly care, but it seems to reduce implementation choice, so I was wondering if that was the goal. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:4029: \item if $d$ is a prefix $p$, a compile-time error occurs unless $d$ is followed by a dot. "followed by a dot" is too vague, since it seems to allow "p..x" which still evaluates to the object for p, leaking it as an object. It should somehow say that this is at the lexical level, so maybe "unless the next {\em token} is `.'." https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:4030: \item If $d$ is a class or type alias $T$, the value of $e$ is an instance of class \code{Type} reifying $T$. It can't be an instance of Type, but must be an instance of a class implementing Type. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:5120: Next, if $I$ includes a prefix clause of the form \AS{} $p$, let $NS = NS_n \cup \{p: prefixObject(NS_n)\}$ where $prefixObject(NS_n)$ is a {\em prefix object} for the namespace $NS_n$, which is an object that has the following members: Ok, reflect(p) is disallowed because "p" isn't valid on its own. The Object inherited members are not prevented, since "p.toString()" does follow the "p" with a dot.
Great comments. I've fixed a few things, explained others, and need to think about a few more. I'll ping you again when I think it's ready for another look. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:547: We say that a function $f_1$ {\em forwards} to another function $f_2$ iff invoking $f_1$ causes $f_2$ to be executed with the same arguments and/or receiver as $f_1$, and returns the result of executing $f_2$ to the caller of $f_1$, unless $f_2$ throws an exception, in which case $f_1$ throws the same exception. On 2014/07/16 05:55:00, Lasse Reichstein Nielsen wrote: > The "iff" is too strong. This sounds like it's defining an (equivalence) > relation on functions, and it would technically make `f1() { print("hello > world"); return f2(); }` mean that f1 forwards to f2. > > How about: "When we say that a function $f_1$ {\em forwards} to another function > $f_2$, we mean that ... ", and maybe say that we use this only to *introduce* a > new synthetic $f_1$ function. Done. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:894: A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. On 2014/07/16 05:55:00, Lasse Reichstein Nielsen wrote: > You say above that the forwarding has the same arguments and/or receiver, but > here it seem like it doesn't have the same receiver. > > Will a static method be able to use "this" to refer to the Type object? I.e., > does it actually have the same receiver (and therefore have a receiver), or > should the text in the definition above not mention the receiver? So the goal of the phrase "and/or" was to account for the fact that sometimes there is no receiver. Sounds like this should be stated more explicitly. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:894: A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. On 2014/07/16 05:54:59, Lasse Reichstein Nielsen wrote: > This means that the class of the "Type object for class C" is not just Type. It > must be some anonymous subclass. Yes, in principle each type has its own class object (that is, each class has a specialized metaclass). However, in practice the restrictions on Type objects still exist. This is just a definitional conceit. The goal is that working implementations need not change (which effects the answers to the following questions). Of course, I'm hoping to get these restrictions removed eventually. What does it's runtimeType getter return? Type What > is the result of `reflect(C).type`? Type. This is irregular of course, and therefore I guess I agree with you that it needs to be stated explicitly. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:894: A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. On 2014/07/16 07:58:14, Lasse Reichstein Nielsen wrote: > Never mind the "this" part - it's a compile time error to have "this" in a > static method, so it doesn't matter if it has a receiver or not. Correct. The intent here is not to fix Dart static methods so that they are truly instance methods on the Type object for the class (i.e., the metaclass). That would be wonderful, but really all I am trying to do at this point is formulate the spec *as if* things worked this way, because that makes the correct scoping fall out with less special cases. However, since I'm not actually changing the semantics to really support first class libraries and types, there are a set of restrictions that need to be in place. The ban on this is one of them, which is already there. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3255: If $n < h$, or $n > m$, the method lookup has failed. Furthermore, each $x_i, n+1 \le i \le n+k$, must have a corresponding named parameter in the set $\{p_{m+1}, \ldots, p_{h+l}\}$ or the method lookup also fails. If $v_o$ is an instance of \code{Type} but $o$ is not a constant type literal, then if $m$ is a method that forwards (\ref{functionDeclarations}) to a static method, method lookup fails. Otherwise method lookup has succeeded. On 2014/07/16 07:58:14, Lasse Reichstein Nielsen wrote: > Does "instance of \code{Type}" mean that it is an instance of that exact type, > or can it be a subtype (which it probably will be, since we are adding different > methods to different Type objects of type literals). The latter. This will only matter if we actually removed the restrictions on Type objects. > > This is where it's important that "forwards" isn't just a relation based on on > the behavior, but only applies to the functions that we have explicitly > introduced using "forwards" notation. > A user may write a class that extends/implements Type and which has a method > that delegates to a static method, like: > class D extends Type { > int p(x) => int.parse(x); > } > We don't want "new D().p(42)" to fail lookup (even if it's stupid). > Yes. I will fix that. > Alternatively, don't allow user code to implement Type. If the spec has parts > that test whether something is an instance of Type, it easily gets fragile. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3299: \item $T$ is \code{Type}, $e$ is a constant type literal and the class corresponding to $e$ has a static getter named $m$. On 2014/07/16 05:55:00, Lasse Reichstein Nielsen wrote: > Is this just to get around the problem that the static type of a constant type > literal is just plain Type, and not the subtype of Type with the instance > members that forwards to the static members? Yes. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3354: \item \code{im.namedArguments} evaluates to the value of \code{\CONST{} \{\}}. On 2014/07/16 07:58:13, Lasse Reichstein Nielsen wrote: > That is: I don't particularly care, but it seems to reduce implementation > choice, so I was wondering if that was the goal. I realized we were using this formulation elsewhere in the spec, so it seemed more uniform to make these cases the same. It does appear to force you into using a shared constant object here, but that might not be observable (unless Object.noSuchMethod() tests for identity). https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3398: First, the expression $e$ is evaluated to an object $o$. Then, the getter function (\ref{getters}) $m$ is looked up (\ref{getterAndSetterLookup}) in $o$ with respect to the current library. If $o$ is an instance of \code{Type} but $e$ is not a constant type literal, then if $m$ is a getter that forwards (\ref{functionDeclarations}) to a static getter, method getter fails. Otherwise, the body of $m$ is executed with \THIS{} bound to $o$. The value of the getter invocation expression is the result returned by the call to the getter function. On 2014/07/16 05:55:00, Lasse Reichstein Nielsen wrote: > "method getter fails" -> "getter lookup fails"? Done. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:4029: \item if $d$ is a prefix $p$, a compile-time error occurs unless $d$ is followed by a dot. On 2014/07/16 07:58:14, Lasse Reichstein Nielsen wrote: > "followed by a dot" is too vague, since it seems to allow "p..x" which still > evaluates to the object for p, leaking it as an object. > > It should somehow say that this is at the lexical level, so maybe "unless the > next {\em token} is `.'." Done. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:4030: \item If $d$ is a class or type alias $T$, the value of $e$ is an instance of class \code{Type} reifying $T$. On 2014/07/16 07:58:13, Lasse Reichstein Nielsen wrote: > It can't be an instance of Type, but must be an instance of a class implementing > Type. Actually, we use instance of loosely here. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:5120: Next, if $I$ includes a prefix clause of the form \AS{} $p$, let $NS = NS_n \cup \{p: prefixObject(NS_n)\}$ where $prefixObject(NS_n)$ is a {\em prefix object} for the namespace $NS_n$, which is an object that has the following members: On 2014/07/16 07:58:14, Lasse Reichstein Nielsen wrote: > Ok, reflect(p) is disallowed because "p" isn't valid on its own. Yes. > The Object inherited members are not prevented, since "p.toString()" does follow > the "p" with a dot. That is true, and that was the intent. Except that I now realize that is a change from the current implementation. I would need to add even more ad hoc restrictions to prevent those being called on type literals or prefix objects.
PTAL. The reflection spec would have to also state that a few of these magical methods are not observable. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3255: If $n < h$, or $n > m$, the method lookup has failed. Furthermore, each $x_i, n+1 \le i \le n+k$, must have a corresponding named parameter in the set $\{p_{m+1}, \ldots, p_{h+l}\}$ or the method lookup also fails. If $v_o$ is an instance of \code{Type} but $o$ is not a constant type literal, then if $m$ is a method that forwards (\ref{functionDeclarations}) to a static method, method lookup fails. Otherwise method lookup has succeeded. On 2014/07/16 18:28:47, gbracha wrote: > On 2014/07/16 07:58:14, Lasse Reichstein Nielsen wrote: > > Does "instance of \code{Type}" mean that it is an instance of that exact type, > > or can it be a subtype (which it probably will be, since we are adding > different > > methods to different Type objects of type literals). > > The latter. This will only matter if we actually removed the restrictions on > Type objects. > > > > > This is where it's important that "forwards" isn't just a relation based on on > > the behavior, but only applies to the functions that we have explicitly > > introduced using "forwards" notation. > > A user may write a class that extends/implements Type and which has a method > > that delegates to a static method, like: > > class D extends Type { > > int p(x) => int.parse(x); > > } > > We don't want "new D().p(42)" to fail lookup (even if it's stupid). > > > > Yes. I will fix that. > > > Alternatively, don't allow user code to implement Type. If the spec has parts > > that test whether something is an instance of Type, it easily gets fragile. > Done. https://codereview.chromium.org/396733003/diff/20001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:5120: Next, if $I$ includes a prefix clause of the form \AS{} $p$, let $NS = NS_n \cup \{p: prefixObject(NS_n)\}$ where $prefixObject(NS_n)$ is a {\em prefix object} for the namespace $NS_n$, which is an object that has the following members: On 2014/07/16 18:28:47, gbracha wrote: > On 2014/07/16 07:58:14, Lasse Reichstein Nielsen wrote: > > Ok, reflect(p) is disallowed because "p" isn't valid on its own. > > Yes. > > > The Object inherited members are not prevented, since "p.toString()" does > follow > > the "p" with a dot. > > That is true, and that was the intent. Except that I now realize that is a > change from the current implementation. I would need to add even more ad hoc > restrictions to prevent those being called on type literals or prefix objects. > I've now added suitably gross rules banning the use of object members on these synthetic objects. Hopefully, the grossness of this will help persuade everyone that this needs to change.
Getting close (so I found an only-slightly-related thing to comment on too :) https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3304: It is a compile-time error to invoke any of the methods of class \cd{object} on a prefix object (\ref{imports}) or a constant type literal that is not immediately enclosed in parentheses. Works for prefix objects, but "enclosed in parentheses" seems a little too specific and syntactic for type literals, while still seemingly disallowing "int..toString()". That is , as usual, silly but allowed, and will call the toString of the Type object. Maybe it's "invoke" that doesn't sound specific enough. What we want to prohibit are the expressions: C.toString C.toString(...) C.toString= ... C.hashCode C.hashCode(...) C.hashCode= ... C.runtimeType C.runtimeType(...) C.runtimeType= ... C == ... where C is a type literal (right?). The == operator is even harder, because it's actually valid code to write "if (int == x) ..." and expect "int" to evaluate to a type object. You also can't have a static operator, so there is no doubt what it should mean, but I'm not sure we match it yet. That is "int == x" must be allowed and must work the same as "(int) == x". How about disallowing Object members if the constant type literal is followed by the token '.'. Or if method or getter lookup on an expression starting with "C.", where C is a type literal, resolves to an member inherited from Object, it's an error. Not sure where the best place to put that is, though. https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3387: If $C$ declares a concrete instance getter (respectively setter) named $m$ that is accessible to $L$, then that getter (respectively setter) is the result of the lookup. Otherwise, if $C$ has a superclass $S$, then the result of the lookup is the result of looking up getter (respectively setter) $m$ in $S$ with respect to $L$. Otherwise, we say that the lookup has failed. I could be wrong, but I don't think a method introduces an implicit getter. This explanation goes directly to the superclass if it doesn't find a getter/setter, but it should stop if it finds another declaration of the same name in the class (only methods and type parameters are possible, as far as I can see, and type parameters are not inherited). That is, maybe add a line before the "Otherwise" saying other something like "If $C$ declares any other concrete member named $m$ that is accessible to $L$, then we say that lookup has failed. Otherwise ..." https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3403: If the getter lookup has failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : At this point, shouldn't we try to see if the object has a method of the same name, and if so, extract that method? E.g. "class C {int foo()=>42;} main() { var p = new C().foo ; }". The getterAndSetterLookup only checks for getters, not methods, so the lookup of "foo" fails. At that point, we should recognize that C has a method with the same name, and create a closure for that method. I don't think this section does that. See also above.
PTYAL https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3304: It is a compile-time error to invoke any of the methods of class \cd{object} on a prefix object (\ref{imports}) or a constant type literal that is not immediately enclosed in parentheses. On 2014/07/17 13:42:45, Lasse Reichstein Nielsen wrote: > Works for prefix objects, but "enclosed in parentheses" seems a little too > specific and syntactic for type literals, while still seemingly disallowing > "int..toString()". That is , as usual, silly but allowed, and will call the > toString of the Type object. > Maybe it's "invoke" that doesn't sound specific enough. > > What we want to prohibit are the expressions: > C.toString C.toString(...) C.toString= ... > C.hashCode C.hashCode(...) C.hashCode= ... > C.runtimeType C.runtimeType(...) C.runtimeType= ... > C == ... > where C is a type literal (right?). > The == operator is even harder, because it's actually valid code to > write "if (int == x) ..." and expect "int" to evaluate to a type object. You > also can't have a static operator, so there is no doubt what it should mean, but > I'm not sure we match it yet. > That is "int == x" must be allowed and must work the same as "(int) == x". > > How about disallowing Object members if the constant type literal is followed by > the token '.'. > Or if method or getter lookup on an expression starting with "C.", where C is a > type literal, resolves to an member inherited from Object, it's an error. Not > sure where the best place to put that is, though. Fair enough. I'll reformulate as you suggest. https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3387: If $C$ declares a concrete instance getter (respectively setter) named $m$ that is accessible to $L$, then that getter (respectively setter) is the result of the lookup. Otherwise, if $C$ has a superclass $S$, then the result of the lookup is the result of looking up getter (respectively setter) $m$ in $S$ with respect to $L$. Otherwise, we say that the lookup has failed. On 2014/07/17 13:42:45, Lasse Reichstein Nielsen wrote: > I could be wrong, but I don't think a method introduces an implicit getter. This > explanation goes directly to the superclass if it doesn't find a getter/setter, > but it should stop if it finds another declaration of the same name in the class > (only methods and type parameters are possible, as far as I can see, and type > parameters are not inherited). > > That is, maybe add a line before the "Otherwise" saying other something like "If > $C$ declares any other concrete member named $m$ that is accessible to $L$, then > we say that lookup has failed. Otherwise ..." See reply to comment below. However, I don't think this text has the problem you allude to. Since overriding a getter with a method or vice versa is a compile time error, we are ok searching up the chain for methods before searching the whole chain for getters. If we find a method, we know there was no getter of that same name anywhere in the chain. https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3403: If the getter lookup has failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : On 2014/07/17 13:42:45, Lasse Reichstein Nielsen wrote: > At this point, shouldn't we try to see if the object has a method of the same > name, and if so, extract that method? > > E.g. "class C {int foo()=>42;} main() { var p = new C().foo > ; }". > The getterAndSetterLookup only checks for getters, not methods, so the lookup of > "foo" fails. At that point, we should recognize that C has a method with the > same name, and create a closure for that method. I don't think this section does > that. See also above. Both this and the comment above allude to another weakness in the spec that I would like to fix. The spec currently discusses closurization in a separate section, 15.14 (Property Extraction). Alas, this structure means that the spec is not easy to follow as the rules are scattered about. The spec says that if you see e.m(), you think of its as a function expression invocation (15.15.4) e'(), and then consider whether e' is a property extraction - i.e., whether it has the form e.m. Since it does, you treat the whole expression as an ordinary method invocation (15.16.1) where we look for a method up the chain and invoke it. If we don't find any we look for a getter up the chain, and feed it to Function.apply (should say we use call()) and if we don't find any, we call noSuchMethod. If you see e.m in isolation, it is a property extraction (15.14) , and there you check if a concrete method m exists. If there isn't one you treat it as a getter invocation (15.18). That is why it is safe for this section to not look up methods. Since this is not at all clear, I might want to restructure this. At root, the problem is that expressions of the form e.m() are not compositional, because of the distinction between getters and methods. It may be that it's best to integrate 15.14 and 15.18, because they deal with the same syntactic form, e.m. This is somewhat similar to what you suggest. As you say, all these are distinct (but somewhat related to) from the changes I've made so far, which are focused on treating types and prefixes as objects with restrictions. The question is whether I should deal with these in the same CL, or independently. I can see arguments either way. Treating it all in one go creates a new spec that one hopes is more readable. On the other hand, keeping changes independent makes each CL smaller and simpler. So I think I'll do it in stages, which means I leave 15.14 unchanged for now, and then extend this CL with fixes to address this issue.
lgtm https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3387: If $C$ declares a concrete instance getter (respectively setter) named $m$ that is accessible to $L$, then that getter (respectively setter) is the result of the lookup. Otherwise, if $C$ has a superclass $S$, then the result of the lookup is the result of looking up getter (respectively setter) $m$ in $S$ with respect to $L$. Otherwise, we say that the lookup has failed. Ack, good - but subtle - point. https://codereview.chromium.org/396733003/diff/60001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3403: If the getter lookup has failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : Sounds good. I often find it a little hard to figure out where in the spec something is, even when I know it's there :) https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3304: It is a compile-time error to invoke any of the methods of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. This looks correct (but it's hard to be absolutely sure). Since this is the section on "ordinary invocation", "invoke" should cover all the cases. If the getter section below prevents "int.hashCode" from being used as part of "int.hashCode()", then it should be safe. https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3452: It is a compile-time error to invoke any of the getters of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. I'm not sure this this covers method extraction. Doing "int.toString" does not invoke a getter, but it does access the Object-inherited method inappropriately. https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3537: It is a compile-time error to invoke any of the setters of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. Do we add implicit throwing setters when we add a getter? Otherwise Object won't have any setters (but I guess it's safer to keep this here).
https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3452: It is a compile-time error to invoke any of the getters of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. On 2014/07/18 13:49:08, Lasse Reichstein Nielsen wrote: > I'm not sure this this covers method extraction. Doing "int.toString" does not > invoke a getter, but it does access the Object-inherited method inappropriately. Ok, I think this is a problem. So I may go ahead and redo property extraction as we discussed in order to deal with this. https://codereview.chromium.org/396733003/diff/80001/docs/language/dartLangSp... docs/language/dartLangSpec.tex:3537: It is a compile-time error to invoke any of the setters of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. On 2014/07/18 13:49:08, Lasse Reichstein Nielsen wrote: > Do we add implicit throwing setters when we add a getter? Otherwise Object won't > have any setters (but I guess it's safer to keep this here). No, we don't. This is here just out of an abundance of paranoia.
So I finally decided that to make things consistent I would have to reorganize getter invocation and property extraction so that they follow the syntax structure. Hence another big set of changes - mostly reshuffling of existing text, but of course, it's the subtleties that matter. I'll send a link to the PDF in a moment.
I think it looks good now, but I must admit it's hard to be sure I haven't lost track of something. The only question I have is: class C { int call() => 42; } print(C()); Will this print 42 or fail? If it fails, then LGTM. https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3004: \item An uncaught exception is thrown. -> An exception is thrown which is not caught inside the method. Whether an exception "is caught" is a global property, and would be matched if it's caught by the calling code. (Let's ignore what "inside the method" means for recursive calls :) https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3005: \item A return statement (\ref{return}) immediately nested in the body of $f$ is executed. "immediately nested" sounds like it can't be inside, e.g., a loop. How about "A return statement (...) that is not inside a nested function, is execuited"? https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3048: Let $f$ be a function with $h$ required parameters, let $p_1 \ldots p_n$ be the positional parameters of $f$ and let $p_{h+1}, \ldots, p_{h+k}$ be the optional parameters declared by $f$. "required positional parameters" - the optional parameters may also be positional. https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3128: the static type of $i$ is the declared return type of $F$. You talk about the static type of $e_f$ here, not of $e_f.call$. That may not be a function type, even if $e_f$ has a call method. As I read it (<- disclaimer, I might not know what I'm talking about), an instance of the class: class D { int call(int x)=>x; } has static type D. That is assignable to a function type because D is more specific than int->int, but it is not itself a function type. That is, the interface of a class does not contain the function type, only Function, and it is just considered more specific than the corresponding function type (which is good, because that means function type is not inherited). Unless the commentary above is prescriptive, the signature of the call method isn't actually used here. https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3433: \item Iff \code{identical($o_1, o_2$)} then \cd{$o_1.m$ == $o_2.m$}. This now includes closurization of static members, because it's seen as a lookup on the the Type object. Static methods are compile-time constants, so the closurization should be identical for those, not just equal.
On 2014/07/23 12:45:33, Lasse Reichstein Nielsen wrote: > I think it looks good now, but I must admit it's hard to be sure I haven't lost > track of something. > > The only question I have is: > > class C { int call() => 42; } > print(C()); > > Will this print 42 or fail? If it fails, then LGTM. It fails. C() is an unqualified invocation, and the rules are very clear. > > https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... > File docs/language/dartLangSpec.tex (right): > > https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... > docs/language/dartLangSpec.tex:3004: \item An uncaught exception is thrown. > -> An exception is thrown which is not caught inside the method. > > Whether an exception "is caught" is a global property, and would be matched if > it's caught by the calling code. > (Let's ignore what "inside the method" means for recursive calls :) > > https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... > docs/language/dartLangSpec.tex:3005: \item A return statement (\ref{return}) > immediately nested in the body of $f$ is executed. > "immediately nested" sounds like it can't be inside, e.g., a loop. How about "A > return statement (...) that is not inside a nested function, is execuited"? > > https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... > docs/language/dartLangSpec.tex:3048: Let $f$ be a function with $h$ required > parameters, let $p_1 \ldots p_n$ be the positional parameters of $f$ and let > $p_{h+1}, \ldots, p_{h+k}$ be the optional parameters declared by $f$. > "required positional parameters" - the optional parameters may also be > positional. > > https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... > docs/language/dartLangSpec.tex:3128: the static type of $i$ is the declared > return type of $F$. > You talk about the static type of $e_f$ here, not of $e_f.call$. That may not be > a function type, even if $e_f$ has a call method. > > As I read it (<- disclaimer, I might not know what I'm talking about), an > instance of the class: > class D { int call(int x)=>x; } > has static type D. That is assignable to a function type because D is more > specific than int->int, but it is not itself a function type. That is, the > interface of a class does not contain the function type, only Function, and it > is just considered more specific than the corresponding function type (which is > good, because that means function type is not inherited). > > Unless the commentary above is prescriptive, the signature of the call method > isn't actually used here. > > https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... > docs/language/dartLangSpec.tex:3433: \item Iff \code{identical($o_1, o_2$)} > then \cd{$o_1.m$ == $o_2.m$}. > This now includes closurization of static members, because it's seen as a lookup > on the the Type object. > > Static methods are compile-time constants, so the closurization should be > identical for those, not just equal.
https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3004: \item An uncaught exception is thrown. On 2014/07/23 12:45:33, Lasse Reichstein Nielsen wrote: > -> An exception is thrown which is not caught inside the method. > > Whether an exception "is caught" is a global property, and would be matched if > it's caught by the calling code. > (Let's ignore what "inside the method" means for recursive calls :) Uncaught means "uncaught inside the function" but I can clarify. Fixed. https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3005: \item A return statement (\ref{return}) immediately nested in the body of $f$ is executed. On 2014/07/23 12:45:33, Lasse Reichstein Nielsen wrote: > "immediately nested" sounds like it can't be inside, e.g., a loop. Doesn't sound like that to me :-) I'm sure I use this terminology in multiple places. If I change it, I'll have to be consistent, so I'll defer this for now. How about "A > return statement (...) that is not inside a nested function, is execuited"? https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3048: Let $f$ be a function with $h$ required parameters, let $p_1 \ldots p_n$ be the positional parameters of $f$ and let $p_{h+1}, \ldots, p_{h+k}$ be the optional parameters declared by $f$. On 2014/07/23 12:45:33, Lasse Reichstein Nielsen wrote: > "required positional parameters" - the optional parameters may also be > positional. Indeed, and the indexing and text below all reflect that. There are h required parameters, so optionals start at h+1. There are n positional parameters and n may well be greater than h, in which case there are optional positional parameters. I think all is well. https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3128: the static type of $i$ is the declared return type of $F$. On 2014/07/23 12:45:33, Lasse Reichstein Nielsen wrote: > You talk about the static type of $e_f$ here, not of $e_f.call$. That may not be > a function type, even if $e_f$ has a call method. > > As I read it (<- disclaimer, I might not know what I'm talking about), an > instance of the class: > class D { int call(int x)=>x; } > has static type D. That is assignable to a function type because D is more > specific than int->int, but it is not itself a function type. That is, the > interface of a class does not contain the function type, only Function, and it > is just considered more specific than the corresponding function type (which is > good, because that means function type is not inherited). > > Unless the commentary above is prescriptive, the signature of the call method > isn't actually used here. Acknowledged. https://codereview.chromium.org/396733003/diff/160001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3433: \item Iff \code{identical($o_1, o_2$)} then \cd{$o_1.m$ == $o_2.m$}. On 2014/07/23 12:45:33, Lasse Reichstein Nielsen wrote: > This now includes closurization of static members, because it's seen as a lookup > on the the Type object. > Yes. This fixes a bug in the old spec. > Static methods are compile-time constants, so the closurization should be > identical for those, not just equal. Yes. We don't say that here, nor do we contradict it.
Message was sent while issue was closed.
Committed patchset #10 manually as r38518 (presubmit successful). |