diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index 4608437ec89e3..9cadd28e0ac5a 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -13,12 +13,19 @@ #include "ClangDerivedConformances.h" #include "ImporterImpl.h" #include "swift/AST/ConformanceLookup.h" +#include "swift/AST/LayoutConstraint.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/Assertions.h" #include "swift/ClangImporter/ClangImporterRequests.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" #include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/Lookup.h" #include "clang/Sema/Overload.h" using namespace swift; @@ -58,6 +65,28 @@ static CxxStdType identifyCxxStdTypeByName(StringRef name) { #undef CxxStdCase } +static const clang::TypeDecl * +lookupCxxTypeMember(clang::Sema &Sema, const clang::CXXRecordDecl *Rec, + StringRef name, bool mustBeComplete = false) { + auto R = clang::LookupResult(Sema, &Sema.PP.getIdentifierTable().get(name), + clang::SourceLocation(), + clang::Sema::LookupMemberName); + R.suppressDiagnostics(); + auto *Ctx = static_cast(Rec); + Sema.LookupQualifiedName(R, const_cast(Ctx)); + + if (auto *td = R.getAsSingle()) { + if (auto *paths = R.getBasePaths(); + paths && paths->front().Access != clang::AS_public) + return nullptr; + if (mustBeComplete && + !Sema.isCompleteType({}, td->getASTContext().getTypeDeclType(td))) + return nullptr; + return td; + } + return nullptr; +} + /// Alternative to `NominalTypeDecl::lookupDirect`. /// This function does not attempt to load extensions of the nominal decl. static TinyPtrVector @@ -106,55 +135,6 @@ static Decl *lookupDirectSingleWithoutExtensions(NominalTypeDecl *decl, return dyn_cast(results.front()); } -static FuncDecl *getInsertFunc(NominalTypeDecl *decl, - TypeAliasDecl *valueType) { - ASTContext &ctx = decl->getASTContext(); - - auto insertId = ctx.getIdentifier("__insertUnsafe"); - auto inserts = lookupDirectWithoutExtensions(decl, insertId); - FuncDecl *insert = nullptr; - for (auto candidate : inserts) { - if (auto candidateMethod = dyn_cast(candidate)) { - auto params = candidateMethod->getParameters(); - if (params->size() != 1) - continue; - auto param = params->front(); - if (param->getTypeInContext()->getCanonicalType() != - valueType->getUnderlyingType()->getCanonicalType()) - continue; - insert = candidateMethod; - break; - } - } - return insert; -} - -static clang::TypeDecl * -lookupNestedClangTypeDecl(const clang::CXXRecordDecl *clangDecl, - StringRef name) { - clang::IdentifierInfo *nestedDeclName = - &clangDecl->getASTContext().Idents.get(name); - auto nestedDecls = clangDecl->lookup(nestedDeclName); - // If this is a templated typedef, Clang might have instantiated several - // equivalent typedef decls. If they aren't equivalent, Clang has already - // complained about this. Let's assume that they are equivalent. (see - // filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp) - if (nestedDecls.empty()) - return nullptr; - auto nestedDecl = nestedDecls.front(); - return dyn_cast_or_null(nestedDecl); -} - -static clang::TypeDecl * -getIteratorCategoryDecl(const clang::CXXRecordDecl *clangDecl) { - return lookupNestedClangTypeDecl(clangDecl, "iterator_category"); -} - -static clang::TypeDecl * -getIteratorConceptDecl(const clang::CXXRecordDecl *clangDecl) { - return lookupNestedClangTypeDecl(clangDecl, "iterator_concept"); -} - static ValueDecl *lookupOperator(NominalTypeDecl *decl, Identifier id, function_ref isValid) { // First look for operator declared as a member. @@ -420,8 +400,18 @@ static bool synthesizeCXXOperator(ClangImporter::Implementation &impl, return true; } -bool swift::isIterator(const clang::CXXRecordDecl *clangDecl) { - return getIteratorCategoryDecl(clangDecl); +bool swift::hasIteratorCategory(const clang::CXXRecordDecl *clangDecl) { + clang::IdentifierInfo *name = + &clangDecl->getASTContext().Idents.get("iterator_category"); + auto members = clangDecl->lookup(name); + if (members.empty()) + return false; + // NOTE: If this is a templated typedef, Clang might have instantiated + // several equivalent typedef decls, so members.isSingleResult() may + // return false here. But if they aren't equivalent, Clang should have + // already complained about this. Let's assume that they are equivalent. + // (see filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp) + return isa(members.front()); } ValueDecl * @@ -453,6 +443,7 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl, PrettyStackTraceDecl trace("trying to conform to UnsafeCxxInputIterator", decl); ASTContext &ctx = decl->getASTContext(); clang::ASTContext &clangCtx = clangDecl->getASTContext(); + clang::Sema &clangSema = impl.getClangSema(); if (!ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator)) return; @@ -460,13 +451,19 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl, // We consider a type to be an input iterator if it defines an // `iterator_category` that inherits from `std::input_iterator_tag`, e.g. // `using iterator_category = std::input_iterator_tag`. - auto iteratorCategory = getIteratorCategoryDecl(clangDecl); - if (!iteratorCategory) + // + // FIXME: The second hasIteratorCategory() is more conservative than it should + // be because it doesn't consider things like inheritance, but checking this + // here maintains existing behavior and ensures consistency across + // ClangImporter, where clang::Sema isn't always readily available. + const auto *iteratorCategory = + lookupCxxTypeMember(clangSema, clangDecl, "iterator_category"); + if (!iteratorCategory || !hasIteratorCategory(clangDecl)) return; auto unwrapUnderlyingTypeDecl = - [](clang::TypeDecl *typeDecl) -> clang::CXXRecordDecl * { - clang::CXXRecordDecl *underlyingDecl = nullptr; + [](const clang::TypeDecl *typeDecl) -> const clang::CXXRecordDecl * { + const clang::CXXRecordDecl *underlyingDecl = nullptr; if (auto typedefDecl = dyn_cast(typeDecl)) { auto type = typedefDecl->getUnderlyingType(); underlyingDecl = type->getAsCXXRecordDecl(); @@ -525,7 +522,8 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl, // `iterator_concept`. It is not possible to detect a contiguous iterator // based on its `iterator_category`. The type might not have an // `iterator_concept` defined. - if (auto iteratorConcept = getIteratorConceptDecl(clangDecl)) { + if (const auto *iteratorConcept = + lookupCxxTypeMember(clangSema, clangDecl, "iterator_concept")) { if (auto underlyingConceptDecl = unwrapUnderlyingTypeDecl(iteratorConcept)) { isContiguousIterator = isContiguousIteratorDecl(underlyingConceptDecl); @@ -702,23 +700,18 @@ static void conformToCxxOptional(ClangImporter::Implementation &impl, clang::ASTContext &clangCtx = impl.getClangASTContext(); clang::Sema &clangSema = impl.getClangSema(); - ProtocolDecl *cxxOptionalProto = - ctx.getProtocol(KnownProtocolKind::CxxOptional); - // If the Cxx module is missing, or does not include one of the necessary - // protocol, bail. - if (!cxxOptionalProto) + auto *value_type = lookupCxxTypeMember(clangSema, clangDecl, "value_type", + /*mustBeComplete=*/true); + if (!value_type) return; - auto pointeeId = ctx.getIdentifier("pointee"); - auto pointees = lookupDirectWithoutExtensions(decl, pointeeId); - if (pointees.size() != 1) + auto *Wrapped = dyn_cast_or_null( + impl.importDecl(value_type, impl.CurrentVersion)); + if (!Wrapped) return; - auto pointee = dyn_cast(pointees.front()); - if (!pointee) - return; - auto pointeeTy = pointee->getInterfaceType(); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Wrapped"), pointeeTy); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Wrapped"), + Wrapped->getUnderlyingType()); impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxOptional}); // `std::optional` has a C++ constructor that takes the wrapped value as a @@ -726,11 +719,7 @@ static void conformToCxxOptional(ClangImporter::Implementation &impl, // it isn't directly usable from Swift. Let's explicitly instantiate a // constructor with the wrapped value type, and then import it into Swift. - auto valueTypeDecl = lookupNestedClangTypeDecl(clangDecl, "value_type"); - if (!valueTypeDecl) - // `std::optional` without a value_type?! - return; - auto valueType = clangCtx.getTypeDeclType(valueTypeDecl); + auto valueType = clangCtx.getTypeDeclType(value_type); auto constRefValueType = clangCtx.getLValueReferenceType(valueType.withConst()); @@ -976,59 +965,127 @@ static void conformToCxxSet(ClangImporter::Implementation &impl, bool isUniqueSet) { PrettyStackTraceDecl trace("conforming to CxxSet", decl); ASTContext &ctx = decl->getASTContext(); + auto &clangCtx = impl.getClangASTContext(); + auto &clangSema = impl.getClangSema(); - auto valueType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("value_type")); - auto sizeType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("size_type")); - if (!valueType || !sizeType) - return; - - auto insert = getInsertFunc(decl, valueType); - if (!insert) + // Look up the type members we need from Clang + // + // N.B. we don't actually need const_iterator for multiset, but it should be + // there. If it's not there for any reason, we should probably bail out. + + auto *size_type = lookupCxxTypeMember(clangSema, clangDecl, "size_type", + /*mustBeComplete=*/true); + auto *value_type = lookupCxxTypeMember(clangSema, clangDecl, "value_type", + /*mustBeComplete=*/true); + auto *iterator = lookupCxxTypeMember(clangSema, clangDecl, "iterator", + /*mustBeComplete=*/true); + auto *const_iterator = + lookupCxxTypeMember(clangSema, clangDecl, "const_iterator", + /*mustBeComplete=*/true); + if (!size_type || !value_type || !iterator || !const_iterator) return; - impl.addSynthesizedTypealias(decl, ctx.Id_Element, - valueType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement, - valueType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), - sizeType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), - insert->getResultInterfaceType()); - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet}); + const clang::CXXMethodDecl *insert = nullptr; + { + // CxxSet requires the InsertionResult associated type, which is the return + // type of std::set (and co.)'s insert function. But there is no equivalent + // typedef in C++ we can use directly, so we need get it by converting the + // return type of the insert function. + // + // A wrinkle here is that std::set actually has multiple insert overloads. + // There are two overloads that could work for us: + // + // insert_return_type insert(const value_type &value); + // insert_return_type insert(value_type &&value); + // + // where insert_return_type is std::pair for std::set and + // std::unordered_set and just iterator for std::multiset. + // + // Look for the version with the single const-lref value_type parameter, + // since that's the one that maps to Swift's semantics most closely. + // + // NOTE: this code is a bit lengthy, and could be abstracted into a helper + // function, but at this time of writing, the only two times we need to look + // for a member is for std::set and std::map's insert methods. We keep this + // lookup routine inlined for now until the interface for a reasonably + // encapsulated helper function emerges. + + auto R = clang::LookupResult( + clangSema, &clangSema.PP.getIdentifierTable().get("insert"), + clang::SourceLocation(), clang::Sema::LookupMemberName); + R.suppressDiagnostics(); + auto *Ctx = static_cast(clangDecl); + clangSema.LookupQualifiedName(R, const_cast(Ctx)); + switch (R.getResultKind()) { + case clang::LookupResultKind::Found: + case clang::LookupResultKind::FoundOverloaded: + break; + default: + return; + } - ProtocolDecl *cxxInputIteratorProto = - ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator); - if (!cxxInputIteratorProto) + for (auto *nd : R) { + if (auto *insertOverload = dyn_cast(nd)) { + if (insertOverload->param_size() != 1) + continue; + auto *paramTy = (*insertOverload->param_begin()) + ->getType() + ->getAs(); + if (!paramTy) + continue; + if (paramTy->getPointeeType()->getCanonicalTypeUnqualified() != + clangCtx.getTypeDeclType(value_type)->getCanonicalTypeUnqualified()) + continue; + if (!paramTy->getPointeeType().isConstQualified()) + continue; + insert = insertOverload; // Found the insert() we're looking for + break; + } + } + } + if (!insert) return; - auto rawIteratorType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("const_iterator")); - auto rawMutableIteratorType = - lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("iterator")); - if (!rawIteratorType || !rawMutableIteratorType) + // We've looked up everything we need from Clang for the conformance. + // Now, use ClangImporter to convert import those types to Swift. + // + // NOTE: we're actually importing the typedefs and function members here, + // but not *adding* them as members to the Swift StructDecl---that is done + // elsewhere (and could be lazy too, though not at this time of writing). + // We are just using these imported Swift members for their type fields, + // because importDecl() needs fewer arguments than importTypeIgnoreIUO(). + + auto *Size = dyn_cast_or_null( + impl.importDecl(size_type, impl.CurrentVersion)); + auto *Value = dyn_cast_or_null( + impl.importDecl(value_type, impl.CurrentVersion)); + auto *RawMutableIterator = dyn_cast_or_null( + impl.importDecl(iterator, impl.CurrentVersion)); + auto *RawIterator = dyn_cast_or_null( + impl.importDecl(const_iterator, impl.CurrentVersion)); + auto *Insert = + dyn_cast_or_null(impl.importDecl(insert, impl.CurrentVersion)); + if (!Size || !Value || !RawMutableIterator || !RawIterator || !Insert) return; - auto rawIteratorTy = rawIteratorType->getUnderlyingType(); - auto rawMutableIteratorTy = rawMutableIteratorType->getUnderlyingType(); - - if (!checkConformance(rawIteratorTy, cxxInputIteratorProto) || - !checkConformance(rawMutableIteratorTy, cxxInputIteratorProto)) - return; + // We have our Swift types, synthesize type aliases and conformances + impl.addSynthesizedTypealias(decl, ctx.Id_Element, + Value->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement, + Value->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), + Size->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"), - rawIteratorTy); + RawIterator->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"), - rawMutableIteratorTy); - - // Synthesize conformance to CxxUniqueSet if the caller asked for it - // (if decl is std::set or std::unordered_set, but not std::multiset) - if (!isUniqueSet) - return; + RawMutableIterator->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), + Insert->getResultInterfaceType()); - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxUniqueSet}); + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet}); + if (isUniqueSet) + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxUniqueSet}); } static void conformToCxxPair(ClangImporter::Implementation &impl, @@ -1036,18 +1093,26 @@ static void conformToCxxPair(ClangImporter::Implementation &impl, const clang::CXXRecordDecl *clangDecl) { PrettyStackTraceDecl trace("conforming to CxxPair", decl); ASTContext &ctx = decl->getASTContext(); + clang::Sema &clangSema = impl.getClangSema(); - auto firstType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("first_type")); - auto secondType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("second_type")); - if (!firstType || !secondType) + auto *first_type = lookupCxxTypeMember(clangSema, clangDecl, "first_type", + /*mustBeComplete=*/true); + auto *second_type = lookupCxxTypeMember(clangSema, clangDecl, "second_type", + /*mustBeComplete=*/true); + if (!first_type || !second_type) + return; + + auto *First = dyn_cast_or_null( + impl.importDecl(first_type, impl.CurrentVersion)); + auto *Second = dyn_cast_or_null( + impl.importDecl(second_type, impl.CurrentVersion)); + if (!First || !Second) return; impl.addSynthesizedTypealias(decl, ctx.getIdentifier("First"), - firstType->getUnderlyingType()); + First->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Second"), - secondType->getUnderlyingType()); + Second->getUnderlyingType()); impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxPair}); } @@ -1056,69 +1121,113 @@ static void conformToCxxDictionary(ClangImporter::Implementation &impl, const clang::CXXRecordDecl *clangDecl) { PrettyStackTraceDecl trace("conforming to CxxDictionary", decl); ASTContext &ctx = decl->getASTContext(); + clang::ASTContext &clangCtx = impl.getClangASTContext(); + clang::Sema &clangSema = impl.getClangSema(); - auto keyType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("key_type")); - auto valueType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("mapped_type")); - auto iterType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("const_iterator")); - auto mutableIterType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("iterator")); - auto sizeType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("size_type")); - auto keyValuePairType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("value_type")); - if (!keyType || !valueType || !iterType || !mutableIterType || !sizeType || - !keyValuePairType) - return; +#define lookup_type(member) \ + auto *member = lookupCxxTypeMember(clangSema, clangDecl, #member, \ + /*mustBeComplete=*/true); \ + if (!member) \ + return + + lookup_type(key_type); + lookup_type(mapped_type); + lookup_type(value_type); + lookup_type(size_type); + lookup_type(iterator); + lookup_type(const_iterator); +#undef lookup_type + + const clang::CXXMethodDecl *insert = nullptr; + { + // CxxDictionary requires the InsertionResult associated type, which is the + // return type of std::map (and co.)'s insert function. But there is no + // equivalent typedef in C++ we can use directly, so we need get it by + // converting the return type of this overload of the insert function: + // + // insert_return_type insert(const value_type &value); + // + // See also: extended monologuing in conformToCxxSet(). + auto R = clang::LookupResult( + clangSema, &clangSema.PP.getIdentifierTable().get("insert"), + clang::SourceLocation(), clang::Sema::LookupMemberName); + R.suppressDiagnostics(); + auto *Ctx = static_cast(clangDecl); + clangSema.LookupQualifiedName(R, const_cast(Ctx)); + switch (R.getResultKind()) { + case clang::LookupResultKind::Found: + case clang::LookupResultKind::FoundOverloaded: + break; + default: + return; + } - auto insert = getInsertFunc(decl, keyValuePairType); + for (auto *nd : R) { + if (auto *insertOverload = dyn_cast(nd)) { + if (insertOverload->param_size() != 1) + continue; + auto *paramTy = (*insertOverload->param_begin()) + ->getType() + ->getAs(); + if (!paramTy) + continue; + if (paramTy->getPointeeType()->getCanonicalTypeUnqualified() != + clangCtx.getTypeDeclType(value_type)->getCanonicalTypeUnqualified()) + continue; + if (!paramTy->getPointeeType().isConstQualified()) + continue; + insert = insertOverload; // Found the insert() we're looking for + break; + } + } + } if (!insert) return; - ProtocolDecl *cxxInputIteratorProto = - ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator); - ProtocolDecl *cxxMutableInputIteratorProto = - ctx.getProtocol(KnownProtocolKind::UnsafeCxxMutableInputIterator); - if (!cxxInputIteratorProto || !cxxMutableInputIteratorProto) - return; - - auto rawIteratorTy = iterType->getUnderlyingType(); - auto rawMutableIteratorTy = mutableIterType->getUnderlyingType(); - - // Check if RawIterator conforms to UnsafeCxxInputIterator. - if (!checkConformance(rawIteratorTy, cxxInputIteratorProto)) +#define importTypeAlias(to, from) \ + auto *to = dyn_cast_or_null( \ + impl.importDecl(from, impl.CurrentVersion)); \ + if (!to) \ + return + + importTypeAlias(Size, size_type); + importTypeAlias(Key, key_type); + importTypeAlias(Value, mapped_type); + importTypeAlias(Element, value_type); + importTypeAlias(RawIterator, const_iterator); + importTypeAlias(RawMutableIterator, iterator); +#undef importTypeAlias + + auto *Insert = + dyn_cast_or_null(impl.importDecl(insert, impl.CurrentVersion)); + if (!Insert) return; - // Check if RawMutableIterator conforms to UnsafeCxxMutableInputIterator. - if (!checkConformance(rawMutableIteratorTy, cxxMutableInputIteratorProto)) - return; + impl.addSynthesizedTypealias(decl, ctx.Id_Key, Key->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.Id_Value, Value->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.Id_Element, + Element->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"), + RawIterator->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"), + RawMutableIterator->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), + Size->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), + Insert->getResultInterfaceType()); + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxDictionary}); // Make the original subscript that returns a non-optional value unavailable. // CxxDictionary adds another subscript that returns an optional value, // similarly to Swift.Dictionary. + // + // NOTE: this relies on the SubscriptDecl member being imported eagerly. for (auto member : decl->getCurrentMembersWithoutLoading()) { if (auto subscript = dyn_cast(member)) { impl.markUnavailable(subscript, "use subscript with optional return value"); } } - - impl.addSynthesizedTypealias(decl, ctx.Id_Key, keyType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.Id_Value, - valueType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.Id_Element, - keyValuePairType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"), - rawIteratorTy); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"), - rawMutableIteratorTy); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), - sizeType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), - insert->getResultInterfaceType()); - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxDictionary}); } static void conformToCxxVector(ClangImporter::Implementation &impl, @@ -1126,35 +1235,33 @@ static void conformToCxxVector(ClangImporter::Implementation &impl, const clang::CXXRecordDecl *clangDecl) { PrettyStackTraceDecl trace("conforming to CxxVector", decl); ASTContext &ctx = decl->getASTContext(); + clang::Sema &clangSema = impl.getClangSema(); - auto valueType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("value_type")); - auto iterType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("const_iterator")); - auto sizeType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("size_type")); - if (!valueType || !iterType || !sizeType) - return; - - ProtocolDecl *cxxRandomAccessIteratorProto = - ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator); - if (!cxxRandomAccessIteratorProto) - return; - - auto rawIteratorTy = iterType->getUnderlyingType(); - - // Check if RawIterator conforms to UnsafeCxxRandomAccessIterator. - if (!checkConformance(rawIteratorTy, cxxRandomAccessIteratorProto)) + auto *value_type = lookupCxxTypeMember(clangSema, clangDecl, "value_type", + /*mustBeComplete=*/true); + auto *size_type = lookupCxxTypeMember(clangSema, clangDecl, "size_type", + /*mustBeComplete=*/true); + auto *const_iterator = + lookupCxxTypeMember(clangSema, clangDecl, "const_iterator", + /*mustBeComplete=*/true); + + auto *Element = dyn_cast_or_null( + impl.importDecl(value_type, impl.CurrentVersion)); + auto *Size = dyn_cast_or_null( + impl.importDecl(size_type, impl.CurrentVersion)); + auto *RawIterator = dyn_cast_or_null( + impl.importDecl(const_iterator, impl.CurrentVersion)); + if (!Element || !Size || !RawIterator) return; impl.addSynthesizedTypealias(decl, ctx.Id_Element, - valueType->getUnderlyingType()); + Element->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement, - valueType->getUnderlyingType()); + Element->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), - sizeType->getUnderlyingType()); + Size->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"), - rawIteratorTy); + RawIterator->getUnderlyingType()); impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxVector}); } @@ -1166,22 +1273,36 @@ static void conformToCxxSpan(ClangImporter::Implementation &impl, clang::ASTContext &clangCtx = impl.getClangASTContext(); clang::Sema &clangSema = impl.getClangSema(); - auto elementType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("element_type")); - auto sizeType = lookupDirectSingleWithoutExtensions( - decl, ctx.getIdentifier("size_type")); - - if (!elementType || !sizeType) + auto *element_type = lookupCxxTypeMember(clangSema, clangDecl, "element_type", + /*mustBeComplete=*/true); + auto *size_type = lookupCxxTypeMember(clangSema, clangDecl, "size_type", + /*mustBeComplete=*/true); + auto *pointer = lookupCxxTypeMember(clangSema, clangDecl, "pointer", + /*mustBeComplete=*/true); + if (!element_type || !size_type || !pointer) return; - auto pointerTypeDecl = lookupNestedClangTypeDecl(clangDecl, "pointer"); - auto countTypeDecl = lookupNestedClangTypeDecl(clangDecl, "size_type"); + auto pointerType = clangCtx.getTypeDeclType(pointer); + auto sizeType = clangCtx.getTypeDeclType(size_type); - if (!pointerTypeDecl || !countTypeDecl) + auto *Element = dyn_cast_or_null( + impl.importDecl(element_type, impl.CurrentVersion)); + auto *Size = dyn_cast_or_null( + impl.importDecl(size_type, impl.CurrentVersion)); + if (!Element || !Size) return; + impl.addSynthesizedTypealias(decl, ctx.Id_Element, + Element->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), + Size->getUnderlyingType()); + + if (pointerType->getPointeeType().isConstQualified()) + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSpan}); + else + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxMutableSpan}); + // create fake variable for pointer (constructor arg 1) - clang::QualType pointerType = clangCtx.getTypeDeclType(pointerTypeDecl); auto fakePointerVarDecl = clang::VarDecl::Create( clangCtx, /*DC*/ clangCtx.getTranslationUnitDecl(), clang::SourceLocation(), clang::SourceLocation(), /*Id*/ nullptr, @@ -1193,15 +1314,14 @@ static void conformToCxxSpan(ClangImporter::Implementation &impl, clang::ExprValueKind::VK_LValue, clang::SourceLocation()); // create fake variable for count (constructor arg 2) - auto countType = clangCtx.getTypeDeclType(countTypeDecl); auto fakeCountVarDecl = clang::VarDecl::Create( clangCtx, /*DC*/ clangCtx.getTranslationUnitDecl(), clang::SourceLocation(), clang::SourceLocation(), /*Id*/ nullptr, - countType, clangCtx.getTrivialTypeSourceInfo(countType), + sizeType, clangCtx.getTrivialTypeSourceInfo(sizeType), clang::StorageClass::SC_None); auto fakeCount = new (clangCtx) clang::DeclRefExpr( - clangCtx, fakeCountVarDecl, false, countType, + clangCtx, fakeCountVarDecl, false, sizeType, clang::ExprValueKind::VK_LValue, clang::SourceLocation()); // Use clangSema.BuildCxxTypeConstructExpr to create a CXXTypeConstructExpr, @@ -1234,17 +1354,6 @@ static void conformToCxxSpan(ClangImporter::Implementation &impl, importedConstructor->addAttribute(attr); decl->addMember(importedConstructor); - - impl.addSynthesizedTypealias(decl, ctx.Id_Element, - elementType->getUnderlyingType()); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), - sizeType->getUnderlyingType()); - - if (pointerType->getPointeeType().isConstQualified()) { - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSpan}); - } else { - impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxMutableSpan}); - } } void swift::deriveAutomaticCxxConformances( @@ -1283,9 +1392,10 @@ void swift::deriveAutomaticCxxConformances( return; case CxxStdType::set: case CxxStdType::unordered_set: + conformToCxxSet(Impl, result, clangDecl, /*isUniqueSet=*/true); + return; case CxxStdType::multiset: - conformToCxxSet(Impl, result, clangDecl, - /*isUniqueSet=*/ty != CxxStdType::multiset); + conformToCxxSet(Impl, result, clangDecl, /*isUniqueSet=*/false); return; case CxxStdType::pair: conformToCxxPair(Impl, result, clangDecl); diff --git a/lib/ClangImporter/ClangDerivedConformances.h b/lib/ClangImporter/ClangDerivedConformances.h index 0b7d17bfcf1d4..a3fedb64a15b7 100644 --- a/lib/ClangImporter/ClangDerivedConformances.h +++ b/lib/ClangImporter/ClangDerivedConformances.h @@ -18,7 +18,14 @@ namespace swift { -bool isIterator(const clang::CXXRecordDecl *clangDecl); +/// Whether a C++ record decl contains a type member named "iterator_category", +/// a heuristic we use to determine whether that record type is an iterator. +/// +/// This function returns true if there is exactly one public type member named +/// "iterator_category", but does not look for inherited members. Note that, as +/// a result of these limitations, it may return false even if that record type +/// is usable as an iterator. +bool hasIteratorCategory(const clang::CXXRecordDecl *clangDecl); bool isUnsafeStdMethod(const clang::CXXMethodDecl *methodDecl); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index b6125283435ff..5999abeb09101 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -8376,7 +8376,7 @@ static bool hasPointerInSubobjects(const clang::CXXRecordDecl *decl) { hasUnsafeAPIAttr(cxxRecord)) return false; - if (hasIteratorAPIAttr(cxxRecord) || isIterator(cxxRecord)) + if (hasIteratorAPIAttr(cxxRecord) || hasIteratorCategory(cxxRecord)) return true; if (hasPointerInSubobjects(cxxRecord)) @@ -8497,7 +8497,7 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator, if (isSwiftClassType(cxxDecl)) return CxxRecordSemanticsKind::SwiftClassType; - if (hasIteratorAPIAttr(cxxDecl) || isIterator(cxxDecl)) { + if (hasIteratorAPIAttr(cxxDecl) || hasIteratorCategory(cxxDecl)) { return CxxRecordSemanticsKind::Iterator; } @@ -8760,7 +8760,7 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator, return true; if (hasIteratorAPIAttr(cxxRecordReturnType) || - isIterator(cxxRecordReturnType)) + hasIteratorCategory(cxxRecordReturnType)) return false; // Mark this as safe to help our diganostics down the road. diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 2245408fb6962..a5010b5f744b6 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2449,6 +2449,64 @@ namespace { } } + // Determine whether we should import this member; we should only import + // members that are necessary even when they are never referred to. + // + // Not only is this better for performance, it also avoids unnecessarily + // instantiating templates, which can be a bit of a minefield in light + // of SFINAE and template meta-programming. + bool shouldImport = false; + + // underlyingMember is what we will use to determine whether to import, + // looking through any using decls. + auto *underlyingMember = nd; + while (auto *usd = dyn_cast(underlyingMember)) + underlyingMember = usd->getTargetDecl(); + + if (isa(underlyingMember)) { + // Always import fields; these affect layout and thus codegen. + shouldImport = true; + } else if (isa(underlyingMember)) { + // Import function template decls for now. The way these these are + // handled right now, these should be safe from spurious + // instantiation, and seem to be necessary for lookups. + // FIXME: these shouldn't be necessary to import. + shouldImport = true; + } else if (auto *fn = dyn_cast(underlyingMember)) { + switch (fn->getDeclName().getNameKind()) { + case clang::DeclarationName::Identifier: + // Always import begin() and end() eagerly, since those are + // handled specially, but skip importing other functions. + if (fn->getMinRequiredArguments() == 0 && + (fn->getName() == "begin" || fn->getName() == "end")) { + shouldImport = true; + } else if (auto *md = dyn_cast(fn)) { + // Lookup doesn't know about these + if (CXXMethodBridging(md).classify() != + CXXMethodBridging::Kind::unknown) + shouldImport = true; + } + break; + + case clang::DeclarationName::CXXOperatorName: + case clang::DeclarationName::CXXConversionFunctionName: + case clang::DeclarationName::CXXConstructorName: + // Always import operators, conversions, and constructors for now. + // Some of these are handled in special ways that require them to + // be eagerly available, e.g., in VisitCXXRecordDecl() and + // conformToCxxConvertibleToBoolIfNeeded(). + shouldImport = true; + break; + default: + break; + } + } + + // Members we skip here can be imported on-demand if and when they are + // referred to in Swift + if (!shouldImport) + continue; + Decl *member = Impl.importDecl(nd, getActiveSwiftVersion()); if (!member) { diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 6988c78a462e2..3a93c154a9377 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -2436,13 +2436,7 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( if (!genericPointerType) addDiag(Diagnostic(diag::bridged_pointer_type_not_found, pointerKind)); importedType = {genericPointerType, false}; - } else if (!(isa(returnType) || - isa(returnType)) || - // TODO: we currently don't lazily load operator return types, but - // this should be trivial to add. - clangDecl->isOverloadedOperator() || - // Dependant types are trivially mapped as Any. - returnType->isDependentType()) { + } else { // If importedType is already initialized, it means we found the enum that // was supposed to be used (instead of the typedef type). if (!importedType) {