-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#2976. Add semantics tests #2998
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, with a couple of comments!
The feature specification talks about "like an accessible type alias" which is a somewhat Byzantine way to say that we can ignore type arguments as needed (namely, when we're invoking a static member of the context type).
This means that we basically can't test whether it is actually "like an accessible type alias", because there's no way we could do anything other than ignoring any actual type arguments in cases like .myStaticMethod()
with context type C<String>
.
However, it gets worse: Constructor invocations will ignore the actual type arguments in the context type, and use normal inference to find them. Often, this means to "re-find" the actual type arguments because we will frequently infer exactly the same type arguments that we just erased. However, it actually matters in some cases, especially with non-trivial selector chains.
So I'm suggesting that the @description
is adjusted to say that these tests are just testing that when we're computing a static namespace from a given context type we will also expand all type aliases.
/// | ||
/// Because of that, the specification of the static and runtime semantics of | ||
/// the new constructs needs to address all the forms `.id`, `.id<typeArgs>`, | ||
/// `.id(args)`, `.id<typeArgs>(args)`, `.new` or `.new(args)`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The feature spec should be generalized to cover all selector chains (e.g., .id.foo(42).bar[true]!
), and I'm actually surprised that it hasn't happened yet.
I guess we'll just keep an eye on the language PRs and update these tests when the feature spec is updated.
At that time I think we'd add some extra cases (because the currently specified forms are just special cases of the more general selector*
form).
OK, so it's fine to just rely on the currently specified cases.
/// | ||
/// @description Checks that expressions of the form `.id`, `.id(args)` and | ||
/// `.id<typeArgs>(args)` are treated as if they are prefixed by a fresh | ||
/// identifier `X` which denotes an accessible type alias. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really hard to test! ;-)
The point could be that a type alias can be non-generic and still denote a parameterized type (typedef X = SomeType<With, Args>;
), and this allows for the constructor invocations to have the actual type arguments, and it also allows for a static method invocation to ignore the same type arguments (because that's how we treat type aliases when used to access static members).
Apart from this, there would be no distinction between this description and a shorter one that simply says "the semantics of .id
is T.id
, where T
is the greatest closure of the context type.
However, that works for invocations of static methods, but it is not the actual semantics of constructor invocations: For them we strip off the actual type arguments (so the greatest closure operation is ignored ... for both static member invocations and constructor invocations!), and then type inference is performed as if no actual type arguments were passed.
This means that it isn't hugely meaningful to test "as if prefixed by a type alias" at all.
What we need to test is that it works to call a static method even in the case where the context type has actual type arguments (where those actual type arguments must be ignored).
Next, we should test that it works to invoke a constructor in a case where the context type has actual type arguments (e.g., List<num>
as context type should make .filled(10, 10)
mean List<num>.filled(10, 10)
, and the type argument should not be int
, as it would be if the context type were _
).
Finally, it should work to invoke a constructor with a context type schema (say, List<Iterable<_>>
), and we should then see that the actual arguments to the constructor invocation plays a role:
Object foo<X>(Map<int, X> map) => map;
var map = foo(.fromIterables([1], ['Hello']));
In this case we'd get int
from the context type and String
from the actual argument ['Hello']
, and the result would then be a Map<int, String>
.
Expect.equals(1, et3.t); | ||
|
||
ETInt et4 = .id2<int>(4); | ||
Expect.equals(4, et4.t); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test is fine, but it doesn't test the location in the feature specification which is mentioned in the @assertion
, it just tests that the processing of the context type includes a type alias expansion, as it should.
So the @description
should be adjusted to match this situation.
The remaining tests are fine in the same sense as well.
main() { | ||
Object o = C(); | ||
if (o is C) { | ||
o = .id<int>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
No description provided.