@@ -123,19 +123,24 @@ static bool isStdDecl(const clang::CXXRecordDecl *clangDecl,
123123}
124124
125125static clang::TypeDecl *
126- getIteratorCategoryDecl (const clang::CXXRecordDecl *clangDecl) {
127- clang::IdentifierInfo *iteratorCategoryDeclName =
128- &clangDecl->getASTContext ().Idents .get (" iterator_category" );
129- auto iteratorCategories = clangDecl->lookup (iteratorCategoryDeclName);
126+ lookupNestedClangTypeDecl (const clang::CXXRecordDecl *clangDecl,
127+ StringRef name) {
128+ clang::IdentifierInfo *nestedDeclName =
129+ &clangDecl->getASTContext ().Idents .get (name);
130+ auto nestedDecls = clangDecl->lookup (nestedDeclName);
130131 // If this is a templated typedef, Clang might have instantiated several
131132 // equivalent typedef decls. If they aren't equivalent, Clang has already
132133 // complained about this. Let's assume that they are equivalent. (see
133134 // filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp)
134- if (iteratorCategories .empty ())
135+ if (nestedDecls .empty ())
135136 return nullptr ;
136- auto iteratorCategory = iteratorCategories.front ();
137+ auto nestedDecl = nestedDecls.front ();
138+ return dyn_cast_or_null<clang::TypeDecl>(nestedDecl);
139+ }
137140
138- return dyn_cast_or_null<clang::TypeDecl>(iteratorCategory);
141+ static clang::TypeDecl *
142+ getIteratorCategoryDecl (const clang::CXXRecordDecl *clangDecl) {
143+ return lookupNestedClangTypeDecl (clangDecl, " iterator_category" );
139144}
140145
141146static ValueDecl *lookupOperator (NominalTypeDecl *decl, Identifier id,
@@ -639,6 +644,8 @@ void swift::conformToCxxOptionalIfNeeded(
639644 assert (decl);
640645 assert (clangDecl);
641646 ASTContext &ctx = decl->getASTContext ();
647+ clang::ASTContext &clangCtx = impl.getClangASTContext ();
648+ clang::Sema &clangSema = impl.getClangSema ();
642649
643650 if (!isStdDecl (clangDecl, {" optional" }))
644651 return ;
@@ -661,6 +668,63 @@ void swift::conformToCxxOptionalIfNeeded(
661668
662669 impl.addSynthesizedTypealias (decl, ctx.getIdentifier (" Wrapped" ), pointeeTy);
663670 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxOptional});
671+
672+ // `std::optional` has a C++ constructor that takes the wrapped value as a
673+ // parameter. Unfortunately this constructor has templated parameter type, so
674+ // it isn't directly usable from Swift. Let's explicitly instantiate a
675+ // constructor with the wrapped value type, and then import it into Swift.
676+
677+ auto valueTypeDecl = lookupNestedClangTypeDecl (clangDecl, " value_type" );
678+ if (!valueTypeDecl)
679+ // `std::optional` without a value_type?!
680+ return ;
681+ auto valueType = clangCtx.getTypeDeclType (valueTypeDecl);
682+
683+ auto constRefValueType =
684+ clangCtx.getLValueReferenceType (valueType.withConst ());
685+ // Create a fake variable with type of the wrapped value.
686+ auto fakeValueVarDecl = clang::VarDecl::Create (
687+ clangCtx, /* DC*/ clangCtx.getTranslationUnitDecl (),
688+ clang::SourceLocation (), clang::SourceLocation (), /* Id*/ nullptr ,
689+ constRefValueType, clangCtx.getTrivialTypeSourceInfo (constRefValueType),
690+ clang::StorageClass::SC_None);
691+ auto fakeValueRefExpr = new (clangCtx) clang::DeclRefExpr (
692+ clangCtx, fakeValueVarDecl, false ,
693+ constRefValueType.getNonReferenceType (), clang::ExprValueKind::VK_LValue,
694+ clang::SourceLocation ());
695+
696+ auto clangDeclTyInfo = clangCtx.getTrivialTypeSourceInfo (
697+ clang::QualType (clangDecl->getTypeForDecl (), 0 ));
698+ SmallVector<clang::Expr *, 1 > constructExprArgs = {fakeValueRefExpr};
699+
700+ // Instantiate the templated constructor that would accept this fake variable.
701+ auto constructExprResult = clangSema.BuildCXXTypeConstructExpr (
702+ clangDeclTyInfo, clangDecl->getLocation (), constructExprArgs,
703+ clangDecl->getLocation (), /* ListInitialization*/ false );
704+ if (!constructExprResult.isUsable ())
705+ return ;
706+
707+ auto castExpr = dyn_cast_or_null<clang::CastExpr>(constructExprResult.get ());
708+ if (!castExpr)
709+ return ;
710+
711+ // The temporary bind expression will only be present for some non-trivial C++
712+ // types.
713+ auto bindTempExpr =
714+ dyn_cast_or_null<clang::CXXBindTemporaryExpr>(castExpr->getSubExpr ());
715+
716+ auto constructExpr = dyn_cast_or_null<clang::CXXConstructExpr>(
717+ bindTempExpr ? bindTempExpr->getSubExpr () : castExpr->getSubExpr ());
718+ if (!constructExpr)
719+ return ;
720+
721+ auto constructorDecl = constructExpr->getConstructor ();
722+
723+ auto importedConstructor =
724+ impl.importDecl (constructorDecl, impl.CurrentVersion );
725+ if (!importedConstructor)
726+ return ;
727+ decl->addMember (importedConstructor);
664728}
665729
666730void swift::conformToCxxSequenceIfNeeded (
0 commit comments