SILType substitutions are still done with the old form, and until
BoundGenericTypes hold conformances, we still have to pass around
a ModuleDecl in a few places we really shouldn't, but one step
at a time.
This patch is rather large, since it was hard to make this change
incrementally, but most of the changes are mechanical.
Now that we have a lighter-weight data structure in the AST for mapping
interface types to archetypes and vice versa, use that in SIL instead of
a GenericParamList.
This means that when serializing a SILFunction body, we no longer need to
serialize references to archetypes from other modules.
Several methods used for forming substitutions can now be moved from
GenericParamList to GenericEnvironment.
Also, GenericParamList::cloneWithOuterParameters() and
GenericParamList::getEmpty() can now go away, since they were only used
when SILGen-ing witness thunks.
Finally, when printing generic parameters with identical names, the
SIL printer used to number them from highest depth to lowest, by
walking generic parameter lists starting with the innermost one.
Now, ambiguous generic parameters are numbered from lowest depth
to highest, by walking the generic signature, which means test
output in one of the SILGen tests has changed.
One minor revision: this lifts the proposed restriction against
overriding a non-open method with an open one. On reflection,
that was inconsistent with the existing rule permitting non-public
methods to be overridden with public ones. The restriction on
subclassing a non-open class with an open class remains, and is
in fact consistent with the existing access rule.
What I've implemented here deviates from the current proposal text
in the following ways:
- I had to introduce a FunctionArrowPrecedence to capture the parsing
of -> in expression contexts.
- I found it convenient to continue to model the assignment property
explicitly.
- The comparison and casting operators have historically been
non-associative; I have chosen to preserve that, since I don't
think this proposal intended to change it.
- This uses the precedence group names and higherThan/lowerThan
as agreed in discussion.
There are many places where we do the 'if inside a protocol, get the
Self type parameter, otherwise, use the declared type' dance.
We actually have really handy utility methods that encapsulate this,
so let's use them more.
This made call sites confusing to read because it doesn't actually
check if the function already exists.
Also fix some minor formatting issues. This came up while I was working
on a fix for a bug that turned out to not be a bug.
Also, mark witness thunks for [fragile] witnesses as [fragile].
This allows us to serialize the witness table, as well as any thunks
for witnesses declared @_transparent and @inline(__always).
This is only used in the verifier, to ensure that default witness
thunks are suffiently visible.
Also this patch removes the asserts enforcing that only resilient
protocols have a default witness table. This will change in an
upcoming patch, and in this patch is necessary for the test to work.
Don't hardcode linkage of default witness thunks, addressing a FIXME.
This will allow us to emit default witness thunks for requirements of
internal protocols, too.
Don't hardcode linkage of default implementations of materializeForSet
to public, addressing a FIXME.
This will allow us to emit default witness thunks for requirements of
internal protocols, too.
Previously we would emit two types of MaterializeForSet implementations
in SILGen:
- materializeForSet for a concrete storage declaration
- materializeForSet witness thunk in a conformance
This refactoring decouples the code from taking a conformance, which is
needed for two new types of materializeForSet that we need:
- materializeForSet witness thunk in a default witness table -- this is
necessary in order to be able to resiliently add storage requirements
with default implementations to protocols
- materializeForSet vtable thunk -- this is necessary to fix a missing
re-abstraction case with overriding storage in a subclass
This patch brings us closer to implementing these two. For default
implementations, we still have an issue in that the materializeForSet
has a different "generic signature abstraction pattern" in concrete
and default witnesses, so default and concrete witnesses for
materializeForSet are currently ABI-incompatible because the type
metadata for the storage is passed differently to the callback.
This patch wires up SILGenDefaultWitnessTable to actually emit
thunks and add them to the SILDefaultWitnessTable, using the
new logic in Sema for inferring default implementations.
Note that default witness thunks are mangled like the protocol
requirement itself.
After emitting thunks, SILGen populates a SILDefaultWitnessTable
for consumption by IRGen.
Default witness thunks for properties and subscripts are not
supported yet; a bit more refactoring in MaterializeForSet
emission is necessary.
In this case we do not have a conformance, and the default witness
thunk uses the same signature and context archetypes as the protocol
requirement.
There might still be an abstraction change between the requirement
and witness, though.
Tests are in the next patch that actually adds the ability to emit
these thunks. This is just a refactoring.
This ireapplies commit 255c52de9f.
Original commit message:
Serialize debug scope and location info in the SIL assembler language.
At the moment it is only possible to test the effects that SIL
optimization passes have on debug information by observing the
effects of a full .swift -> LLVM IR compilation. This change enable us
to write targeted testcases for single SIL optimization passes.
The new syntax is as follows:
sil-scope-ref ::= 'scope' [0-9]+
sil-scope ::= 'sil_scope' [0-9]+ '{'
sil-loc
'parent' scope-parent
('inlined_at' sil-scope-ref )?
'}'
scope-parent ::= sil-function-name ':' sil-type
scope-parent ::= sil-scope-ref
sil-loc ::= 'loc' string-literal ':' [0-9]+ ':' [0-9]+
Each instruction may have a debug location and a SIL scope reference
at the end. Debug locations consist of a filename, a line number, and
a column number. If the debug location is omitted, it defaults to the
location in the SIL source file. SIL scopes describe the position
inside the lexical scope structure that the Swift expression a SIL
instruction was generated from had originally. SIL scopes also hold
inlining information.
<rdar://problem/22706994>
At the moment it is only possible to test the effects that SIL
optimization passes have on debug information by observing the
effects of a full .swift -> LLVM IR compilation. This change enable us
to write targeted testcases for single SIL optimization passes.
The new syntax is as follows:
sil-scope-ref ::= 'scope' [0-9]+
sil-scope ::= 'sil_scope' [0-9]+ '{'
sil-loc
'parent' scope-parent
('inlined_at' sil-scope-ref )?
'}'
scope-parent ::= sil-function-name ':' sil-type
scope-parent ::= sil-scope-ref
sil-loc ::= 'loc' string-literal ':' [0-9]+ ':' [0-9]+
Each instruction may have a debug location and a SIL scope reference
at the end. Debug locations consist of a filename, a line number, and
a column number. If the debug location is omitted, it defaults to the
location in the SIL source file. SIL scopes describe the position
inside the lexical scope structure that the Swift expression a SIL
instruction was generated from had originally. SIL scopes also hold
inlining information.
<rdar://problem/22706994>
Fix some interface type/context type confusion in the AST synthesis from the previous patch, add a unique private mangling for behavior protocol conformances, and set up SILGen to emit the conformances when property declarations with behaviors are visited. Disable synthesis of the struct memberwise initializer if any instance properties use behaviors; codegen will need to be redesigned here.
Similarly to how we've always handled parameter types, we
now recursively expand tuples in result types and separately
determine a result convention for each result.
The most important code-generation change here is that
indirect results are now returned separately from each
other and from any direct results. It is generally far
better, when receiving an indirect result, to receive it
as an independent result; the caller is much more likely
to be able to directly receive the result in the address
they want to initialize, rather than having to receive it
in temporary memory and then copy parts of it into the
target.
The most important conceptual change here that clients and
producers of SIL must be aware of is the new distinction
between a SILFunctionType's *parameters* and its *argument
list*. The former is just the formal parameters, derived
purely from the parameter types of the original function;
indirect results are no longer in this list. The latter
includes the indirect result arguments; as always, all
the indirect results strictly precede the parameters.
Apply instructions and entry block arguments follow the
argument list, not the parameter list.
A relatively minor change is that there can now be multiple
direct results, each with its own result convention.
This is a minor change because I've chosen to leave
return instructions as taking a single operand and
apply instructions as producing a single result; when
the type describes multiple results, they are implicitly
bound up in a tuple. It might make sense to split these
up and allow e.g. return instructions to take a list
of operands; however, it's not clear what to do on the
caller side, and this would be a major change that can
be separated out from this already over-large patch.
Unsurprisingly, the most invasive changes here are in
SILGen; this requires substantial reworking of both call
emission and reabstraction. It also proved important
to switch several SILGen operations over to work with
RValue instead of ManagedValue, since otherwise they
would be forced to spuriously "implode" buffers.
This will be used to help IRGen record protocol requirements
with resilient default implementations in protocol metadata.
To enable testing before all the Sema support is in place, this
patch adds SIL parser, printer and verifier support for default
witness tables.
For now, SILGen emits empty default witness tables for protocol
declarations in resilient modules, and IRGen ignores them when
emitting protocol metadata.
Previously we treated the * platform as checking for the minimum
deployment target, but that's definitely unnecessary.
There is a bit of a hack here to avoid diagnosing the 'else' branch as
unreachable: if a constant true/false came from #available, ignore it.
This is where the AllowLoweredTypes flag to Type::subst() is important,
since this function is used on both canonical AST types, and lowered
AST types.
When compiling a module with multiple FileUnits (eg, while running
swiftc without -whole-module-optimization), we emit all decls
in the ExternalDeclarations list for every FileUnit, leading to
duplicate symbols if any of those decls were public.
Joe's workaround skipped decls on ExternalDefinitions that were
already emitted in the current FileUnit, but that was not sufficient;
the right fix is to not put stuff on ExternalDeclarations at all,
if we're going to emit it as part of a decl inside our file.
In this case, the synthesized _code accessors for an ErrorType
conformance do not belong in ExternalDefinitions, because they're
part of an ExtensionDecl that is in the current FileUnit.
Finally add some tests for this stuff, because it appears our
existing coverage was insufficient.
If you extend a C/ObjC enum type to conform to ErrorType, then the synthesized _code getter ends up registered as an external decl, but appears to already get synthesized due to @slavapestov's changes. I'm not sure whether this is reliable enough that we can simply stop registering the external decls, so just to keep the build from crashing, insert a check in emitExternalDecls that we didn't already emit the decl and skip emission. Low-risk workaround for rdar://problem/24287125.
And use project_box to get to the address value.
SILGen now generates a project_box for each alloc_box.
And IRGen re-uses the address value from the alloc_box if the operand of project_box is an alloc_box.
This lets the generated code be the same as before.
Other than that most changes of this (quite large) commit are straightforward.
Now that all the pieces are in place, we can finally start seeing
some benefits. In particular, the code for witness thunk emission
is much simpler now.
We weren't adding them as external decls unless they were for
storage on an imported type, which meant SILGen wasn't emitting
them if the conforming type was from a different Swift source
file, or in whole-module mode, a different module. This led
to linker errors.
Instead, always add accessors to the external decl list, but
skip them in SILGen if they are contained in the DeclContext
we are currently emitting (which is a source file or module).
Note that they are still emitted with the wrong linkage, from a
resilience perspective. Clients must only ever see public
exports for getters, setters and materializeForSet emitted
because they are required by resilience or the access pattern;
'accidental' accessors synthesized for protocol conformance
should not be public.
This is the first in a series of patches that fixes some resilience-related
issues with synthesized accessors and materializeForSet.
Previously we maintained two lists of external declarations encountered while
type checking:
- ASTContext::ExternalDefinitions
- TypeChecker::implicitlyDefinedFunctions
The former contained the following:
- Imported nominal types from Clang, so that SILGen can emit witness tables
- Functions and variables with Clang decls, so that IRGen can instruct Clang
to emit them
- Synthesized accessors
The latter contained synthesized functions for derived conformances.
Since the second list was not visible outside Sema, we relied on the Clang
importer to add the type that contained the declaration to the
ExternalDefinitions list. In practice, we only synthesized members of enums
in this manner.
Because of this, SILGenModule::emitExternalDefinitions() had special logic to
skip members of enums, since it would visit them when visiting the enum itself.
Instead, it appears that we can remove implicitlyDefinedFunctions completely,
changing usage sites to add the decl to ExternalDefinitions instead, and
simplify SILGenModule::emitExternalDefinition() a bit in the process.
Also, it looks like we never had Modules appear in ExternalDefinitions, so
assert if those come up instead of skipping them.
It is still not clear to me when we access global variables from other
modules directly, versus using accessors; it seems to be controlled
by the -sil-serialize-all flag, rather than any language feature.
Until/if we add a @_fixed_layout equivalent for globals, I can't really
test direct access of globals from other modules; when we figure out
the story here I'll be able to add more tests and also tighten up
some isResilient() checks in the global code, but what's in there now
seems to work.
The main idea here is that we really, really want to be
able to recover the protocol requirement of a conformance
reference even if it's abstract due to the conforming type
being abstract (e.g. an archetype). I've made the conversion
from ProtocolConformance* explicit to discourage casual
contamination of the Ref with a null value.
As part of this change, always make conformance arrays in
Substitutions fully parallel to the requirements, as opposed
to occasionally being empty when the conformances are abstract.
As another part of this, I've tried to proactively fix
prospective bugs with partially-concrete conformances, which I
believe can happen with concretely-bound archetypes.
In addition to just giving us stronger invariants, this is
progress towards the removal of the archetype from Substitution.