1111//===----------------------------------------------------------------------===//
1212
1313import SIL
14+ import AST
1415
15- extension ApplyInst : OnoneSimplifiable {
16+ extension ApplyInst : OnoneSimplifiable , SILCombineSimplifiable {
1617 func simplify( _ context: SimplifyContext ) {
1718 if tryTransformThickToThinCallee ( of: self , context) {
1819 return
@@ -21,13 +22,23 @@ extension ApplyInst : OnoneSimplifiable {
2122 context. erase ( instruction: self )
2223 return
2324 }
24- _ = context. tryDevirtualize ( apply: self , isMandatory: false )
25+ if context. tryDevirtualize ( apply: self , isMandatory: false ) != nil {
26+ return
27+ }
28+ if !context. preserveDebugInfo {
29+ _ = tryReplaceExistentialArchetype ( of: self , context)
30+ }
2531 }
2632}
2733
28- extension TryApplyInst : OnoneSimplifiable {
34+ extension TryApplyInst : OnoneSimplifiable , SILCombineSimplifiable {
2935 func simplify( _ context: SimplifyContext ) {
30- _ = context. tryDevirtualize ( apply: self , isMandatory: false )
36+ if context. tryDevirtualize ( apply: self , isMandatory: false ) != nil {
37+ return
38+ }
39+ if !context. preserveDebugInfo {
40+ _ = tryReplaceExistentialArchetype ( of: self , context)
41+ }
3142 }
3243}
3344
@@ -61,3 +72,131 @@ private func tryTransformThickToThinCallee(of apply: ApplyInst, _ context: Simpl
6172 }
6273 return false
6374}
75+
76+ /// If the apply uses an existential archetype (`@opened("...")`) and the concrete type is known,
77+ /// replace the existential archetype with the concrete type
78+ /// 1. in the apply's substitution map
79+ /// 2. in the arguments, e.g. by inserting address casts
80+ /// For example:
81+ /// ```
82+ /// %5 = apply %1<@opend("...")>(%2) : <τ_0_0> (τ_0_0) -> ()
83+ /// ```
84+ /// ->
85+ /// ```
86+ /// %4 = unchecked_addr_cast %2 to $*ConcreteType
87+ /// %5 = apply %1<ConcreteType>(%4) : <τ_0_0> (τ_0_0) -> ()
88+ /// ```
89+ private func tryReplaceExistentialArchetype( of apply: ApplyInst , _ context: SimplifyContext ) -> Bool {
90+ if let concreteType = apply. concreteTypeOfDependentExistentialArchetype,
91+ apply. canReplaceExistentialArchetype ( )
92+ {
93+ let builder = Builder ( after: apply, context)
94+
95+ let newApply = builder. createApply (
96+ function: apply. callee,
97+ apply. replaceOpenedArchetypeInSubstituations ( withConcreteType: concreteType, context) ,
98+ arguments: apply. replaceExistentialArchetypeInArguments ( withConcreteType: concreteType, context) ,
99+ isNonThrowing: apply. isNonThrowing, isNonAsync: apply. isNonAsync,
100+ specializationInfo: apply. specializationInfo)
101+ apply. replace ( with: newApply, context)
102+
103+ return true
104+ }
105+ return false
106+ }
107+
108+ // The same as the previous function, just for try_apply instructions.
109+ private func tryReplaceExistentialArchetype( of tryApply: TryApplyInst , _ context: SimplifyContext ) -> Bool {
110+ if let concreteType = tryApply. concreteTypeOfDependentExistentialArchetype,
111+ tryApply. canReplaceExistentialArchetype ( )
112+ {
113+ let builder = Builder ( before: tryApply, context)
114+
115+ builder. createTryApply (
116+ function: tryApply. callee,
117+ tryApply. replaceOpenedArchetypeInSubstituations ( withConcreteType: concreteType, context) ,
118+ arguments: tryApply. replaceExistentialArchetypeInArguments ( withConcreteType: concreteType, context) ,
119+ normalBlock: tryApply. normalBlock, errorBlock: tryApply. errorBlock,
120+ isNonAsync: tryApply. isNonAsync,
121+ specializationInfo: tryApply. specializationInfo)
122+ context. erase ( instruction: tryApply)
123+
124+ return true
125+ }
126+ return false
127+ }
128+
129+ private extension FullApplySite {
130+ // Precondition: the apply uses only a single existential archetype.
131+ // This is checked in `concreteTypeOfDependentExistentialArchetype`
132+ func canReplaceExistentialArchetype( ) -> Bool {
133+ // Make sure that existential archetype _is_ a replacement type and not e.g. _contained_ in a
134+ // replacement type, like
135+ // apply %1<Array<@opened("...")>()
136+ guard substitutionMap. replacementTypes. contains ( where: { $0. isExistentialArchetype } ) ,
137+ substitutionMap. replacementTypes. allSatisfy ( { $0. isExistentialArchetype || !$0. hasLocalArchetype } )
138+ else {
139+ return false
140+ }
141+
142+ // Don't allow existential archetypes in direct results and error results.
143+ // Note that an opened existential value is address only, so it cannot be a direct result anyway
144+ // (but it can be once we have opaque values).
145+ // Also don't support things like direct `Array<@opened("...")>` return values.
146+ if let singleDirectResult, singleDirectResult. type. astType. hasLocalArchetype {
147+ return false
148+ }
149+ if let singleDirectErrorResult, singleDirectErrorResult. type. astType. hasLocalArchetype {
150+ return false
151+ }
152+
153+ return arguments. allSatisfy { value in
154+ let astTy = value. type. astType
155+ // Allow three cases:
156+ // case 1. the argument _is_ the existential archetype
157+ return astTy. isExistentialArchetype ||
158+ // case 2. the argument _is_ a metatype of the existential archetype
159+ ( astTy. isMetatypeType && astTy. instanceTypeOfMetatype. isExistentialArchetype) ||
160+ // case 3. the argument has nothing to do with the existential archetype (or any other local archetype)
161+ !astTy. hasLocalArchetype
162+ }
163+ }
164+
165+ func replaceExistentialArchetypeInArguments(
166+ withConcreteType concreteType: CanonicalType ,
167+ _ context: SimplifyContext
168+ ) -> [ Value ] {
169+ let newArgs = arguments. map { ( arg) -> Value in
170+ let argTy = arg. type. astType
171+ if argTy. isExistentialArchetype {
172+ // case 1. the argument _is_ the existential archetype:
173+ // just insert an address cast to satisfy type equivalence.
174+ let builder = Builder ( before: self , context)
175+ let concreteSILType = concreteType. loweredType ( in: self . parentFunction)
176+ return builder. createUncheckedAddrCast ( from: arg, to: concreteSILType. addressType)
177+ }
178+ if argTy. isMetatypeType, argTy. instanceTypeOfMetatype. isExistentialArchetype {
179+ // case 2. the argument _is_ a metatype of the existential archetype:
180+ // re-create the metatype with the concrete type.
181+ let builder = Builder ( before: self , context)
182+ return builder. createMetatype ( ofInstanceType: concreteType, representation: argTy. representationOfMetatype)
183+ }
184+ // case 3. the argument has nothing to do with the existential archetype (or any other local archetype)
185+ return arg
186+ }
187+ return Array ( newArgs)
188+ }
189+
190+ func replaceOpenedArchetypeInSubstituations(
191+ withConcreteType concreteType: CanonicalType ,
192+ _ context: SimplifyContext
193+ ) -> SubstitutionMap {
194+ let openedArcheType = substitutionMap. replacementTypes. first ( where: { $0. isExistentialArchetype } ) !
195+
196+ let newReplacementTypes = substitutionMap. replacementTypes. map {
197+ return $0 == openedArcheType ? concreteType. type : $0
198+ }
199+ let genSig = callee. type. astType. invocationGenericSignatureOfFunctionType
200+ return SubstitutionMap ( genericSignature: genSig, replacementTypes: newReplacementTypes)
201+ }
202+ }
0 commit comments