Skip to content

Commit 7190ac4

Browse files
committed
[NFC] Add a detailed explanatory comment
1 parent 1f1aa3e commit 7190ac4

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

lib/SILGen/SILGenPoly.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,123 @@ class TranslateIndirect : public Cleanup {
891891
}
892892
};
893893

894+
/// Given a list of inputs that are suited to the parameters of one
895+
/// function, translate them into a list of outputs that are suited
896+
/// for the parameters of another function, given that the two
897+
/// functions differ only by abstraction differences and/or a small
898+
/// a small set of subtyping-esque conversions tolerated by the
899+
/// type checker.
900+
///
901+
/// This is a conceptually rich transformation, and there are several
902+
/// different concepts of expansion and transformation going on at
903+
/// once here. We will briefly review these concepts in order to
904+
/// explain what has to happen here.
905+
///
906+
/// Swift functions have *formal* parameters. These are represented here
907+
/// as the list of input and and output `CanParam` structures. The
908+
/// type-checker requires function types to be formally related to each
909+
/// in specific ways in order for a conversion to be accepted; this
910+
/// includes the parameter lists being the same length (other than
911+
/// the exception of SE-0110's tuple-splat behavior).
912+
///
913+
/// SIL functions have *lowered* parameters. These correspond to the
914+
/// SIL values we receive in Inputs and must generate in Outputs.
915+
///
916+
/// A single formal parameter can correspond to any number of
917+
/// lowered parameters because SIL function type lowering recursively
918+
/// expands tuples that aren't being passed inout.
919+
///
920+
/// The lowering of generic Swift function types must be independent
921+
/// of the actual types substituted for generic parameters, which means
922+
/// that decisions about when to expand must be made according to the
923+
/// orig (unsubstituted) function type, not the substituted types.
924+
/// But the type checker only cares that function types are related
925+
/// as substituted types, so we may need to combine or expand tuples
926+
/// because of the differences between abstraction.
927+
///
928+
/// This translation therefore recursively walks the orig parameter
929+
/// types of the input and output function type and expects the
930+
/// corresponding lowered parameter lists to match with the structure
931+
/// we see there. When the walk reaches a non-tuple orig type on the
932+
/// input side, we know that the input function receives a lowered
933+
/// parameter and therefore there is a corresponding value in Inputs.
934+
/// When the walk reaches a non-tuple orig type on the output side,
935+
/// we know that the output function receives a lowered parameter
936+
/// and therefore there is a corresponding type in OutputTypes (and
937+
/// a need to produce an argument value in Outputs).
938+
///
939+
/// Variadic generics complicate this because both tuple element
940+
/// lists and function formal parameter lists can contain pack
941+
/// expansions. Again, the relevant question is where expansions
942+
/// appear in the orig type; the substituted parameters / tuple
943+
/// elements are still required to be related by the type-checker.
944+
/// Like any other non-tuple pattern, an expansion always corresponds
945+
/// to a single lowered parameter, which may then include multiple
946+
/// formal parameters or tuple elements from the substituted type's
947+
/// perspective. The orig type should carry precise substitutions
948+
/// that will tell us how many components the expansion expands to,
949+
/// which we must use to inform our recursive walk. These components
950+
/// may or may not still contain pack expansions in the substituted
951+
/// types; if they do, they will have the same shape.
952+
///
953+
/// An example might help. Suppose that we have a generic function
954+
/// that returns a function value:
955+
///
956+
/// func produceFunction<T_0, each T_1>() ->
957+
/// (Optional<T_0>, (B, C), repeat Array<each T_1>) -> ()
958+
///
959+
/// And suppose we call this with generic arguments <A, Pack{D, E}>.
960+
/// Then formally we now have a function of type:
961+
///
962+
/// (Optional<A>, (B, C), Array<D>, Array<E>) -> ()
963+
///
964+
/// These are the orig and subst formal parameter sequences. The
965+
/// lowered parameter sequence of this type (assuming all the concrete
966+
/// types A,B,... are non-trivial but loadable) is:
967+
///
968+
/// (@in_guaranteed Optional<A>,
969+
/// @guaranteed B,
970+
/// @guaranteed C,
971+
/// @pack_guaranteed Pack{Array<D>, Array<E>})
972+
///
973+
/// Just for edification, if we had written this function type in a
974+
/// non-generic context, it would have this lowered parameter sequence:
975+
///
976+
/// (@guaranteed Optional<A>,
977+
/// @guaranteed B,
978+
/// @guaranteed C,
979+
/// @guaranteed Array<D>,
980+
/// @guaranteed Array<E>)
981+
///
982+
/// Now suppose we also have a function that takes a function value:
983+
///
984+
/// func consumeFunction<T_out_0, each T_out_1>(
985+
/// _ function: (A, T_out_0, repeat each T_out_1, Array<E>) -> ()
986+
/// )
987+
///
988+
/// If we call this with the generic arguments <(B, C), Pack{Array<D>}>,
989+
/// then it will expect a value of type:
990+
///
991+
/// (A, (B, C), Array<D>, Array<E>) -> ()
992+
///
993+
/// This is a supertype of the function type above (because of the
994+
/// contravariance of function parameters), and so the type-checker will
995+
/// permit the result of one call to be passed to the other. The
996+
/// lowered parameter sequence of this type will be:
997+
///
998+
/// (@guaranteed A,
999+
/// @in_guaranteed (B, C),
1000+
/// @in_guaranteed Array<D>,
1001+
/// @guaranteeed Array<E>)
1002+
///
1003+
/// We will then end up in this code with the second type as the input
1004+
/// type and the first type as the output type. (The second type is
1005+
/// the input because of contravariance again: we do this conversion
1006+
/// by capturing a value of the first function type in a closure which
1007+
/// presents the interface of the second function type. In this code,
1008+
/// we are emitting the body of that closure, and therefore the inputs
1009+
/// we receive are the parameters of that closure, matching the lowered
1010+
/// signature of the second function type.)
8941011
class TranslateArguments {
8951012
SILGenFunction &SGF;
8961013
SILLocation Loc;

0 commit comments

Comments
 (0)