|
13 | 13 | #include "ClangDerivedConformances.h" |
14 | 14 | #include "ImporterImpl.h" |
15 | 15 | #include "swift/AST/ConformanceLookup.h" |
| 16 | +#include "swift/AST/LayoutConstraint.h" |
16 | 17 | #include "swift/AST/ParameterList.h" |
17 | 18 | #include "swift/AST/PrettyStackTrace.h" |
18 | 19 | #include "swift/AST/ProtocolConformance.h" |
19 | 20 | #include "swift/Basic/Assertions.h" |
20 | 21 | #include "swift/ClangImporter/ClangImporterRequests.h" |
| 22 | +#include "clang/AST/ASTContext.h" |
21 | 23 | #include "clang/AST/CXXInheritance.h" |
| 24 | +#include "clang/AST/Decl.h" |
| 25 | +#include "clang/AST/DeclCXX.h" |
| 26 | +#include "clang/AST/Type.h" |
22 | 27 | #include "clang/Sema/DelayedDiagnostic.h" |
23 | 28 | #include "clang/Sema/Lookup.h" |
24 | 29 | #include "clang/Sema/Overload.h" |
@@ -62,23 +67,22 @@ static CxxStdType identifyCxxStdTypeByName(StringRef name) { |
62 | 67 |
|
63 | 68 | static const clang::TypeDecl * |
64 | 69 | lookupCxxTypeMember(clang::Sema &Sema, const clang::CXXRecordDecl *Rec, |
65 | | - StringRef name) { |
| 70 | + StringRef name, bool mustBeComplete = false) { |
66 | 71 | auto R = clang::LookupResult(Sema, &Sema.PP.getIdentifierTable().get(name), |
67 | 72 | clang::SourceLocation(), |
68 | 73 | clang::Sema::LookupMemberName); |
69 | 74 | R.suppressDiagnostics(); |
70 | | - |
71 | 75 | auto *Ctx = static_cast<const clang::DeclContext *>(Rec); |
72 | 76 | Sema.LookupQualifiedName(R, const_cast<clang::DeclContext *>(Ctx)); |
73 | 77 |
|
74 | | - if (R.isSingleResult()) { |
| 78 | + if (auto *td = R.getAsSingle<clang::TypeDecl>()) { |
75 | 79 | if (auto *paths = R.getBasePaths(); |
76 | | - paths && R.getBasePaths()->front().Access != clang::AS_public) |
| 80 | + paths && paths->front().Access != clang::AS_public) |
77 | 81 | return nullptr; |
78 | | - |
79 | | - for (auto *nd : R) |
80 | | - if (auto *td = dyn_cast<clang::TypeDecl>(nd)) |
81 | | - return td; |
| 82 | + if (mustBeComplete && |
| 83 | + !Sema.isCompleteType({}, td->getASTContext().getTypeDeclType(td))) |
| 84 | + return nullptr; |
| 85 | + return td; |
82 | 86 | } |
83 | 87 | return nullptr; |
84 | 88 | } |
@@ -993,59 +997,127 @@ static void conformToCxxSet(ClangImporter::Implementation &impl, |
993 | 997 | bool isUniqueSet) { |
994 | 998 | PrettyStackTraceDecl trace("conforming to CxxSet", decl); |
995 | 999 | ASTContext &ctx = decl->getASTContext(); |
| 1000 | + auto &clangCtx = impl.getClangASTContext(); |
| 1001 | + auto &clangSema = impl.getClangSema(); |
996 | 1002 |
|
997 | | - auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>( |
998 | | - decl, ctx.getIdentifier("value_type")); |
999 | | - auto sizeType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>( |
1000 | | - decl, ctx.getIdentifier("size_type")); |
1001 | | - if (!valueType || !sizeType) |
1002 | | - return; |
1003 | | - |
1004 | | - auto insert = getInsertFunc(decl, valueType); |
1005 | | - if (!insert) |
| 1003 | + // Look up the type members we need from Clang |
| 1004 | + // |
| 1005 | + // N.B. we don't actually need const_iterator for multiset, but it should be |
| 1006 | + // there. If it's not there for any reason, we should probably bail out. |
| 1007 | + |
| 1008 | + auto *size_type = lookupCxxTypeMember(clangSema, clangDecl, "size_type", |
| 1009 | + /*mustBeComplete=*/true); |
| 1010 | + auto *value_type = lookupCxxTypeMember(clangSema, clangDecl, "value_type", |
| 1011 | + /*mustBeComplete=*/true); |
| 1012 | + auto *iterator = lookupCxxTypeMember(clangSema, clangDecl, "iterator", |
| 1013 | + /*mustBeComplete=*/true); |
| 1014 | + auto *const_iterator = |
| 1015 | + lookupCxxTypeMember(clangSema, clangDecl, "const_iterator", |
| 1016 | + /*mustBeComplete=*/true); |
| 1017 | + if (!size_type || !value_type || !iterator || !const_iterator) |
1006 | 1018 | return; |
1007 | 1019 |
|
1008 | | - impl.addSynthesizedTypealias(decl, ctx.Id_Element, |
1009 | | - valueType->getUnderlyingType()); |
1010 | | - impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement, |
1011 | | - valueType->getUnderlyingType()); |
1012 | | - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), |
1013 | | - sizeType->getUnderlyingType()); |
1014 | | - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), |
1015 | | - insert->getResultInterfaceType()); |
1016 | | - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet}); |
| 1020 | + const clang::CXXMethodDecl *insert = nullptr; |
| 1021 | + { |
| 1022 | + // CxxSet requires the InsertionResult associated type, which is the return |
| 1023 | + // type of std::set (and co.)'s insert function. But there is no equivalent |
| 1024 | + // typedef in C++ we can use directly, so we need get it by converting the |
| 1025 | + // return type of the insert function. |
| 1026 | + // |
| 1027 | + // A wrinkle here is that std::set actually has multiple insert overloads. |
| 1028 | + // There are two overloads that could work for us: |
| 1029 | + // |
| 1030 | + // insert_return_type insert(const value_type &value); |
| 1031 | + // insert_return_type insert(value_type &&value); |
| 1032 | + // |
| 1033 | + // where insert_return_type is std::pair<iterator, bool> for std::set and |
| 1034 | + // std::unordered_set and just iterator for std::multiset. |
| 1035 | + // |
| 1036 | + // Look for the version with the single const-lref value_type parameter, |
| 1037 | + // since that's the one that maps to Swift's semantics most closely. |
| 1038 | + // |
| 1039 | + // NOTE: this code is a bit lengthy, and could be abstracted into a helper |
| 1040 | + // function, but at this time of writing, the only two times we need to look |
| 1041 | + // for a member is for std::set and std::map's insert methods. We keep this |
| 1042 | + // lookup routine inlined for now until the interface for a reasonably |
| 1043 | + // encapsulated helper function emerges. |
| 1044 | + |
| 1045 | + auto R = clang::LookupResult( |
| 1046 | + clangSema, &clangSema.PP.getIdentifierTable().get("insert"), |
| 1047 | + clang::SourceLocation(), clang::Sema::LookupMemberName); |
| 1048 | + R.suppressDiagnostics(); |
| 1049 | + auto *Ctx = static_cast<const clang::DeclContext *>(clangDecl); |
| 1050 | + clangSema.LookupQualifiedName(R, const_cast<clang::DeclContext *>(Ctx)); |
| 1051 | + switch (R.getResultKind()) { |
| 1052 | + case clang::LookupResultKind::Found: |
| 1053 | + case clang::LookupResultKind::FoundOverloaded: |
| 1054 | + break; |
| 1055 | + default: |
| 1056 | + return; |
| 1057 | + } |
1017 | 1058 |
|
1018 | | - ProtocolDecl *cxxInputIteratorProto = |
1019 | | - ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator); |
1020 | | - if (!cxxInputIteratorProto) |
| 1059 | + for (auto *nd : R) { |
| 1060 | + if (auto *insertOverload = dyn_cast<clang::CXXMethodDecl>(nd)) { |
| 1061 | + if (insertOverload->param_size() != 1) |
| 1062 | + continue; |
| 1063 | + auto *paramTy = (*insertOverload->param_begin()) |
| 1064 | + ->getType() |
| 1065 | + ->getAs<clang::ReferenceType>(); |
| 1066 | + if (!paramTy) |
| 1067 | + continue; |
| 1068 | + if (paramTy->getPointeeType()->getCanonicalTypeUnqualified() != |
| 1069 | + clangCtx.getTypeDeclType(value_type)->getCanonicalTypeUnqualified()) |
| 1070 | + continue; |
| 1071 | + if (!paramTy->getPointeeType().isConstQualified()) |
| 1072 | + continue; |
| 1073 | + insert = insertOverload; // Found the insert() we're looking for |
| 1074 | + break; |
| 1075 | + } |
| 1076 | + } |
| 1077 | + } |
| 1078 | + if (!insert) |
1021 | 1079 | return; |
1022 | 1080 |
|
1023 | | - auto rawIteratorType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>( |
1024 | | - decl, ctx.getIdentifier("const_iterator")); |
1025 | | - auto rawMutableIteratorType = |
1026 | | - lookupDirectSingleWithoutExtensions<TypeAliasDecl>( |
1027 | | - decl, ctx.getIdentifier("iterator")); |
1028 | | - if (!rawIteratorType || !rawMutableIteratorType) |
| 1081 | + // We've looked up everything we need from Clang for the conformance. |
| 1082 | + // Now, use ClangImporter to convert import those types to Swift. |
| 1083 | + // |
| 1084 | + // NOTE: we're actually importing the typedefs and function members here, |
| 1085 | + // but not *adding* them as members to the Swift StructDecl---that is done |
| 1086 | + // elsewhere (and could be lazy too, though not at this time of writing). |
| 1087 | + // We are just using these imported Swift members for their type fields, |
| 1088 | + // because importDecl() needs fewer arguments than importTypeIgnoreIUO(). |
| 1089 | + |
| 1090 | + auto *Size = dyn_cast_or_null<TypeAliasDecl>( |
| 1091 | + impl.importDecl(size_type, impl.CurrentVersion)); |
| 1092 | + auto *Value = dyn_cast_or_null<TypeAliasDecl>( |
| 1093 | + impl.importDecl(value_type, impl.CurrentVersion)); |
| 1094 | + auto *RawMutableIterator = dyn_cast_or_null<TypeAliasDecl>( |
| 1095 | + impl.importDecl(iterator, impl.CurrentVersion)); |
| 1096 | + auto *RawIterator = dyn_cast_or_null<TypeAliasDecl>( |
| 1097 | + impl.importDecl(const_iterator, impl.CurrentVersion)); |
| 1098 | + auto *Insert = |
| 1099 | + dyn_cast_or_null<FuncDecl>(impl.importDecl(insert, impl.CurrentVersion)); |
| 1100 | + if (!Size || !Value || !RawMutableIterator || !RawIterator || !Insert) |
1029 | 1101 | return; |
1030 | 1102 |
|
1031 | | - auto rawIteratorTy = rawIteratorType->getUnderlyingType(); |
1032 | | - auto rawMutableIteratorTy = rawMutableIteratorType->getUnderlyingType(); |
1033 | | - |
1034 | | - if (!checkConformance(rawIteratorTy, cxxInputIteratorProto) || |
1035 | | - !checkConformance(rawMutableIteratorTy, cxxInputIteratorProto)) |
1036 | | - return; |
| 1103 | + // We have our Swift types, synthesize type aliases and conformances |
1037 | 1104 |
|
| 1105 | + impl.addSynthesizedTypealias(decl, ctx.Id_Element, |
| 1106 | + Value->getUnderlyingType()); |
| 1107 | + impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement, |
| 1108 | + Value->getUnderlyingType()); |
| 1109 | + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), |
| 1110 | + Size->getUnderlyingType()); |
1038 | 1111 | impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"), |
1039 | | - rawIteratorTy); |
| 1112 | + RawIterator->getUnderlyingType()); |
1040 | 1113 | impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"), |
1041 | | - rawMutableIteratorTy); |
1042 | | - |
1043 | | - // Synthesize conformance to CxxUniqueSet if the caller asked for it |
1044 | | - // (if decl is std::set or std::unordered_set, but not std::multiset) |
1045 | | - if (!isUniqueSet) |
1046 | | - return; |
| 1114 | + RawMutableIterator->getUnderlyingType()); |
| 1115 | + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), |
| 1116 | + Insert->getResultInterfaceType()); |
1047 | 1117 |
|
1048 | | - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxUniqueSet}); |
| 1118 | + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet}); |
| 1119 | + if (isUniqueSet) |
| 1120 | + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxUniqueSet}); |
1049 | 1121 | } |
1050 | 1122 |
|
1051 | 1123 | static void conformToCxxPair(ClangImporter::Implementation &impl, |
|
0 commit comments