Hello everyone, I'm Yakup Florek, a second year Bachelor of Computer Science Engineering
student at UDEL, and last summer I was fortunate to participate in Google Summer of
Code developing new Swift on qualified look-up implementation of my mentor, that Gregor.
I'd like to tell you today a bit about the library of the developed and about the
process and my experiences in newcomer to the community.
First, I'd like to address the elephant in the room, and what I'm qualified actually
is.
And for that, I would like to refer to our common understanding of how name-sworking
programming languages.
So, in Swift, it's for example possible to refer to a variable introduced inside
of the guard statement, or the variable declaration, could possibly shadow its predecessors,
such as a member here.
Speaking of members, we can access members that could possibly be introduced in a separate
source-fix-separate module.
Sometimes we even use variables that don't get any concrete syntax we could assign it
to such a self-key word here, which is implicitly introduced on the function boundary.
We understand those relations, but so does the compiler, and it does it through the unqualified
look-up.
And that's exactly what Swift-lexic look-up, the library of the developed, actually is.
But in contrast to the compiler implementation, it doesn't build an auxiliary data structure,
it's lightweight and stateless.
And a syntax node can be used as an entry point for the query.
And it then traverses the syntax tree from the starting point to the source, to the root
of the source, to the top of the file, and collects all of the names, and partitions them
according on the scope of introduction.
Then returns this nice in-and-base data structure, which makes it really easy to model some
corner cases, such as implicit names we have just seen.
So let's discuss now a bit more concrete example of how such query could behave like.
So let's perform our look-up here, and let's see what names and what scopes does the query
traverse through.
So first, called look, here is variable declaration.
Then we have a guard statement with two names in it, and we are back to original code
look, the function body with one more variable declaration, and then another guard statement.
Now on the function boundary, we need to introduce the implicit cell, and now the interesting
bit, because how can we represent the members that could possibly be introduced in a separate
source file?
Well, we can't reason about them with unqualified look-up, and we have more powerful
member look-up to do that, but we can prompt clients to perform such look-up with what
we call in-smooth-explicable look-up a result, which says clients to basically just go there,
and try to find any names that could possibly be introduced there.
It's almost the entire picture of how and call for it to look-up behaves in this case.
There is one bit missing here, but we'll come back into in just a second.
So having built an intuition of how the query should behave like, how do we ensure the
quality of our implementation?
So for that we've devised two kinds of tests.
First, we have the unit tests, which are really cheap and easy to write.
Standard XC test, which are inside of the switch syntax package, and they use our custom
developed test harness that takes a code snippet, annotated with markers, and runs a lot
of assertions for them.
Let's try to make a sample test case for our earlier example.
So first, we can simplify our code a bit.
Now we can put it as a string to our test harness, and annotated with markers.
Yes, there's very modgies.
We're testing important pieces of the compiler of the modgies, right?
And it's because now we can define our first entry point using one of those markers
and start defining our assertions.
This data structure that we used to do those assertions,
closely follows what's with lexical cap producers and makes it really easy to write to a test.
Now, let's do the also need to tell the test harness to not perform name matching,
and that's done with a simple parameter.
It just says to not filter out the name space on the identifier we started to look up with.
So now we have a way to validate our implementation and go against our meant for model.
But so how do we check if that's what the compiler and language expects?
For that we have the second kind of the stintic ratio test, which run inside of the compiler,
whenever a special experimental flag is set, it runs and records all of the names produced
by the compiler implementation, and runs the equivalent kind of look-on with Swiftlexic
look-up site, and tries to match the results.
Whenever something goes wrong, we get those nice tables which show us exactly where our discrepancies,
and those gray markers, we also account for some funky compiler behavior,
because as it turns out, it's also not perfect.
Now, my favorite part of the presentation, and I'd like to show you some interesting cases
with encountered along the way when working on the library.
So let's go back to the function body, callbook, or how we more generally call it in Swiftlexic
look-up, as sequential scope.
Particularly, listen to the second guard statement, because guard statements are weird.
So you see, in the second condition, we have reference to the data variable,
but it doesn't refer to the variable declaration maybe for the guard statement.
First to the first condition, which has a variable declaration in it,
which then refers to the variable declaration maybe for the guard statement.
Sure, thanks, Sands.
But what happens if we try to perform the same kind of look-up from inside of its body?
Well, then we no longer refer to the first condition.
We immediately refer to the variable declaration, made before the guard statement.
And that's where guard statement are so weird and different from other scope,
and require additional filtering logic during look-up and short-stripe behavior.
There is also this one bit that I left out earlier, and it's because of what's going on here.
Particularly, normally, no-minotype declarations made inside of the codebook are invisible
across its entire scope, right?
So with our entire intuition of just going towards the root of the tree,
we need to revisit it for something like this, where we suddenly also need to check
if there are any names.
After that, or maybe there is also a notion of almost visible names that's required by
it.
Sequential scopes are easily the most complicated scope we model with, for example, look-up.
Lastly, I also wanted to give you an example of a compiler mark.
Namely, we're a compiler first introduced generic parameters, and then function parameters
in macro declarations.
Let's rather unexpected, and that's exactly the other way around how it works in, for example,
function declarations.
And I think this example beautifully explains why it's so hard to work with, those
template annotations, because it's about constantly making those judgments, whether it's
a bug on SwiftExical look-up site, or maybe something we just want to model differently
there, or maybe a relatively harmless compiler bug that can be easily counted for by clients,
or maybe something that could potentially lead to weird and unexpected behavior on compiler
site, such as in this case.
Lastly, I also wanted to give you some examples of how to contribute to this, but when
working on the presentation, I realized I'd rather tell you how did I do it, and give you
some concrete examples.
And I really wanted this whole presentation to be about that, because we first tried to map
our understanding of how names work in programming languages to what and qualified look-up
could behave like.
That's also when I started building my first demo, and writing my G Suite proposal.
Then, after I started working with my inventor, look, we started implementing our first
initial implementation, play this sort of test-driven development using our test harness,
and subsequently, once we were able to finally model a great part of the language, we started
testing it against the compiler, and that's really when the complexity just went through
the roof, because I had to dive into a huge heterogeneous codebase, main Swift compiler codebase.
Once we were able to run validation for an entire compilation process of sort of standard
library recently, we started stabilizing the API, and I also published an RFC last week to hopefully
make it public to enable clients to start using the library, and with the added benefit that
once the new implementation makes its way to the compiler, and becomes the canonical implementation,
the results will be guaranteed to be correct.
So, with that, I hope you enjoyed the presentation, if you learned something new,
and you now consider maybe contributing to Swift.
Thank you.