2828
2929namespace Carbon ::Check {
3030
31+ // A function that wraps a C++ type to form another C++ type. Note that this is
32+ // a raw function pointer; we don't currently use any lambda captures here. This
33+ // can be replaced by a `std::function` if captures are found to be needed.
34+ using WrapFn = auto (*)(Context& context, clang::QualType inner_type)
35+ -> clang::QualType;
36+
37+ // Represents a type that requires a subtype to be mapped into a Clang type
38+ // before it can be mapped.
39+ struct WrappedType {
40+ // The type contained in this wrapped type.
41+ SemIR::TypeId inner_type_id;
42+ // A function to construct the wrapped type from the mapped unwrapped type.
43+ WrapFn wrap_fn;
44+ };
45+
46+ // Possible results from attempting to map a type. A null QualType indicates
47+ // that the type couldn't be mapped.
48+ using TryMapTypeResult = std::variant<clang::QualType, WrappedType>;
49+
3150// Find the bit width of an integer literal. Following the C++ standard rules
3251// for assigning a type to a decimal integer literal, the first signed integer
3352// in which the value could fit among bit widths of 32, 64 and 128 is selected.
@@ -44,9 +63,12 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
4463 // TODO: Add tests for these cases.
4564 return IntId::None;
4665 }
47- auto arg = context.insts ().GetAs <SemIR::IntValue>(
66+ auto arg = context.insts ().TryGetAs <SemIR::IntValue>(
4867 context.constant_values ().GetInstId (arg_const_id));
49- llvm::APInt arg_val = context.ints ().Get (arg.int_id );
68+ if (!arg) {
69+ return IntId::None;
70+ }
71+ llvm::APInt arg_val = context.ints ().Get (arg->int_id );
5072 int arg_non_sign_bits = arg_val.getSignificantBits () - 1 ;
5173
5274 if (arg_non_sign_bits >= 128 ) {
@@ -55,7 +77,7 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
5577 " integer type; requires {1} bits, but max is 128" ,
5678 TypedInt, int );
5779 context.emitter ().Emit (arg_id, IntTooLargeForCppType,
58- {.type = arg. type_id , .value = arg_val},
80+ {.type = arg-> type_id , .value = arg_val},
5981 arg_non_sign_bits + 1 );
6082 return IntId::None;
6183 }
@@ -103,7 +125,7 @@ static auto LookupCppType(
103125// Maps a Carbon class type to a C++ type. Returns a null `QualType` if the
104126// type is not supported.
105127static auto TryMapClassType (Context& context, SemIR::ClassType class_type)
106- -> clang::QualType {
128+ -> TryMapTypeResult {
107129 clang::ASTContext& ast_context = context.ast_context ();
108130
109131 // If the class was imported from C++, return the original C++ type.
@@ -126,10 +148,10 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
126148 break ;
127149 }
128150 case SemIR::RecognizedTypeInfo::Numeric: {
129- // Carbon supports large bit width beyond C++ builtins; we don't need to
130- // translate those.
151+ // Carbon supports large bit width beyond C++ builtins; we don't translate
152+ // those into integer types .
131153 if (!type_info.numeric .bit_width_id .is_embedded_value ()) {
132- return clang::QualType () ;
154+ break ;
133155 }
134156 int bit_width = type_info.numeric .bit_width_id .AsValue ();
135157
@@ -164,6 +186,25 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
164186 case SemIR::RecognizedTypeInfo::CppVoidBase: {
165187 return ast_context.VoidTy ;
166188 }
189+ case SemIR::RecognizedTypeInfo::Optional: {
190+ auto args = context.inst_blocks ().GetOrEmpty (type_info.args_id );
191+ if (args.size () == 1 ) {
192+ auto arg_id = args[0 ];
193+ if (auto facet = context.insts ().TryGetAs <SemIR::FacetValue>(arg_id)) {
194+ arg_id = facet->type_inst_id ;
195+ }
196+ if (auto pointer_type =
197+ context.insts ().TryGetAs <SemIR::PointerType>(arg_id)) {
198+ return WrappedType{
199+ .inner_type_id = context.types ().GetTypeIdForTypeInstId (
200+ pointer_type->pointee_id ),
201+ .wrap_fn = [](Context& context, clang::QualType inner_type) {
202+ return context.ast_context ().getPointerType (inner_type);
203+ }};
204+ }
205+ }
206+ break ;
207+ }
167208 case SemIR::RecognizedTypeInfo::Str: {
168209 return LookupCppType (context, {" std" , " string_view" });
169210 }
@@ -175,12 +216,13 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
175216 return clang::QualType ();
176217}
177218
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.
219+ // Maps a Carbon type to a C++ type. Either returns the mapped type, a null type
220+ // as a placeholder indicating the type can't be mapped, or a `WrappedType`
221+ // representing a type that needs more work before it can be mapped.
180222// TODO: Have both Carbon -> C++ and C++ -> Carbon mappings in a single place
181223// to keep them in sync.
182- static auto MapNonWrapperType (Context& context, SemIR::InstId inst_id,
183- SemIR::TypeId type_id) -> clang::QualType {
224+ static auto TryMapType (Context& context, SemIR::TypeId type_id)
225+ -> TryMapTypeResult {
184226 auto type_inst = context.types ().GetAsInst (type_id);
185227
186228 CARBON_KIND_SWITCH (type_inst) {
@@ -193,67 +235,62 @@ static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
193235 case CARBON_KIND (SemIR::ClassType class_type): {
194236 return TryMapClassType (context, class_type);
195237 }
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 ) ;
238+ case CARBON_KIND ( SemIR::ConstType const_type) : {
239+ return WrappedType{
240+ . inner_type_id =
241+ context. types (). GetTypeIdForTypeInstId (const_type. inner_id ),
242+ . wrap_fn = [](Context& /* context */ , clang::QualType inner_type) {
243+ return inner_type. withConst ();
244+ }} ;
203245 }
204246 case SemIR::FloatLiteralType::Kind: {
205247 return context.ast_context ().DoubleTy ;
206248 }
249+ case CARBON_KIND (SemIR::PointerType pointer_type): {
250+ return WrappedType{
251+ .inner_type_id =
252+ context.types ().GetTypeIdForTypeInstId (pointer_type.pointee_id ),
253+ .wrap_fn = [](Context& context, clang::QualType inner_type) {
254+ auto pointer_type =
255+ context.ast_context ().getPointerType (inner_type);
256+ return context.ast_context ().getAttributedType (
257+ clang::attr::TypeNonNull, pointer_type, pointer_type);
258+ }};
259+ }
260+
207261 default : {
208262 return clang::QualType ();
209263 }
210264 }
265+
266+ return clang::QualType ();
211267}
212268
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
269+ // Maps a Carbon type to a C++ type. Returns `clang::QualType` if the mapping
215270// succeeds, or `clang::QualType::isNull()` if the type is not supported.
216271// TODO: unify this with the C++ to Carbon type mapping function.
217- static auto MapToCppType (Context& context, SemIR::InstId inst_id )
272+ static auto MapToCppType (Context& context, SemIR::TypeId type_id )
218273 -> clang::QualType {
219- auto type_id = context.insts ().Get (inst_id).type_id ();
220- llvm::SmallVector<SemIR::TypeId> wrapper_types;
274+ llvm::SmallVector<WrapFn> wrap_fns;
221275 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- }
276+ CARBON_KIND_SWITCH (TryMapType (context, type_id)) {
277+ case CARBON_KIND (clang::QualType type): {
278+ for (auto wrap_fn : llvm::reverse (wrap_fns)) {
279+ if (type.isNull ()) {
280+ break ;
281+ }
282+ type = wrap_fn (context, type);
283+ }
284+ return type;
285+ }
241286
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 ();
287+ case CARBON_KIND (WrappedType wrapped): {
288+ wrap_fns.push_back (wrapped.wrap_fn );
289+ type_id = wrapped.inner_type_id ;
290+ break ;
291+ }
253292 }
254293 }
255-
256- return mapped_type;
257294}
258295
259296auto InventClangArg (Context& context, SemIR::InstId arg_id) -> clang::Expr* {
@@ -296,7 +333,25 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
296333 return nullptr ;
297334 }
298335
299- clang::QualType arg_cpp_type = MapToCppType (context, arg_id);
336+ clang::QualType arg_cpp_type;
337+
338+ // Special case: if the argument is an integer literal, look at its value.
339+ // TODO: Consider producing a `clang::IntegerLiteral` in this case instead, so
340+ // that C++ overloads that behave differently for zero-valued int literals can
341+ // recognize it.
342+ auto type_id = context.insts ().Get (arg_id).type_id ();
343+ if (context.types ().Is <SemIR::IntLiteralType>(type_id)) {
344+ IntId bit_width_id = FindIntLiteralBitWidth (context, arg_id);
345+ if (bit_width_id != IntId::None) {
346+ arg_cpp_type = context.ast_context ().getIntTypeForBitwidth (
347+ bit_width_id.AsValue (), true );
348+ }
349+ }
350+
351+ if (arg_cpp_type.isNull ()) {
352+ arg_cpp_type = MapToCppType (context, type_id);
353+ }
354+
300355 if (arg_cpp_type.isNull ()) {
301356 CARBON_DIAGNOSTIC (CppCallArgTypeNotSupported, Error,
302357 " call argument of type {0} is not supported" ,
0 commit comments