2828
2929namespace Carbon ::Check {
3030
31+ // A function that wraps a C++ type to form another C++ type.
32+ using WrapFn = llvm::function_ref<
33+ auto (Context& context, clang::QualType inner_type)->clang::QualType>;
34+
35+ // Represents a type that requires a subtype to be mapped into a Clang type
36+ // before it can be mapped.
37+ struct WrappedType {
38+ // The type contained in this wrapped type.
39+ SemIR::TypeId inner_type_id;
40+ // A function to construct the wrapped type from the mapped unwrapped type.
41+ WrapFn wrap_fn;
42+ };
43+
44+ // Possible results from attempting to map a type. A null QualType indicates
45+ // that the type couldn't be mapped.
46+ using TryMapTypeResult = std::variant<clang::QualType, WrappedType>;
47+
3148// Find the bit width of an integer literal. Following the C++ standard rules
3249// for assigning a type to a decimal integer literal, the first signed integer
3350// in which the value could fit among bit widths of 32, 64 and 128 is selected.
@@ -44,9 +61,12 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
4461 // TODO: Add tests for these cases.
4562 return IntId::None;
4663 }
47- auto arg = context.insts ().GetAs <SemIR::IntValue>(
64+ auto arg = context.insts ().TryGetAs <SemIR::IntValue>(
4865 context.constant_values ().GetInstId (arg_const_id));
49- llvm::APInt arg_val = context.ints ().Get (arg.int_id );
66+ if (!arg) {
67+ return IntId::None;
68+ }
69+ llvm::APInt arg_val = context.ints ().Get (arg->int_id );
5070 int arg_non_sign_bits = arg_val.getSignificantBits () - 1 ;
5171
5272 if (arg_non_sign_bits >= 128 ) {
@@ -55,7 +75,7 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
5575 " integer type; requires {1} bits, but max is 128" ,
5676 TypedInt, int );
5777 context.emitter ().Emit (arg_id, IntTooLargeForCppType,
58- {.type = arg. type_id , .value = arg_val},
78+ {.type = arg-> type_id , .value = arg_val},
5979 arg_non_sign_bits + 1 );
6080 return IntId::None;
6181 }
@@ -103,7 +123,7 @@ static auto LookupCppType(
103123// Maps a Carbon class type to a C++ type. Returns a null `QualType` if the
104124// type is not supported.
105125static auto TryMapClassType (Context& context, SemIR::ClassType class_type)
106- -> clang::QualType {
126+ -> TryMapTypeResult {
107127 clang::ASTContext& ast_context = context.ast_context ();
108128
109129 // If the class was imported from C++, return the original C++ type.
@@ -126,10 +146,10 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
126146 break ;
127147 }
128148 case SemIR::RecognizedTypeInfo::Numeric: {
129- // Carbon supports large bit width beyond C++ builtins; we don't need to
130- // translate those.
149+ // Carbon supports large bit width beyond C++ builtins; we don't translate
150+ // those into integer types .
131151 if (!type_info.numeric .bit_width_id .is_embedded_value ()) {
132- return clang::QualType () ;
152+ break ;
133153 }
134154 int bit_width = type_info.numeric .bit_width_id .AsValue ();
135155
@@ -164,6 +184,25 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
164184 case SemIR::RecognizedTypeInfo::CppVoidBase: {
165185 return ast_context.VoidTy ;
166186 }
187+ case SemIR::RecognizedTypeInfo::Optional: {
188+ auto args = context.inst_blocks ().GetOrEmpty (type_info.args_id );
189+ if (args.size () == 1 ) {
190+ auto arg_id = args[0 ];
191+ if (auto facet = context.insts ().TryGetAs <SemIR::FacetValue>(arg_id)) {
192+ arg_id = facet->type_inst_id ;
193+ }
194+ if (auto pointer_type =
195+ context.insts ().TryGetAs <SemIR::PointerType>(arg_id)) {
196+ return WrappedType{
197+ .inner_type_id = context.types ().GetTypeIdForTypeInstId (
198+ pointer_type->pointee_id ),
199+ .wrap_fn = [](Context& context, clang::QualType inner_type) {
200+ return context.ast_context ().getPointerType (inner_type);
201+ }};
202+ }
203+ }
204+ break ;
205+ }
167206 case SemIR::RecognizedTypeInfo::Str: {
168207 return LookupCppType (context, {" std" , " string_view" });
169208 }
@@ -175,12 +214,13 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
175214 return clang::QualType ();
176215}
177216
178- // Maps a non-wrapper (no const or pointer) Carbon type to a C++ type. Returns a
179- // null QualType if the type is not supported.
217+ // Maps a Carbon type to a C++ type. Either returns the mapped type, a null type
218+ // as a placeholder indicating the type can't be mapped, or a `WrappedType`
219+ // representing a type that needs more work before it can be mapped.
180220// TODO: Have both Carbon -> C++ and C++ -> Carbon mappings in a single place
181221// to keep them in sync.
182- static auto MapNonWrapperType (Context& context, SemIR::InstId inst_id,
183- SemIR::TypeId type_id) -> clang::QualType {
222+ static auto TryMapType (Context& context, SemIR::TypeId type_id)
223+ -> TryMapTypeResult {
184224 auto type_inst = context.types ().GetAsInst (type_id);
185225
186226 CARBON_KIND_SWITCH (type_inst) {
@@ -193,67 +233,62 @@ static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
193233 case CARBON_KIND (SemIR::ClassType class_type): {
194234 return TryMapClassType (context, class_type);
195235 }
196- case SemIR::IntLiteralType::Kind : {
197- IntId bit_width_id = FindIntLiteralBitWidth (context, inst_id);
198- if (bit_width_id == IntId::None) {
199- return clang::QualType ();
200- }
201- return context. ast_context (). getIntTypeForBitwidth (bit_width_id. AsValue (),
202- true ) ;
236+ case CARBON_KIND ( SemIR::ConstType const_type) : {
237+ return WrappedType{
238+ . inner_type_id =
239+ context. types (). GetTypeIdForTypeInstId (const_type. inner_id ),
240+ . wrap_fn = [](Context& /* context */ , clang::QualType inner_type) {
241+ return inner_type. withConst ();
242+ }} ;
203243 }
204244 case SemIR::FloatLiteralType::Kind: {
205245 return context.ast_context ().DoubleTy ;
206246 }
247+ case CARBON_KIND (SemIR::PointerType pointer_type): {
248+ return WrappedType{
249+ .inner_type_id =
250+ context.types ().GetTypeIdForTypeInstId (pointer_type.pointee_id ),
251+ .wrap_fn = [](Context& context, clang::QualType inner_type) {
252+ auto pointer_type =
253+ context.ast_context ().getPointerType (inner_type);
254+ return context.ast_context ().getAttributedType (
255+ clang::attr::TypeNonNull, pointer_type, pointer_type);
256+ }};
257+ }
258+
207259 default : {
208260 return clang::QualType ();
209261 }
210262 }
263+
264+ return clang::QualType ();
211265}
212266
213- // Maps a Carbon type to a C++ type. Accepts an InstId, representing a value
214- // whose type is mapped to a C++ type. Returns `clang::QualType` if the mapping
267+ // Maps a Carbon type to a C++ type. Returns `clang::QualType` if the mapping
215268// succeeds, or `clang::QualType::isNull()` if the type is not supported.
216269// TODO: unify this with the C++ to Carbon type mapping function.
217- static auto MapToCppType (Context& context, SemIR::InstId inst_id )
270+ static auto MapToCppType (Context& context, SemIR::TypeId type_id )
218271 -> clang::QualType {
219- auto type_id = context.insts ().Get (inst_id).type_id ();
220- llvm::SmallVector<SemIR::TypeId> wrapper_types;
272+ llvm::SmallVector<WrapFn> wrap_fns;
221273 while (true ) {
222- SemIR::TypeId orig_type_id = type_id;
223- if (auto const_type = context.types ().TryGetAs <SemIR::ConstType>(type_id);
224- const_type) {
225- type_id = context.types ().GetTypeIdForTypeInstId (const_type->inner_id );
226- } else if (auto pointer_type =
227- context.types ().TryGetAs <SemIR::PointerType>(type_id);
228- pointer_type) {
229- type_id =
230- context.types ().GetTypeIdForTypeInstId (pointer_type->pointee_id );
231- } else {
232- break ;
233- }
234- wrapper_types.push_back (orig_type_id);
235- }
236-
237- clang::QualType mapped_type = MapNonWrapperType (context, inst_id, type_id);
238- if (mapped_type.isNull ()) {
239- return mapped_type;
240- }
274+ CARBON_KIND_SWITCH (TryMapType (context, type_id)) {
275+ case CARBON_KIND (clang::QualType type): {
276+ for (auto wrap_fn : llvm::reverse (wrap_fns)) {
277+ if (type.isNull ()) {
278+ break ;
279+ }
280+ type = wrap_fn (context, type);
281+ }
282+ return type;
283+ }
241284
242- for (auto wrapper_type_id : llvm::reverse (wrapper_types)) {
243- if (auto const_type =
244- context.types ().TryGetAs <SemIR::ConstType>(wrapper_type_id);
245- const_type) {
246- mapped_type.addConst ();
247- } else if (context.types ().TryGetAs <SemIR::PointerType>(wrapper_type_id)) {
248- auto pointer_type = context.ast_context ().getPointerType (mapped_type);
249- mapped_type = context.ast_context ().getAttributedType (
250- clang::attr::TypeNonNull, pointer_type, pointer_type);
251- } else {
252- return clang::QualType ();
285+ case CARBON_KIND (WrappedType wrapped): {
286+ wrap_fns.push_back (wrapped.wrap_fn );
287+ type_id = wrapped.inner_type_id ;
288+ break ;
289+ }
253290 }
254291 }
255-
256- return mapped_type;
257292}
258293
259294auto InventClangArg (Context& context, SemIR::InstId arg_id) -> clang::Expr* {
@@ -296,7 +331,25 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
296331 return nullptr ;
297332 }
298333
299- clang::QualType arg_cpp_type = MapToCppType (context, arg_id);
334+ clang::QualType arg_cpp_type;
335+
336+ // Special case: if the argument is an integer literal, look at its value.
337+ // TODO: Consider producing a `clang::IntegerLiteral` in this case instead, so
338+ // that C++ overloads that behave differently for zero-valued int literals can
339+ // recognize it.
340+ auto type_id = context.insts ().Get (arg_id).type_id ();
341+ if (context.types ().Is <SemIR::IntLiteralType>(type_id)) {
342+ IntId bit_width_id = FindIntLiteralBitWidth (context, arg_id);
343+ if (bit_width_id != IntId::None) {
344+ arg_cpp_type = context.ast_context ().getIntTypeForBitwidth (
345+ bit_width_id.AsValue (), true );
346+ }
347+ }
348+
349+ if (arg_cpp_type.isNull ()) {
350+ arg_cpp_type = MapToCppType (context, type_id);
351+ }
352+
300353 if (arg_cpp_type.isNull ()) {
301354 CARBON_DIAGNOSTIC (CppCallArgTypeNotSupported, Error,
302355 " call argument of type {0} is not supported" ,
0 commit comments