Skip to content

Commit 008273b

Browse files
authored
Merge pull request #41441 from zoecarver/re-land-import-dependent-types-as-any
Re land import dependent types as any
2 parents 36a8058 + 5511805 commit 008273b

File tree

8 files changed

+321
-51
lines changed

8 files changed

+321
-51
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,8 @@ namespace {
881881
ImportResult Visit##KIND##Type(const clang::KIND##Type *type) { \
882882
if (type->isSugared()) \
883883
return Visit(type->desugar()); \
884+
if (type->isDependentType()) \
885+
return Impl.SwiftContext.TheAnyType; \
884886
return Type(); \
885887
}
886888
MAYBE_SUGAR_TYPE(TypeOfExpr)
@@ -1859,7 +1861,9 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
18591861
isa<clang::TemplateSpecializationType>(clangDecl->getReturnType())) ||
18601862
// TODO: we currently don't lazily load operator return types, but
18611863
// this should be trivial to add.
1862-
clangDecl->isOverloadedOperator()) {
1864+
clangDecl->isOverloadedOperator() ||
1865+
// Dependant types are trivially mapped as Any.
1866+
clangDecl->getReturnType()->isDependentType()) {
18631867
importedType =
18641868
importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt);
18651869
if (!importedType) {

lib/Sema/CSApply.cpp

Lines changed: 151 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Solution::computeSubstitutions(GenericSignature sig,
112112
// On Windows and 32-bit platforms we need to force "Int" to actually be
113113
// re-imported as "Int." This is needed because otherwise, we cannot round-trip
114114
// "Int" and "UInt". For example, on Windows, "Int" will be imported into C++ as
115-
// "long long" and then back into Swift as "Int64" not "Int."
115+
// "long long" and then back into Swift as "Int64" not "Int."
116116
static ValueDecl *rewriteIntegerTypes(SubstitutionMap subst, ValueDecl *oldDecl,
117117
AbstractFunctionDecl *newDecl) {
118118
auto originalFnSubst = cast<AbstractFunctionDecl>(oldDecl)
@@ -172,51 +172,116 @@ static ValueDecl *rewriteIntegerTypes(SubstitutionMap subst, ValueDecl *oldDecl,
172172
newFnDecl->setSelfAccessKind(func->getSelfAccessKind());
173173
newFnDecl->setSelfIndex(func->getSelfIndex());
174174
}
175+
175176
return newFnDecl;
176177
}
177178
}
178179

179180
return newDecl;
180181
}
181182

182-
// Derive a concrete function type for fdecl by substituting the generic args
183-
// and use that to derive the corresponding function type and parameter list.
184-
static std::pair<FunctionType *, ParameterList *>
185-
substituteFunctionTypeAndParamList(ASTContext &ctx, AbstractFunctionDecl *fdecl,
186-
SubstitutionMap subst) {
187-
FunctionType *newFnType = nullptr;
188-
// Create a new ParameterList with the substituted type.
189-
if (auto oldFnType = dyn_cast<GenericFunctionType>(
190-
fdecl->getInterfaceType().getPointer())) {
191-
newFnType = oldFnType->substGenericArgs(subst);
192-
} else {
193-
newFnType = cast<FunctionType>(fdecl->getInterfaceType().getPointer());
183+
// Synthesize a thunk body for the function created in
184+
// "addThunkForDependentTypes". This will just cast all params and forward them
185+
// along to the specialized function. It will also cast the result before
186+
// returning it.
187+
static std::pair<BraceStmt *, bool>
188+
synthesizeDependentTypeThunkParamForwarding(AbstractFunctionDecl *afd, void *context) {
189+
ASTContext &ctx = afd->getASTContext();
190+
191+
auto thunkDecl = cast<FuncDecl>(afd);
192+
auto specializedFuncDecl = static_cast<FuncDecl *>(context);
193+
194+
SmallVector<Argument, 8> forwardingParams;
195+
unsigned paramIndex = 0;
196+
for (auto param : *thunkDecl->getParameters()) {
197+
if (isa<MetatypeType>(param->getType().getPointer())) {
198+
paramIndex++;
199+
continue;
200+
}
201+
202+
auto paramRefExpr = new (ctx) DeclRefExpr(param, DeclNameLoc(),
203+
/*Implicit=*/true);
204+
paramRefExpr->setType(param->getType());
205+
206+
auto specParamTy = specializedFuncDecl->getParameters()->get(paramIndex)->getType();
207+
auto cast = ForcedCheckedCastExpr::createImplicit(
208+
ctx, paramRefExpr, specParamTy);
209+
210+
forwardingParams.push_back(Argument(SourceLoc(), Identifier(), cast));
211+
paramIndex++;
194212
}
195-
// The constructor type is a function type as follows:
196-
// (CType.Type) -> (Generic) -> CType
197-
// And a method's function type is as follows:
198-
// (inout CType) -> (Generic) -> Void
199-
// In either case, we only want the result of that function type because that
200-
// is the function type with the generic params that need to be substituted:
201-
// (Generic) -> CType
202-
if (isa<ConstructorDecl>(fdecl) || fdecl->isInstanceMember() ||
203-
fdecl->isStatic())
204-
newFnType = cast<FunctionType>(newFnType->getResult().getPointer());
205-
SmallVector<ParamDecl *, 4> newParams;
206-
unsigned i = 0;
207213

208-
for (auto paramTy : newFnType->getParams()) {
209-
auto *oldParamDecl = fdecl->getParameters()->get(i);
210-
auto *newParamDecl =
211-
ParamDecl::cloneWithoutType(fdecl->getASTContext(), oldParamDecl);
212-
newParamDecl->setInterfaceType(paramTy.getParameterType());
213-
newParams.push_back(newParamDecl);
214-
(void)++i;
214+
auto *specializedFuncDeclRef = new (ctx) DeclRefExpr(ConcreteDeclRef(specializedFuncDecl),
215+
DeclNameLoc(), true);
216+
specializedFuncDeclRef->setType(specializedFuncDecl->getInterfaceType());
217+
218+
auto argList = ArgumentList::createImplicit(ctx, forwardingParams);
219+
auto *specializedFuncCallExpr = CallExpr::createImplicit(ctx, specializedFuncDeclRef, argList);
220+
specializedFuncCallExpr->setType(specializedFuncDecl->getResultInterfaceType());
221+
specializedFuncCallExpr->setThrows(false);
222+
223+
auto cast = ForcedCheckedCastExpr::createImplicit(
224+
ctx, specializedFuncCallExpr, thunkDecl->getResultInterfaceType());
225+
226+
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), cast, /*implicit=*/true);
227+
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
228+
/*implicit=*/true);
229+
return {body, /*isTypeChecked=*/true};
230+
}
231+
232+
// Create a thunk to map functions with dependent types to their specialized
233+
// version. For example, create a thunk with type (Any) -> Any to wrap a
234+
// specialized function template with type (Dependent<T>) -> Dependent<T>.
235+
static ValueDecl *addThunkForDependentTypes(FuncDecl *oldDecl,
236+
FuncDecl *newDecl) {
237+
bool updatedAnyParams = false;
238+
239+
SmallVector<ParamDecl *, 4> fixedParameters;
240+
unsigned parameterIndex = 0;
241+
for (auto *newFnParam : *newDecl->getParameters()) {
242+
// If the un-specialized function had a parameter with type "Any" preserve
243+
// that parameter. Otherwise, use the new function parameter.
244+
auto oldParamType = oldDecl->getParameters()->get(parameterIndex)->getType();
245+
if (oldParamType->isEqual(newDecl->getASTContext().TheAnyType)) {
246+
updatedAnyParams = true;
247+
auto newParam =
248+
ParamDecl::cloneWithoutType(newDecl->getASTContext(), newFnParam);
249+
newParam->setInterfaceType(oldParamType);
250+
fixedParameters.push_back(newParam);
251+
} else {
252+
fixedParameters.push_back(newFnParam);
253+
}
254+
parameterIndex++;
215255
}
216-
auto *newParamList =
217-
ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc());
218256

219-
return {newFnType, newParamList};
257+
// If we don't need this thunk, bail out.
258+
if (!updatedAnyParams &&
259+
!oldDecl->getResultInterfaceType()->isEqual(
260+
oldDecl->getASTContext().TheAnyType))
261+
return newDecl;
262+
263+
auto fixedParams =
264+
ParameterList::create(newDecl->getASTContext(), fixedParameters);
265+
266+
Type fixedResultType;
267+
if (oldDecl->getResultInterfaceType()->isEqual(
268+
oldDecl->getASTContext().TheAnyType))
269+
fixedResultType = oldDecl->getASTContext().TheAnyType;
270+
else
271+
fixedResultType = newDecl->getResultInterfaceType();
272+
273+
// We have to rebuild the whole function.
274+
auto newFnDecl = FuncDecl::createImplicit(
275+
newDecl->getASTContext(), newDecl->getStaticSpelling(),
276+
newDecl->getName(), newDecl->getNameLoc(), newDecl->hasAsync(),
277+
newDecl->hasThrows(), /*genericParams=*/nullptr, fixedParams,
278+
fixedResultType, newDecl->getDeclContext());
279+
newFnDecl->copyFormalAccessFrom(newDecl);
280+
newFnDecl->setBodySynthesizer(synthesizeDependentTypeThunkParamForwarding, newDecl);
281+
newFnDecl->setSelfAccessKind(newDecl->getSelfAccessKind());
282+
newFnDecl->getAttrs().add(
283+
new (newDecl->getASTContext()) TransparentAttr(/*IsImplicit=*/true));
284+
return newFnDecl;
220285
}
221286

222287
// Synthesizes the body of a thunk that takes extra metatype arguments and
@@ -267,16 +332,55 @@ static ValueDecl *generateThunkForExtraMetatypes(SubstitutionMap subst,
267332
// specialization, which are no longer now that we've specialized
268333
// this function. Create a thunk that only forwards the original
269334
// parameters along to the clang function.
270-
auto thunkTypeAndParamList = substituteFunctionTypeAndParamList(oldDecl->getASTContext(),
271-
oldDecl, subst);
335+
SmallVector<ParamDecl *, 4> newParams;
336+
337+
for (auto param : *newDecl->getParameters()) {
338+
auto *newParamDecl = ParamDecl::clone(newDecl->getASTContext(), param);
339+
newParams.push_back(newParamDecl);
340+
}
341+
342+
auto originalFnSubst = cast<AbstractFunctionDecl>(oldDecl)
343+
->getInterfaceType()
344+
->getAs<GenericFunctionType>()
345+
->substGenericArgs(subst);
346+
// The constructor type is a function type as follows:
347+
// (CType.Type) -> (Generic) -> CType
348+
// And a method's function type is as follows:
349+
// (inout CType) -> (Generic) -> Void
350+
// In either case, we only want the result of that function type because that
351+
// is the function type with the generic params that need to be substituted:
352+
// (Generic) -> CType
353+
if (isa<ConstructorDecl>(oldDecl) || oldDecl->isInstanceMember() ||
354+
oldDecl->isStatic())
355+
originalFnSubst = cast<FunctionType>(originalFnSubst->getResult().getPointer());
356+
357+
for (auto paramTy : originalFnSubst->getParams()) {
358+
if (!paramTy.getPlainType()->is<MetatypeType>())
359+
continue;
360+
361+
auto dc = newDecl->getDeclContext();
362+
auto paramVarDecl =
363+
new (newDecl->getASTContext()) ParamDecl(
364+
SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
365+
newDecl->getASTContext().getIdentifier("_"), dc);
366+
paramVarDecl->setInterfaceType(paramTy.getPlainType());
367+
paramVarDecl->setSpecifier(ParamSpecifier::Default);
368+
newParams.push_back(paramVarDecl);
369+
}
370+
371+
auto *newParamList =
372+
ParameterList::create(newDecl->getASTContext(), SourceLoc(), newParams, SourceLoc());
373+
272374
auto thunk = FuncDecl::createImplicit(
273-
oldDecl->getASTContext(), oldDecl->getStaticSpelling(), oldDecl->getName(),
274-
oldDecl->getNameLoc(), oldDecl->hasAsync(), oldDecl->hasThrows(),
275-
/*genericParams=*/nullptr, thunkTypeAndParamList.second,
276-
thunkTypeAndParamList.first->getResult(), oldDecl->getDeclContext());
277-
thunk->copyFormalAccessFrom(oldDecl);
375+
newDecl->getASTContext(), newDecl->getStaticSpelling(), oldDecl->getName(),
376+
newDecl->getNameLoc(), newDecl->hasAsync(), newDecl->hasThrows(),
377+
/*genericParams=*/nullptr, newParamList,
378+
newDecl->getResultInterfaceType(), newDecl->getDeclContext());
379+
thunk->copyFormalAccessFrom(newDecl);
278380
thunk->setBodySynthesizer(synthesizeForwardingThunkBody, newDecl);
279-
thunk->setSelfAccessKind(oldDecl->getSelfAccessKind());
381+
thunk->setSelfAccessKind(newDecl->getSelfAccessKind());
382+
thunk->getAttrs().add(
383+
new (newDecl->getASTContext()) TransparentAttr(/*IsImplicit=*/true));
280384

281385
return thunk;
282386
}
@@ -326,6 +430,10 @@ static ConcreteDeclRef getCXXFunctionTemplateSpecialization(SubstitutionMap subs
326430
}
327431
}
328432

433+
if (auto fn = dyn_cast<FuncDecl>(decl)) {
434+
newDecl = addThunkForDependentTypes(fn, cast<FuncDecl>(newDecl));
435+
}
436+
329437
if (auto fn = dyn_cast<FuncDecl>(decl)) {
330438
if (newFn->getNumParams() != fn->getParameters()->size()) {
331439
newDecl = generateThunkForExtraMetatypes(subst, fn,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEPENDENT_TYPES_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEPENDENT_TYPES_H
3+
4+
template<class T>
5+
struct M {
6+
T value;
7+
8+
using U = T;
9+
10+
T getValue() const { return value; }
11+
};
12+
13+
template<class T, class U>
14+
M<U> differentDependentArgAndRet(M<T> a) { return {a.value}; }
15+
16+
template<class T>
17+
M<T> dependantReturnTypeInffered(T a) { return {a}; }
18+
19+
template<class T>
20+
M<T> dependantReturnTypeSameAsArg(M<T> a) { return {a.value}; }
21+
22+
// TODO: still not supported yet (rdar://89034704)
23+
template<class T, class U>
24+
typename M<U>::U complexDifferentDependentArgAndRet(typename M<T>::U a) { return a.value; }
25+
26+
template<class T>
27+
typename M<T>::U complexDependantReturnTypeInffered(T a) { return a; }
28+
29+
template<class T>
30+
typename M<T>::U complexDependantReturnTypeSameAsArg(typename M<T>::U a) { return a.value; }
31+
32+
template<class T>
33+
M<T> multipleArgs(M<T> a, T b, int c) { return {a.value + b}; }
34+
35+
template<class T, class U>
36+
M<T> multipleDependentArgsInferred(M<T> a, M<U> b, T c, U d) { return {a.value}; }
37+
38+
template<class T, class U>
39+
M<T> multipleDependentArgs(M<T> a, M<U> b) { return {a.value}; }
40+
41+
template<class T>
42+
M<T> refToDependent(const T &a) { return {a}; }
43+
44+
// TODO: We can't import this template rdar://89028943
45+
template<class T>
46+
T &dependentToRef(M<T> a) { return a.value; }
47+
48+
// TODO: We can't convert inout types in a thunk: rdar://89034440
49+
template<class T>
50+
M<T> dependentRef(M<T> &a) { return {a.value}; }
51+
52+
template<class T>
53+
M<T> dependentRefAndRefInferred(const M<T> &a, T &b) { return {a.value + b}; }
54+
55+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEPENDENT_TYPES_H

test/Interop/Cxx/templates/Inputs/module.modulemap

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,9 @@ module TemplateTypeParameterNotInSignature {
131131
module DefineReferencedInline {
132132
header "define-referenced-inline.h"
133133
requires cplusplus
134-
}
134+
}
135+
136+
module DependentTypes {
137+
header "dependent-types.h"
138+
requires cplusplus
139+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=DependentTypes -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
2+
3+
// CHECK: func differentDependentArgAndRet<T, U>(_ a: Any, T: T.Type, U: U.Type) -> Any
4+
// CHECK: func dependantReturnTypeInffered<T>(_ a: T) -> Any
5+
// CHECK: func dependantReturnTypeSameAsArg<T>(_ a: Any, T: T.Type) -> Any
6+
// CHECK: func multipleArgs<T>(_ a: Any, _ b: T, _ c: Int32) -> Any
7+
// CHECK: func multipleDependentArgsInferred<T, U>(_ a: Any, _ b: Any, _ c: T, _ d: U) -> Any
8+
// CHECK: func multipleDependentArgs<T, U>(_ a: Any, _ b: Any, T: T.Type, U: U.Type) -> Any
9+
// CHECK: func refToDependent<T>(_ a: inout T) -> Any
10+
// CHECK: func dependentRef<T>(_ a: inout Any, T: T.Type) -> Any
11+
// CHECK: func dependentRefAndRefInferred<T>(_ a: inout Any, _ b: inout T) -> Any
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-swift-emit-silgen %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import DependentTypes
4+
5+
// Check the test function:
6+
// CHECK-LABEL: sil [ossa] @$s4main4tests5Int64VyF : $@convention(thin) () -> Int64
7+
// CHECK: [[ANY_OUT:%.*]] = alloc_stack $Any
8+
9+
// CHECK: [[THUNK_REF:%.*]] = function_ref @$sSC27dependantReturnTypeInfferedyyps5Int64VF : $@convention(thin) (Int64) -> @out Any
10+
// CHECK: apply [[THUNK_REF]]([[ANY_OUT]], %{{[0-9]+}}) : $@convention(thin) (Int64) -> @out Any
11+
12+
// CHECK: [[SPEC_OUT:%.*]] = alloc_stack $__CxxTemplateInst1MIxE
13+
// CHECK: unconditional_checked_cast_addr Any in [[ANY_OUT]] : $*Any to __CxxTemplateInst1MIxE in [[SPEC_OUT]] : $*__CxxTemplateInst1MIxE
14+
// CHECK: [[SPEC_VAL:%.*]] = load [trivial] [[SPEC_OUT]] : $*__CxxTemplateInst1MIxE
15+
16+
// CHECK: [[SPEC_TEMP:%.*]] = alloc_stack $__CxxTemplateInst1MIxE
17+
// CHECK: store [[SPEC_VAL]] to [trivial] [[SPEC_TEMP]] : $*__CxxTemplateInst1MIxE
18+
19+
// CHECK: [[GET_VAL_FN:%.*]] = function_ref @{{_ZNK1MIxE8getValueEv|\?getValue@\?\$M@_J@@QEBA_JXZ}} : $@convention(cxx_method) (@in_guaranteed __CxxTemplateInst1MIxE) -> Int
20+
// CHECK: [[OUT_VAL:%.*]] = apply [[GET_VAL_FN]]([[SPEC_TEMP]]) : $@convention(cxx_method) (@in_guaranteed __CxxTemplateInst1MIxE) -> Int
21+
22+
// CHECK: return [[OUT_VAL]] : $Int
23+
// CHECK-LABEL: end sil function '$s4main4tests5Int64VyF'
24+
25+
26+
// Check the synthesized thunk:
27+
// CHECK-LABEL: sil [transparent] [serialized] [ossa] @$sSC27dependantReturnTypeInfferedyyps5Int64VF : $@convention(thin) (Int64) -> @out Any
28+
// CHECK: bb0(%0 : $*Any, %1 : $Int64):
29+
// CHECK: [[SPEC_OUT:%.*]] = alloc_stack $__CxxTemplateInst1MIxE
30+
// CHECK: [[TMP_INT:%.*]] = alloc_stack $Int64
31+
// CHECK: store %1 to [trivial] [[TMP_INT]] : $*Int64
32+
// CHECK: [[TMP_INT_CASTED:%.*]] = alloc_stack $Int64
33+
// CHECK: unconditional_checked_cast_addr Int64 in [[TMP_INT]] : $*Int64 to Int64 in [[TMP_INT_CASTED]] : $*Int64
34+
35+
// CHECK: [[ARG:%.*]] = load [trivial] [[TMP_INT_CASTED]] : $*Int64
36+
// CHECK: [[FN:%.*]] = function_ref @{{_Z27dependantReturnTypeInfferedIxE1MIT_ES1_|\?\?\$dependantReturnTypeInffered@_J@@YA\?AU\?\$M@_J@@_J@Z}} : $@convention(c) (Int64) -> __CxxTemplateInst1MIxE
37+
// CHECK: [[OUT:%.*]] = apply [[FN]]([[ARG]]) : $@convention(c) (Int64) -> __CxxTemplateInst1MIxE
38+
39+
// CHECK: store [[OUT]] to [trivial] [[SPEC_OUT]] : $*__CxxTemplateInst1MIxE
40+
// CHECK: unconditional_checked_cast_addr __CxxTemplateInst1MIxE in [[SPEC_OUT]] : $*__CxxTemplateInst1MIxE to Any in %0 : $*Any
41+
// CHECK-LABEL: end sil function '$sSC27dependantReturnTypeInfferedyyps5Int64VF'
42+
43+
public func test() -> Int64 {
44+
let m = dependantReturnTypeInffered(Int64(42)) as! M<Int64>
45+
return m.getValue()
46+
}
47+
48+
// CHECK-LABEL: sil [clang __CxxTemplateInst1MIxE.getValue] @{{_ZNK1MIxE8getValueEv|\?getValue@\?\$M@_J@@QEBA_JXZ}} : $@convention(cxx_method) (@in_guaranteed __CxxTemplateInst1MIxE) -> Int64
49+
// CHECK-LABEL: sil [serializable] [clang dependantReturnTypeInffered] @{{_Z27dependantReturnTypeInfferedIxE1MIT_ES1_|\?\?\$dependantReturnTypeInffered@_J@@YA\?AU\?\$M@_J@@_J@Z}} : $@convention(c) (Int64) -> __CxxTemplateInst1MIxE

0 commit comments

Comments
 (0)