Skip to content

Commit ceff414

Browse files
committed
Distinguish invocation and pattern substitutions on SILFunctionType.
In order to allow this, I've had to rework the syntax of substituted function types; what was previously spelled `<T> in () -> T for <X>` is now spelled `@substituted <T> () -> T for <X>`. I think this is a nice improvement for readability, but it did require me to churn a lot of test cases. Distinguishing the substitutions has two chief advantages over the existing representation. First, the semantics seem quite a bit clearer at use points; the `implicit` bit was very subtle and not always obvious how to use. More importantly, it allows the expression of generic function types that must satisfy a particular generic abstraction pattern, which was otherwise impossible to express. As an example of the latter, consider the following protocol conformance: ``` protocol P { func foo() } struct A<T> : P { func foo() {} } ``` The lowered signature of `P.foo` is `<Self: P> (@in_guaranteed Self) -> ()`. Without this change, the lowered signature of `A.foo`'s witness would be `<T> (@in_guaranteed A<T>) -> ()`, which does not preserve information about the conformance substitution in any useful way. With this change, the lowered signature of this witness could be `<T> @Substituted <Self: P> (@in_guaranteed Self) -> () for <A<T>>`, which nicely preserves the exact substitutions which relate the witness to the requirement. When we adopt this, it will both obviate the need for the special witness-table conformance field in SILFunctionType and make it far simpler for the SILOptimizer to devirtualize witness methods. This patch does not actually take that step, however; it merely makes it possible to do so. As another piece of unfinished business, while `SILFunctionType::substGenericArgs()` conceptually ought to simply set the given substitutions as the invocation substitutions, that would disturb a number of places that expect that method to produce an unsubstituted type. This patch only set invocation arguments when the generic type is a substituted type, which we currently never produce in type-lowering. My plan is to start by producing substituted function types for accessors. Accessors are an important case because the coroutine continuation function is essentially an implicit component of the function type which the current substitution rules simply erase the intended abstraction of. They're also used in narrower ways that should exercise less of the optimizer.
1 parent adbf8da commit ceff414

File tree

96 files changed

+1256
-624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+1256
-624
lines changed

docs/ABI/Mangling.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,10 +583,12 @@ mangled in to disambiguate.
583583
::
584584

585585
impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_'
586-
impl-function-type ::= type* generic-signature 'I' PSEUDO-GENERIC? FUNC-ATTRIBUTES '_'
586+
impl-function-type ::= type* generic-signature 'I' FUNC-ATTRIBUTES '_'
587587

588-
FUNC-ATTRIBUTES ::= CALLEE-ESCAPE? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? PARAM-CONVENTION* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION)?
588+
FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? PARAM-CONVENTION* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION)?
589589

590+
PATTERN-SUBS ::= 's' // has pattern substitutions
591+
INVOCATION-SUB ::= 'I' // has invocation substitutions
590592
PSEUDO-GENERIC ::= 'P'
591593

592594
CALLEE-ESCAPE ::= 'e' // @escaping (inverse of SIL @noescape)

docs/SIL.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,46 @@ autorelease in the callee.
592592
importer only imports non-native methods and types as ``throws``
593593
when it is possible to do this automatically.
594594

595+
- SIL function types may provide a pattern signature and substitutions
596+
to express that values of the type use a particular generic abstraction
597+
pattern. Both must be provided together. If a pattern signature is
598+
present, the component types (parameters, yields, and results) must be
599+
expressed in terms of the generic parameters of that signature.
600+
The pattern substitutions should be expressed in terms of the generic
601+
parameters of the overall generic signature, if any, or else
602+
the enclosing generic context, if any.
603+
604+
A pattern signature follows the ``@substituted`` attribute, which
605+
must be the final attribute preceding the function type. Pattern
606+
substitutions follow the function type, preceded by the ``for``
607+
keyword. For example::
608+
609+
@substituted <T: Collection> (@in T) -> @out T.Element for Array<Int>
610+
611+
The low-level representation of a value of this type may not match
612+
the representation of a value of the substituted-through version of it::
613+
614+
(@in Array<Int>) -> @out Int
615+
616+
Substitution differences at the outermost level of a function value
617+
may be adjusted using the ``convert_function`` instruction. Note that
618+
this only works at the outermost level and not in nested positions.
619+
For example, a function which takes a parameter of the first type above
620+
cannot be converted by ``convert_function`` to a function which takes
621+
a parameter of the second type; such a conversion must be done with a
622+
thunk.
623+
624+
Type substitution on a function type with a pattern signature and
625+
substitutions only substitutes into the substitutions; the component
626+
types are preserved with their exact original structure.
627+
628+
- In the implementation, a SIL function type may also carry substitutions
629+
for its generic signature. This is a convenience for working with
630+
applied generic types and is not generally a formal part of the SIL
631+
language; in particular, values should not have such types. Such a
632+
type behaves like a non-generic type, as if the substitutions were
633+
actually applied to the underlying function type.
634+
595635
Coroutine Types
596636
```````````````
597637

include/swift/AST/DiagnosticsParse.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,14 @@ ERROR(sil_function_subst_expected_l_angle,none,
828828
"expected '<' to begin SIL function type substitution list after 'for'", ())
829829
ERROR(sil_function_subst_expected_r_angle,none,
830830
"expected '>' to end SIL function type substitution list after 'for <...'", ())
831+
ERROR(sil_function_subst_expected_generics,none,
832+
"expected '<' to begin substituted parameter list after '@substituted'", ())
833+
ERROR(sil_function_subst_expected_function,none,
834+
"expected function type after '@substituted'", ())
835+
ERROR(sil_function_subst_expected_subs,none,
836+
"expected 'for' to begin substitutions after '@substituted' function type", ())
837+
ERROR(sil_function_subs_without_generics,none,
838+
"unexpected 'for' to begin substitutions after non-generic function type", ())
831839

832840
// Opaque types
833841
ERROR(opaque_mid_composition,none,

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4019,6 +4019,8 @@ ERROR(sil_non_coro_yields,PointsToFirstBadToken,
40194019
ERROR(sil_function_repeat_convention,PointsToFirstBadToken,
40204020
"repeated %select{parameter|result|callee}0 convention attribute",
40214021
(unsigned))
4022+
ERROR(ast_subst_function_type,none,
4023+
"substitutions cannot be provided on a formal function type", ())
40224024
ERROR(sil_function_multiple_error_results,PointsToFirstBadToken,
40234025
"SIL function types cannot have multiple @error results", ())
40244026
ERROR(unsupported_sil_convention,none,

include/swift/AST/TypeRepr.h

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,15 @@ inline IdentTypeRepr::ComponentRange IdentTypeRepr::getComponentRange() {
472472
/// (x: Foo, y: Bar) -> Baz
473473
/// \endcode
474474
class FunctionTypeRepr : public TypeRepr {
475-
// These fields are only used in SIL mode, which is the only time
476-
// we can have polymorphic and substituted function values.
475+
// The generic params / environment / substitutions fields are only used
476+
// in SIL mode, which is the only time we can have polymorphic and
477+
// substituted function values.
477478
GenericParamList *GenericParams;
478479
GenericEnvironment *GenericEnv;
479-
bool GenericParamsAreImplied;
480-
ArrayRef<TypeRepr *> GenericSubs;
480+
ArrayRef<TypeRepr *> InvocationSubs;
481+
GenericParamList *PatternGenericParams;
482+
GenericEnvironment *PatternGenericEnv;
483+
ArrayRef<TypeRepr *> PatternSubs;
481484

482485
TupleTypeRepr *ArgsTy;
483486
TypeRepr *RetTy;
@@ -487,21 +490,37 @@ class FunctionTypeRepr : public TypeRepr {
487490
public:
488491
FunctionTypeRepr(GenericParamList *genericParams, TupleTypeRepr *argsTy,
489492
SourceLoc throwsLoc, SourceLoc arrowLoc, TypeRepr *retTy,
490-
bool GenericParamsAreImplied = false,
491-
ArrayRef<TypeRepr *> GenericSubs = {})
493+
GenericParamList *patternGenericParams = nullptr,
494+
ArrayRef<TypeRepr *> patternSubs = {},
495+
ArrayRef<TypeRepr *> invocationSubs = {})
492496
: TypeRepr(TypeReprKind::Function),
493-
GenericParams(genericParams),
494-
GenericEnv(nullptr),
495-
GenericParamsAreImplied(GenericParamsAreImplied),
496-
GenericSubs(GenericSubs),
497+
GenericParams(genericParams), GenericEnv(nullptr),
498+
InvocationSubs(invocationSubs),
499+
PatternGenericParams(patternGenericParams), PatternGenericEnv(nullptr),
500+
PatternSubs(patternSubs),
497501
ArgsTy(argsTy), RetTy(retTy),
498502
ArrowLoc(arrowLoc), ThrowsLoc(throwsLoc) {
499503
}
500504

501505
GenericParamList *getGenericParams() const { return GenericParams; }
502506
GenericEnvironment *getGenericEnvironment() const { return GenericEnv; }
503-
bool areGenericParamsImplied() const { return GenericParamsAreImplied; }
504-
ArrayRef<TypeRepr*> getSubstitutions() const { return GenericSubs; }
507+
508+
GenericParamList *getPatternGenericParams() const {
509+
return PatternGenericParams;
510+
}
511+
GenericEnvironment *getPatternGenericEnvironment() const {
512+
return PatternGenericEnv;
513+
}
514+
515+
ArrayRef<TypeRepr*> getPatternSubstitutions() const { return PatternSubs; }
516+
ArrayRef<TypeRepr*> getInvocationSubstitutions() const {
517+
return InvocationSubs;
518+
}
519+
520+
void setPatternGenericEnvironment(GenericEnvironment *genericEnv) {
521+
assert(PatternGenericEnv == nullptr);
522+
PatternGenericEnv = genericEnv;
523+
}
505524

506525
void setGenericEnvironment(GenericEnvironment *genericEnv) {
507526
assert(GenericEnv == nullptr);

0 commit comments

Comments
 (0)