@@ -122,19 +122,24 @@ static bool isStdDecl(const clang::CXXRecordDecl *clangDecl,
122122}
123123
124124static clang::TypeDecl *
125- getIteratorCategoryDecl (const clang::CXXRecordDecl *clangDecl) {
126- clang::IdentifierInfo *iteratorCategoryDeclName =
127- &clangDecl->getASTContext ().Idents .get (" iterator_category" );
128- auto iteratorCategories = clangDecl->lookup (iteratorCategoryDeclName);
125+ lookupNestedClangTypeDecl (const clang::CXXRecordDecl *clangDecl,
126+ StringRef name) {
127+ clang::IdentifierInfo *nestedDeclName =
128+ &clangDecl->getASTContext ().Idents .get (name);
129+ auto nestedDecls = clangDecl->lookup (nestedDeclName);
129130 // If this is a templated typedef, Clang might have instantiated several
130131 // equivalent typedef decls. If they aren't equivalent, Clang has already
131132 // complained about this. Let's assume that they are equivalent. (see
132133 // filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp)
133- if (iteratorCategories .empty ())
134+ if (nestedDecls .empty ())
134135 return nullptr ;
135- auto iteratorCategory = iteratorCategories.front ();
136+ auto nestedDecl = nestedDecls.front ();
137+ return dyn_cast_or_null<clang::TypeDecl>(nestedDecl);
138+ }
136139
137- return dyn_cast_or_null<clang::TypeDecl>(iteratorCategory);
140+ static clang::TypeDecl *
141+ getIteratorCategoryDecl (const clang::CXXRecordDecl *clangDecl) {
142+ return lookupNestedClangTypeDecl (clangDecl, " iterator_category" );
138143}
139144
140145static ValueDecl *lookupOperator (NominalTypeDecl *decl, Identifier id,
@@ -616,6 +621,8 @@ void swift::conformToCxxOptionalIfNeeded(
616621 assert (decl);
617622 assert (clangDecl);
618623 ASTContext &ctx = decl->getASTContext ();
624+ clang::ASTContext &clangCtx = impl.getClangASTContext ();
625+ clang::Sema &clangSema = impl.getClangSema ();
619626
620627 if (!isStdDecl (clangDecl, {" optional" }))
621628 return ;
@@ -638,6 +645,63 @@ void swift::conformToCxxOptionalIfNeeded(
638645
639646 impl.addSynthesizedTypealias (decl, ctx.getIdentifier (" Wrapped" ), pointeeTy);
640647 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxOptional});
648+
649+ // `std::optional` has a C++ constructor that takes the wrapped value as a
650+ // parameter. Unfortunately this constructor has templated parameter type, so
651+ // it isn't directly usable from Swift. Let's explicitly instantiate a
652+ // constructor with the wrapped value type, and then import it into Swift.
653+
654+ auto valueTypeDecl = lookupNestedClangTypeDecl (clangDecl, " value_type" );
655+ if (!valueTypeDecl)
656+ // `std::optional` without a value_type?!
657+ return ;
658+ auto valueType = clangCtx.getTypeDeclType (valueTypeDecl);
659+
660+ auto constRefValueType =
661+ clangCtx.getLValueReferenceType (valueType.withConst ());
662+ // Create a fake variable with type of the wrapped value.
663+ auto fakeValueVarDecl = clang::VarDecl::Create (
664+ clangCtx, /* DC*/ clangCtx.getTranslationUnitDecl (),
665+ clang::SourceLocation (), clang::SourceLocation (), /* Id*/ nullptr ,
666+ constRefValueType, clangCtx.getTrivialTypeSourceInfo (constRefValueType),
667+ clang::StorageClass::SC_None);
668+ auto fakeValueRefExpr = new (clangCtx) clang::DeclRefExpr (
669+ clangCtx, fakeValueVarDecl, false ,
670+ constRefValueType.getNonReferenceType (), clang::ExprValueKind::VK_LValue,
671+ clang::SourceLocation ());
672+
673+ auto clangDeclTyInfo = clangCtx.getTrivialTypeSourceInfo (
674+ clang::QualType (clangDecl->getTypeForDecl (), 0 ));
675+ SmallVector<clang::Expr *, 1 > constructExprArgs = {fakeValueRefExpr};
676+
677+ // Instantiate the templated constructor that would accept this fake variable.
678+ auto constructExprResult = clangSema.BuildCXXTypeConstructExpr (
679+ clangDeclTyInfo, clangDecl->getLocation (), constructExprArgs,
680+ clangDecl->getLocation (), /* ListInitialization*/ false );
681+ if (!constructExprResult.isUsable ())
682+ return ;
683+
684+ auto castExpr = dyn_cast_or_null<clang::CastExpr>(constructExprResult.get ());
685+ if (!castExpr)
686+ return ;
687+
688+ // The temporary bind expression will only be present for some non-trivial C++
689+ // types.
690+ auto bindTempExpr =
691+ dyn_cast_or_null<clang::CXXBindTemporaryExpr>(castExpr->getSubExpr ());
692+
693+ auto constructExpr = dyn_cast_or_null<clang::CXXConstructExpr>(
694+ bindTempExpr ? bindTempExpr->getSubExpr () : castExpr->getSubExpr ());
695+ if (!constructExpr)
696+ return ;
697+
698+ auto constructorDecl = constructExpr->getConstructor ();
699+
700+ auto importedConstructor =
701+ impl.importDecl (constructorDecl, impl.CurrentVersion );
702+ if (!importedConstructor)
703+ return ;
704+ decl->addMember (importedConstructor);
641705}
642706
643707void swift::conformToCxxSequenceIfNeeded (
0 commit comments