Просмотр исходного кода

Add support for f16, f64, f128. (#5952)

Generalize the f64 support to support other sizes. Also provide interop
support for `float`, `_Float16`, and `__float128`.

Also lay some groundwork for non-standard floating-point types, though
we don't have any syntax to name them yet.
Richard Smith 8 месяцев назад
Родитель
Сommit
b851e8c423
27 измененных файлов с 825 добавлено и 378 удалено
  1. 32 14
      toolchain/check/eval.cpp
  2. 1 1
      toolchain/check/eval_inst.cpp
  3. 1 2
      toolchain/check/handle_literal.cpp
  4. 10 9
      toolchain/check/import_cpp.cpp
  5. 3 2
      toolchain/check/import_ref.cpp
  6. 1 3
      toolchain/check/literal.cpp
  7. 1 2
      toolchain/check/literal.h
  8. 0 7
      toolchain/check/testdata/builtins/float/add_assign.carbon
  9. 163 206
      toolchain/check/testdata/builtins/float/convert_checked.carbon
  10. 0 7
      toolchain/check/testdata/builtins/float/div_assign.carbon
  11. 10 7
      toolchain/check/testdata/builtins/float/make_type.carbon
  12. 0 7
      toolchain/check/testdata/builtins/float/mul_assign.carbon
  13. 0 7
      toolchain/check/testdata/builtins/float/sub_assign.carbon
  14. 3 3
      toolchain/check/testdata/deduce/int_float.carbon
  15. 286 0
      toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon
  16. 14 14
      toolchain/check/testdata/interop/cpp/function/param_unsupported.carbon
  17. 5 5
      toolchain/check/testdata/primitives/numeric_literals.carbon
  18. 56 34
      toolchain/check/testdata/primitives/type_literals.carbon
  19. 45 20
      toolchain/check/type.cpp
  20. 4 7
      toolchain/check/type.h
  21. 1 0
      toolchain/diagnostics/diagnostic_kind.def
  22. 3 3
      toolchain/lower/file_context.cpp
  23. 64 0
      toolchain/lower/testdata/builtins/float.carbon
  24. 34 16
      toolchain/lower/testdata/builtins/types.carbon
  25. 48 0
      toolchain/sem_ir/ids.cpp
  26. 39 2
      toolchain/sem_ir/ids.h
  27. 1 0
      toolchain/sem_ir/typed_insts.h

+ 32 - 14
toolchain/check/eval.cpp

@@ -1005,8 +1005,9 @@ static auto MakeFloatTypeResult(Context& context, SemIR::LocId loc_id,
     -> SemIR::ConstantId {
   auto result = SemIR::FloatType{
       .type_id = GetSingletonType(context, SemIR::TypeType::TypeInstId),
-      .bit_width_id = width_id};
-  if (!ValidateFloatType(context, loc_id, result)) {
+      .bit_width_id = width_id,
+      .float_kind = SemIR::FloatKind::None};
+  if (!ValidateFloatTypeAndSetKind(context, loc_id, result)) {
     return SemIR::ErrorInst::ConstantId;
   }
   return MakeConstantResult(context, result, phase);
@@ -1078,9 +1079,17 @@ static auto PerformCheckedFloatConvert(Context& context, SemIR::LocId loc_id,
                                        SemIR::InstId arg_id,
                                        SemIR::TypeId dest_type_id)
     -> SemIR::ConstantId {
+  auto dest_type_object_rep_id = context.types().GetObjectRepr(dest_type_id);
+  CARBON_CHECK(dest_type_object_rep_id.has_value(),
+               "Conversion to incomplete type");
+  auto dest_float_type =
+      context.types().TryGetAs<SemIR::FloatType>(dest_type_object_rep_id);
+  CARBON_CHECK(dest_float_type || context.types().Is<SemIR::FloatLiteralType>(
+                                      dest_type_object_rep_id));
+
   if (auto literal =
           context.insts().TryGetAs<SemIR::FloatLiteralValue>(arg_id)) {
-    if (context.types().Is<SemIR::FloatLiteralType>(dest_type_id)) {
+    if (!dest_float_type) {
       return MakeConstantResult(
           context,
           SemIR::FloatLiteralValue{.type_id = dest_type_id,
@@ -1105,8 +1114,7 @@ static auto PerformCheckedFloatConvert(Context& context, SemIR::LocId loc_id,
     real_value.exponent.toStringSigned(str);
 
     // Convert the string to an APFloat.
-    // TODO: Compute the fltSemantics from the type.
-    llvm::APFloat result(llvm::APFloat::IEEEdouble());
+    llvm::APFloat result(dest_float_type->float_kind.Semantics());
     // TODO: The implementation of this conversion effectively converts back to
     // APInts, but unfortunately the conversion from integer mantissa and
     // exponent in IEEEFloat::roundSignificandWithExponent is not part of the
@@ -1119,27 +1127,37 @@ static auto PerformCheckedFloatConvert(Context& context, SemIR::LocId loc_id,
                    toString(std::move(error)));
     }
     if (status.get() & llvm::APFloat::opOverflow) {
-      CARBON_DIAGNOSTIC(FloatTooLargeForType, Error,
+      CARBON_DIAGNOSTIC(FloatLiteralTooLargeForType, Error,
                         "value {0} too large for floating-point type {1}",
                         RealId, SemIR::TypeId);
-      context.emitter().Emit(loc_id, FloatTooLargeForType, literal->real_id,
-                             dest_type_id);
+      context.emitter().Emit(loc_id, FloatLiteralTooLargeForType,
+                             literal->real_id, dest_type_id);
       return SemIR::ErrorInst::ConstantId;
     }
     return MakeFloatResult(context, dest_type_id, std::move(result));
   }
 
-  // TODO: Perform a conversion if necessary.
-  if (context.types().Is<SemIR::FloatLiteralType>(dest_type_id)) {
+  if (!dest_float_type) {
     context.TODO(loc_id, "conversion from float to float literal");
     return SemIR::ErrorInst::ConstantId;
   }
 
+  // Convert to the destination float semantics.
   auto arg = context.insts().GetAs<SemIR::FloatValue>(arg_id);
-  return MakeConstantResult(
-      context,
-      SemIR::FloatValue{.type_id = dest_type_id, .float_id = arg.float_id},
-      Phase::Concrete);
+  llvm::APFloat result = context.floats().Get(arg.float_id);
+  bool loses_info;
+  auto status = result.convert(dest_float_type->float_kind.Semantics(),
+                               llvm::APFloat::rmNearestTiesToEven, &loses_info);
+  if (status & llvm::APFloat::opOverflow) {
+    CARBON_DIAGNOSTIC(FloatTooLargeForType, Error,
+                      "value {0} too large for floating-point type {1}",
+                      llvm::APFloat, SemIR::TypeId);
+    context.emitter().Emit(loc_id, FloatTooLargeForType,
+                           context.floats().Get(arg.float_id), dest_type_id);
+    return SemIR::ErrorInst::ConstantId;
+  }
+
+  return MakeFloatResult(context, dest_type_id, std::move(result));
 }
 
 // Issues a diagnostic for a compile-time division by zero.

+ 1 - 1
toolchain/check/eval_inst.cpp

@@ -205,7 +205,7 @@ auto EvalConstantInst(Context& context, SemIR::FacetAccessType inst)
 
 auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
                       SemIR::FloatType inst) -> ConstantEvalResult {
-  return ValidateFloatType(context, SemIR::LocId(inst_id), inst)
+  return ValidateFloatTypeAndSetKind(context, SemIR::LocId(inst_id), inst)
              ? ConstantEvalResult::NewSamePhase(inst)
              : ConstantEvalResult::Error;
 }

+ 1 - 2
toolchain/check/handle_literal.cpp

@@ -121,8 +121,7 @@ auto HandleParseNode(Context& context, Parse::FloatTypeLiteralId node_id)
     -> bool {
   auto tok_id = context.parse_tree().node_token(node_id);
   auto size_id = context.tokens().GetTypeLiteralSize(tok_id);
-  auto type_inst_id =
-      MakeFloatTypeLiteral(context, node_id, SemIR::FloatKind::None, size_id);
+  auto type_inst_id = MakeFloatTypeLiteral(context, node_id, size_id);
   context.node_stack().Push(node_id, type_inst_id);
   return true;
 }

+ 10 - 9
toolchain/check/import_cpp.cpp

@@ -1071,15 +1071,16 @@ static auto MapBuiltinType(Context& context, SemIR::LocId loc_id,
       return type_expr;
     }
     // TODO: Handle integer types that map to named aliases.
-  } else if (type.isDoubleType()) {
-    // TODO: Handle other floating point types when Carbon supports fN where N
-    // != 64.
-    CARBON_CHECK(ast_context.getTypeSize(qual_type) == 64);
-    CARBON_CHECK(ast_context.hasSameType(qual_type, ast_context.DoubleTy));
-    return ExprAsType(
-        context, Parse::NodeId::None,
-        MakeFloatTypeLiteral(context, Parse::NodeId::None,
-                             SemIR::FloatKind::None, context.ints().Add(64)));
+  } else if (type.isFloatingPoint()) {
+    if (type.isFloat16Type() || type.isFloat32Type() || type.isDoubleType() ||
+        type.isFloat128Type()) {
+      return ExprAsType(
+          context, Parse::NodeId::None,
+          MakeFloatTypeLiteral(
+              context, Parse::NodeId::None,
+              context.ints().Add(ast_context.getTypeSize(qual_type))));
+    }
+    // TODO: Handle floating-point types that map to named aliases.
   }
 
   return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};

+ 3 - 2
toolchain/check/import_ref.cpp

@@ -1882,8 +1882,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   return ResolveAsDeduplicated<SemIR::FloatType>(
-      resolver,
-      {.type_id = SemIR::TypeType::TypeId, .bit_width_id = bit_width_id});
+      resolver, {.type_id = SemIR::TypeType::TypeId,
+                 .bit_width_id = bit_width_id,
+                 .float_kind = inst.float_kind});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,

+ 1 - 3
toolchain/check/literal.cpp

@@ -38,9 +38,7 @@ auto MakeIntType(Context& context, Parse::NodeId node_id,
 }
 
 auto MakeFloatTypeLiteral(Context& context, Parse::NodeId node_id,
-                          SemIR::FloatKind float_kind, IntId size_id)
-    -> SemIR::InstId {
-  CARBON_CHECK(float_kind == SemIR::FloatKind::None);
+                          IntId size_id) -> SemIR::InstId {
   auto width_id = MakeIntLiteral(context, node_id, size_id);
   auto fn_inst_id = LookupNameInCore(context, node_id, "Float");
   return PerformCall(context, node_id, fn_inst_id, {width_id});

+ 1 - 2
toolchain/check/literal.h

@@ -27,8 +27,7 @@ auto MakeIntType(Context& context, Parse::NodeId node_id,
 
 // Forms a floating point type expression for `fN` literal.
 auto MakeFloatTypeLiteral(Context& context, Parse::NodeId node_id,
-                          SemIR::FloatKind float_kind, IntId size_id)
-    -> SemIR::InstId;
+                          IntId size_id) -> SemIR::InstId;
 
 }  // namespace Carbon::Check
 

+ 0 - 7
toolchain/check/testdata/builtins/float/add_assign.carbon

@@ -30,13 +30,6 @@ library "[[@TEST_NAME]]";
 // CHECK:STDERR:
 fn NotPtr(a: f64, b: f64) = "float.add_assign";
 
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+8]]:24: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.add_assign";
-// CHECK:STDERR:                        ^~~~~~
-// CHECK:STDERR:
 // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add_assign" [InvalidBuiltinSignature]
 // CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.add_assign";
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 163 - 206
toolchain/check/testdata/builtins/float/convert_checked.carbon

@@ -37,43 +37,16 @@ fn Float64ToFloat64(a: f64) -> f64 = "float.convert_checked";
 fn Float64ToFloatLiteral(a: f64) -> FloatLiteral() = "float.convert_checked";
 fn FloatLiteralToFloat64(a: FloatLiteral()) -> f64 = "float.convert_checked";
 
-// --- fail_todo_f32.carbon
+// --- f32.carbon
 
 library "[[@TEST_NAME]]";
 export import library "literal";
 
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_f32.carbon:[[@LINE+8]]:29: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: fn Float32ToFloat32(a: f32) -> f32 = "float.convert_checked";
-// CHECK:STDERR:                             ^~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_f32.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.convert_checked" [InvalidBuiltinSignature]
-// CHECK:STDERR: fn Float32ToFloat32(a: f32) -> f32 = "float.convert_checked";
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 fn Float32ToFloat32(a: f32) -> f32 = "float.convert_checked";
-// CHECK:STDERR: fail_todo_f32.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.convert_checked" [InvalidBuiltinSignature]
-// CHECK:STDERR: fn Float32ToFloatLiteral(a: f32) -> FloatLiteral() = "float.convert_checked";
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 fn Float32ToFloatLiteral(a: f32) -> FloatLiteral() = "float.convert_checked";
-// CHECK:STDERR: fail_todo_f32.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.convert_checked" [InvalidBuiltinSignature]
-// CHECK:STDERR: fn FloatLiteralToFloat32(a: FloatLiteral()) -> f32 = "float.convert_checked";
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 fn FloatLiteralToFloat32(a: FloatLiteral()) -> f32 = "float.convert_checked";
 
-// CHECK:STDERR: fail_todo_f32.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.convert_checked" [InvalidBuiltinSignature]
-// CHECK:STDERR: fn Float32ToFloat64(a: f32) -> f64 = "float.convert_checked";
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 fn Float32ToFloat64(a: f32) -> f64 = "float.convert_checked";
-// CHECK:STDERR: fail_todo_f32.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.convert_checked" [InvalidBuiltinSignature]
-// CHECK:STDERR: fn Float64ToFloat32(a: f64) -> f32 = "float.convert_checked";
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 fn Float64ToFloat32(a: f64) -> f32 = "float.convert_checked";
 
 // --- literal_f64.carbon
@@ -87,19 +60,12 @@ let b: f64 = FloatLiteralToFloat64(1.0);
 let c: f64 = FloatLiteralToFloat64(1.0e308);
 //@dump-sem-ir-end
 
-// --- fail_todo_literal_f32.carbon
+// --- literal_f32.carbon
 
 library "[[@TEST_NAME]]";
 import library "f32";
 
 //@dump-sem-ir-begin
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_literal_f32.carbon:[[@LINE+4]]:8: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: let a: f32 = FloatLiteralToFloat32(0.0);
-// CHECK:STDERR:        ^~~
-// CHECK:STDERR:
 let a: f32 = FloatLiteralToFloat32(0.0);
 let b: f32 = FloatLiteralToFloat32(1.0);
 let c: f32 = FloatLiteralToFloat32(1.0e38);
@@ -116,37 +82,23 @@ let b: f64 = Float64ToFloat64(1.0);
 let c: f64 = Float64ToFloat64(1.0e308);
 //@dump-sem-ir-end
 
-// --- fail_todo_identity_f32.carbon
+// --- identity_f32.carbon
 
 library "[[@TEST_NAME]]";
 import library "f32";
 
 //@dump-sem-ir-begin
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_identity_f32.carbon:[[@LINE+4]]:8: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: let a: f32 = Float32ToFloat32(0.0);
-// CHECK:STDERR:        ^~~
-// CHECK:STDERR:
 let a: f32 = Float32ToFloat32(0.0);
 let b: f32 = Float32ToFloat32(1.0);
 let c: f32 = Float32ToFloat32(1.0e38);
 //@dump-sem-ir-end
 
-// --- fail_todo_truncate.carbon
+// --- truncate.carbon
 
 library "[[@TEST_NAME]]";
 import library "f32";
 
 //@dump-sem-ir-begin
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_truncate.carbon:[[@LINE+4]]:8: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: let a: f32 = Float64ToFloat32(1.0);
-// CHECK:STDERR:        ^~~
-// CHECK:STDERR:
 let a: f32 = Float64ToFloat32(1.0);
 //@dump-sem-ir-end
 
@@ -157,18 +109,17 @@ import library "f32";
 import library "f64";
 
 //@dump-sem-ir-begin
-// TODO: These should all produce an error that the source value doesn't fit in
-// the destination type.
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:8: note: in `f32` used here [ResolvingSpecificHere]
+// CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 9.9999999999999994E+38 too large for floating-point type `f32` [FloatTooLargeForType]
 // CHECK:STDERR: let a: f32 = Float64ToFloat32(1.0e39);
-// CHECK:STDERR:        ^~~
+// CHECK:STDERR:              ^~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 let a: f32 = Float64ToFloat32(1.0e39);
+// CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 10*10^38 too large for floating-point type `f32` [FloatLiteralTooLargeForType]
+// CHECK:STDERR: let b: f32 = FloatLiteralToFloat32(1.0e39);
+// CHECK:STDERR:              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
 let b: f32 = FloatLiteralToFloat32(1.0e39);
-// CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 10*10^308 too large for floating-point type `f64` [FloatTooLargeForType]
+// CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 10*10^308 too large for floating-point type `f64` [FloatLiteralTooLargeForType]
 // CHECK:STDERR: let c: f64 = FloatLiteralToFloat64(1.0e309);
 // CHECK:STDERR:              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
@@ -309,17 +260,20 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_literal_f32.carbon
+// CHECK:STDOUT: --- literal_f32.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %f32: type = class_type @Float, @Float(%int_32) [concrete]
-// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32.97e [concrete]
 // CHECK:STDOUT:   %FloatLiteralToFloat32.type: type = fn_type @FloatLiteralToFloat32 [concrete]
 // CHECK:STDOUT:   %FloatLiteralToFloat32: %FloatLiteralToFloat32.type = struct_value () [concrete]
 // CHECK:STDOUT:   %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
+// CHECK:STDOUT:   %float.4db: %f32.97e = float_value 0 [concrete]
 // CHECK:STDOUT:   %float.674: Core.FloatLiteral = float_literal_value 10e-1 [concrete]
+// CHECK:STDOUT:   %float.e3b: %f32.97e = float_value 1 [concrete]
 // CHECK:STDOUT:   %float.516: Core.FloatLiteral = float_literal_value 10e37 [concrete]
+// CHECK:STDOUT:   %float.520: %f32.97e = float_value 9.99999968E+37 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -330,46 +284,46 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %a.patt: %pattern_type.201 = binding_pattern a [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_8: type = splice_block %f32.loc13 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc13: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc13: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc6_8: type = splice_block %f32.loc6 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc6: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc6: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_39.1: %f32 = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc13
-// CHECK:STDOUT:   %.loc13_39.2: %f32 = converted @__global_init.%FloatLiteralToFloat32.call.loc13, %.loc13_39.1
-// CHECK:STDOUT:   %a: %f32 = bind_name a, %.loc13_39.2
+// CHECK:STDOUT:   %.loc6_39.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc6 [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %.loc6_39.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call.loc6, %.loc6_39.1 [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %a: %f32.97e = bind_name a, %.loc6_39.2
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %b.patt: %pattern_type.201 = binding_pattern b [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc14_8: type = splice_block %f32.loc14 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc14: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc14: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc7_8: type = splice_block %f32.loc7 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc7: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc7: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc14_39.1: %f32 = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc14
-// CHECK:STDOUT:   %.loc14_39.2: %f32 = converted @__global_init.%FloatLiteralToFloat32.call.loc14, %.loc14_39.1
-// CHECK:STDOUT:   %b: %f32 = bind_name b, %.loc14_39.2
+// CHECK:STDOUT:   %.loc7_39.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc7 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc7_39.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call.loc7, %.loc7_39.1 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %b: %f32.97e = bind_name b, %.loc7_39.2
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %c.patt: %pattern_type.201 = binding_pattern c [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc15_8: type = splice_block %f32.loc15 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc15: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc15: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc8_8: type = splice_block %f32.loc8 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc8: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc8: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc15_42.1: %f32 = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc15
-// CHECK:STDOUT:   %.loc15_42.2: %f32 = converted @__global_init.%FloatLiteralToFloat32.call.loc15, %.loc15_42.1
-// CHECK:STDOUT:   %c: %f32 = bind_name c, %.loc15_42.2
+// CHECK:STDOUT:   %.loc8_42.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc8 [concrete = constants.%float.520]
+// CHECK:STDOUT:   %.loc8_42.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call.loc8, %.loc8_42.1 [concrete = constants.%float.520]
+// CHECK:STDOUT:   %c: %f32.97e = bind_name c, %.loc8_42.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %FloatLiteralToFloat32.ref.loc13: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
-// CHECK:STDOUT:   %float.loc13: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7]
-// CHECK:STDOUT:   %FloatLiteralToFloat32.call.loc13: init %f32 = call %FloatLiteralToFloat32.ref.loc13(%float.loc13)
-// CHECK:STDOUT:   %FloatLiteralToFloat32.ref.loc14: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
-// CHECK:STDOUT:   %float.loc14: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.674]
-// CHECK:STDOUT:   %FloatLiteralToFloat32.call.loc14: init %f32 = call %FloatLiteralToFloat32.ref.loc14(%float.loc14)
-// CHECK:STDOUT:   %FloatLiteralToFloat32.ref.loc15: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
-// CHECK:STDOUT:   %float.loc15: Core.FloatLiteral = float_literal_value 10e37 [concrete = constants.%float.516]
-// CHECK:STDOUT:   %FloatLiteralToFloat32.call.loc15: init %f32 = call %FloatLiteralToFloat32.ref.loc15(%float.loc15)
+// CHECK:STDOUT:   %FloatLiteralToFloat32.ref.loc6: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
+// CHECK:STDOUT:   %float.loc6: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7]
+// CHECK:STDOUT:   %FloatLiteralToFloat32.call.loc6: init %f32.97e = call %FloatLiteralToFloat32.ref.loc6(%float.loc6) [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %FloatLiteralToFloat32.ref.loc7: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
+// CHECK:STDOUT:   %float.loc7: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.674]
+// CHECK:STDOUT:   %FloatLiteralToFloat32.call.loc7: init %f32.97e = call %FloatLiteralToFloat32.ref.loc7(%float.loc7) [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %FloatLiteralToFloat32.ref.loc8: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
+// CHECK:STDOUT:   %float.loc8: Core.FloatLiteral = float_literal_value 10e37 [concrete = constants.%float.516]
+// CHECK:STDOUT:   %FloatLiteralToFloat32.call.loc8: init %f32.97e = call %FloatLiteralToFloat32.ref.loc8(%float.loc8) [concrete = constants.%float.520]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -480,17 +434,17 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_identity_f32.carbon
+// CHECK:STDOUT: --- identity_f32.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %f32: type = class_type @Float, @Float(%int_32) [concrete]
-// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32.97e [concrete]
 // CHECK:STDOUT:   %Float32ToFloat32.type: type = fn_type @Float32ToFloat32 [concrete]
 // CHECK:STDOUT:   %Float32ToFloat32: %Float32ToFloat32.type = struct_value () [concrete]
 // CHECK:STDOUT:   %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete]
-// CHECK:STDOUT:   %ImplicitAs.type.31d: type = facet_type <@ImplicitAs, @ImplicitAs(%f32)> [concrete]
-// CHECK:STDOUT:   %ImplicitAs.Convert.type.b8c: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%f32) [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.31d: type = facet_type <@ImplicitAs, @ImplicitAs(%f32.97e)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.b8c: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%f32.97e) [concrete]
 // CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.644: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.b45: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.644 = struct_value () [symbolic]
@@ -502,15 +456,15 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.b37: <bound method> = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3 [concrete]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
 // CHECK:STDOUT:   %bound_method.91e: <bound method> = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %float.540: %f32 = float_value 0 [concrete]
+// CHECK:STDOUT:   %float.4db: %f32.97e = float_value 0 [concrete]
 // CHECK:STDOUT:   %float.674: Core.FloatLiteral = float_literal_value 10e-1 [concrete]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.6d0: <bound method> = bound_method %float.674, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3 [concrete]
 // CHECK:STDOUT:   %bound_method.6b4: <bound method> = bound_method %float.674, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %float.3b9: %f32 = float_value 1 [concrete]
+// CHECK:STDOUT:   %float.e3b: %f32.97e = float_value 1 [concrete]
 // CHECK:STDOUT:   %float.516: Core.FloatLiteral = float_literal_value 10e37 [concrete]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.dc9: <bound method> = bound_method %float.516, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3 [concrete]
 // CHECK:STDOUT:   %bound_method.3b8: <bound method> = bound_method %float.516, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %float.a77: %f32 = float_value 9.9999999999999997E+37 [concrete]
+// CHECK:STDOUT:   %float.520: %f32.97e = float_value 9.99999968E+37 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -523,76 +477,76 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %a.patt: %pattern_type.201 = binding_pattern a [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_8: type = splice_block %f32.loc13 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc13: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc13: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc6_8: type = splice_block %f32.loc6 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc6: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc6: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_34.1: %f32 = value_of_initializer @__global_init.%Float32ToFloat32.call.loc13
-// CHECK:STDOUT:   %.loc13_34.2: %f32 = converted @__global_init.%Float32ToFloat32.call.loc13, %.loc13_34.1
-// CHECK:STDOUT:   %a: %f32 = bind_name a, %.loc13_34.2
+// CHECK:STDOUT:   %.loc6_34.1: %f32.97e = value_of_initializer @__global_init.%Float32ToFloat32.call.loc6 [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %.loc6_34.2: %f32.97e = converted @__global_init.%Float32ToFloat32.call.loc6, %.loc6_34.1 [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %a: %f32.97e = bind_name a, %.loc6_34.2
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %b.patt: %pattern_type.201 = binding_pattern b [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc14_8: type = splice_block %f32.loc14 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc14: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc14: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc7_8: type = splice_block %f32.loc7 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc7: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc7: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc14_34.1: %f32 = value_of_initializer @__global_init.%Float32ToFloat32.call.loc14
-// CHECK:STDOUT:   %.loc14_34.2: %f32 = converted @__global_init.%Float32ToFloat32.call.loc14, %.loc14_34.1
-// CHECK:STDOUT:   %b: %f32 = bind_name b, %.loc14_34.2
+// CHECK:STDOUT:   %.loc7_34.1: %f32.97e = value_of_initializer @__global_init.%Float32ToFloat32.call.loc7 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc7_34.2: %f32.97e = converted @__global_init.%Float32ToFloat32.call.loc7, %.loc7_34.1 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %b: %f32.97e = bind_name b, %.loc7_34.2
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %c.patt: %pattern_type.201 = binding_pattern c [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc15_8: type = splice_block %f32.loc15 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc15: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc15: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc8_8: type = splice_block %f32.loc8 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc8: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc8: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc15_37.1: %f32 = value_of_initializer @__global_init.%Float32ToFloat32.call.loc15
-// CHECK:STDOUT:   %.loc15_37.2: %f32 = converted @__global_init.%Float32ToFloat32.call.loc15, %.loc15_37.1
-// CHECK:STDOUT:   %c: %f32 = bind_name c, %.loc15_37.2
+// CHECK:STDOUT:   %.loc8_37.1: %f32.97e = value_of_initializer @__global_init.%Float32ToFloat32.call.loc8 [concrete = constants.%float.520]
+// CHECK:STDOUT:   %.loc8_37.2: %f32.97e = converted @__global_init.%Float32ToFloat32.call.loc8, %.loc8_37.1 [concrete = constants.%float.520]
+// CHECK:STDOUT:   %c: %f32.97e = bind_name c, %.loc8_37.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Float32ToFloat32.ref.loc13: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32]
-// CHECK:STDOUT:   %float.loc13: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7]
-// CHECK:STDOUT:   %impl.elem0.loc13: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
-// CHECK:STDOUT:   %bound_method.loc13_31.1: <bound method> = bound_method %float.loc13, %impl.elem0.loc13 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.b37]
-// CHECK:STDOUT:   %specific_fn.loc13: <specific function> = specific_function %impl.elem0.loc13, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc13_31.2: <bound method> = bound_method %float.loc13, %specific_fn.loc13 [concrete = constants.%bound_method.91e]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc13: init %f32 = call %bound_method.loc13_31.2(%float.loc13) [concrete = constants.%float.540]
-// CHECK:STDOUT:   %.loc13_31.1: %f32 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc13 [concrete = constants.%float.540]
-// CHECK:STDOUT:   %.loc13_31.2: %f32 = converted %float.loc13, %.loc13_31.1 [concrete = constants.%float.540]
-// CHECK:STDOUT:   %Float32ToFloat32.call.loc13: init %f32 = call %Float32ToFloat32.ref.loc13(%.loc13_31.2)
-// CHECK:STDOUT:   %Float32ToFloat32.ref.loc14: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32]
-// CHECK:STDOUT:   %float.loc14: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.674]
-// CHECK:STDOUT:   %impl.elem0.loc14: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
-// CHECK:STDOUT:   %bound_method.loc14_31.1: <bound method> = bound_method %float.loc14, %impl.elem0.loc14 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.6d0]
-// CHECK:STDOUT:   %specific_fn.loc14: <specific function> = specific_function %impl.elem0.loc14, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc14_31.2: <bound method> = bound_method %float.loc14, %specific_fn.loc14 [concrete = constants.%bound_method.6b4]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc14: init %f32 = call %bound_method.loc14_31.2(%float.loc14) [concrete = constants.%float.3b9]
-// CHECK:STDOUT:   %.loc14_31.1: %f32 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc14 [concrete = constants.%float.3b9]
-// CHECK:STDOUT:   %.loc14_31.2: %f32 = converted %float.loc14, %.loc14_31.1 [concrete = constants.%float.3b9]
-// CHECK:STDOUT:   %Float32ToFloat32.call.loc14: init %f32 = call %Float32ToFloat32.ref.loc14(%.loc14_31.2)
-// CHECK:STDOUT:   %Float32ToFloat32.ref.loc15: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32]
-// CHECK:STDOUT:   %float.loc15: Core.FloatLiteral = float_literal_value 10e37 [concrete = constants.%float.516]
-// CHECK:STDOUT:   %impl.elem0.loc15: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
-// CHECK:STDOUT:   %bound_method.loc15_31.1: <bound method> = bound_method %float.loc15, %impl.elem0.loc15 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.dc9]
-// CHECK:STDOUT:   %specific_fn.loc15: <specific function> = specific_function %impl.elem0.loc15, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc15_31.2: <bound method> = bound_method %float.loc15, %specific_fn.loc15 [concrete = constants.%bound_method.3b8]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc15: init %f32 = call %bound_method.loc15_31.2(%float.loc15) [concrete = constants.%float.a77]
-// CHECK:STDOUT:   %.loc15_31.1: %f32 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc15 [concrete = constants.%float.a77]
-// CHECK:STDOUT:   %.loc15_31.2: %f32 = converted %float.loc15, %.loc15_31.1 [concrete = constants.%float.a77]
-// CHECK:STDOUT:   %Float32ToFloat32.call.loc15: init %f32 = call %Float32ToFloat32.ref.loc15(%.loc15_31.2)
+// CHECK:STDOUT:   %Float32ToFloat32.ref.loc6: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32]
+// CHECK:STDOUT:   %float.loc6: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7]
+// CHECK:STDOUT:   %impl.elem0.loc6: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
+// CHECK:STDOUT:   %bound_method.loc6_31.1: <bound method> = bound_method %float.loc6, %impl.elem0.loc6 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.b37]
+// CHECK:STDOUT:   %specific_fn.loc6: <specific function> = specific_function %impl.elem0.loc6, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc6_31.2: <bound method> = bound_method %float.loc6, %specific_fn.loc6 [concrete = constants.%bound_method.91e]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %f32.97e = call %bound_method.loc6_31.2(%float.loc6) [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %.loc6_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %.loc6_31.2: %f32.97e = converted %float.loc6, %.loc6_31.1 [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %Float32ToFloat32.call.loc6: init %f32.97e = call %Float32ToFloat32.ref.loc6(%.loc6_31.2) [concrete = constants.%float.4db]
+// CHECK:STDOUT:   %Float32ToFloat32.ref.loc7: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32]
+// CHECK:STDOUT:   %float.loc7: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.674]
+// CHECK:STDOUT:   %impl.elem0.loc7: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
+// CHECK:STDOUT:   %bound_method.loc7_31.1: <bound method> = bound_method %float.loc7, %impl.elem0.loc7 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.6d0]
+// CHECK:STDOUT:   %specific_fn.loc7: <specific function> = specific_function %impl.elem0.loc7, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc7_31.2: <bound method> = bound_method %float.loc7, %specific_fn.loc7 [concrete = constants.%bound_method.6b4]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %f32.97e = call %bound_method.loc7_31.2(%float.loc7) [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc7_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc7_31.2: %f32.97e = converted %float.loc7, %.loc7_31.1 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %Float32ToFloat32.call.loc7: init %f32.97e = call %Float32ToFloat32.ref.loc7(%.loc7_31.2) [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %Float32ToFloat32.ref.loc8: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32]
+// CHECK:STDOUT:   %float.loc8: Core.FloatLiteral = float_literal_value 10e37 [concrete = constants.%float.516]
+// CHECK:STDOUT:   %impl.elem0.loc8: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
+// CHECK:STDOUT:   %bound_method.loc8_31.1: <bound method> = bound_method %float.loc8, %impl.elem0.loc8 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.dc9]
+// CHECK:STDOUT:   %specific_fn.loc8: <specific function> = specific_function %impl.elem0.loc8, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_31.2: <bound method> = bound_method %float.loc8, %specific_fn.loc8 [concrete = constants.%bound_method.3b8]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %f32.97e = call %bound_method.loc8_31.2(%float.loc8) [concrete = constants.%float.520]
+// CHECK:STDOUT:   %.loc8_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%float.520]
+// CHECK:STDOUT:   %.loc8_31.2: %f32.97e = converted %float.loc8, %.loc8_31.1 [concrete = constants.%float.520]
+// CHECK:STDOUT:   %Float32ToFloat32.call.loc8: init %f32.97e = call %Float32ToFloat32.ref.loc8(%.loc8_31.2) [concrete = constants.%float.520]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_truncate.carbon
+// CHECK:STDOUT: --- truncate.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %f32: type = class_type @Float, @Float(%int_32) [concrete]
-// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32.97e [concrete]
 // CHECK:STDOUT:   %Float64ToFloat32.type: type = fn_type @Float64ToFloat32 [concrete]
 // CHECK:STDOUT:   %Float64ToFloat32: %Float64ToFloat32.type = struct_value () [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
@@ -612,6 +566,7 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.a03, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_64) [concrete]
 // CHECK:STDOUT:   %bound_method: <bound method> = bound_method %float.674, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %float.d20: %f64.d77 = float_value 1 [concrete]
+// CHECK:STDOUT:   %float.e3b: %f32.97e = float_value 1 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -624,13 +579,13 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %a.patt: %pattern_type.201 = binding_pattern a [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_8: type = splice_block %f32 [concrete = constants.%f32] {
+// CHECK:STDOUT:   %.loc6_8: type = splice_block %f32 [concrete = constants.%f32.97e] {
 // CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:     %f32: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_34.1: %f32 = value_of_initializer @__global_init.%Float64ToFloat32.call
-// CHECK:STDOUT:   %.loc13_34.2: %f32 = converted @__global_init.%Float64ToFloat32.call, %.loc13_34.1
-// CHECK:STDOUT:   %a: %f32 = bind_name a, %.loc13_34.2
+// CHECK:STDOUT:   %.loc6_34.1: %f32.97e = value_of_initializer @__global_init.%Float64ToFloat32.call [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc6_34.2: %f32.97e = converted @__global_init.%Float64ToFloat32.call, %.loc6_34.1 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %a: %f32.97e = bind_name a, %.loc6_34.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
@@ -638,13 +593,13 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   %Float64ToFloat32.ref: %Float64ToFloat32.type = name_ref Float64ToFloat32, imports.%Main.Float64ToFloat32 [concrete = constants.%Float64ToFloat32]
 // CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.674]
 // CHECK:STDOUT:   %impl.elem0: %.678 = impl_witness_access constants.%ImplicitAs.impl_witness.857, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.a03]
-// CHECK:STDOUT:   %bound_method.loc13_31.1: <bound method> = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %bound_method.loc6_31.1: <bound method> = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound]
 // CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc13_31.2: <bound method> = bound_method %float, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc13_31.2(%float) [concrete = constants.%float.d20]
-// CHECK:STDOUT:   %.loc13_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.d20]
-// CHECK:STDOUT:   %.loc13_31.2: %f64.d77 = converted %float, %.loc13_31.1 [concrete = constants.%float.d20]
-// CHECK:STDOUT:   %Float64ToFloat32.call: init %f32 = call %Float64ToFloat32.ref(%.loc13_31.2)
+// CHECK:STDOUT:   %bound_method.loc6_31.2: <bound method> = bound_method %float, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc6_31.2(%float) [concrete = constants.%float.d20]
+// CHECK:STDOUT:   %.loc6_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.d20]
+// CHECK:STDOUT:   %.loc6_31.2: %f64.d77 = converted %float, %.loc6_31.1 [concrete = constants.%float.d20]
+// CHECK:STDOUT:   %Float64ToFloat32.call: init %f32.97e = call %Float64ToFloat32.ref(%.loc6_31.2) [concrete = constants.%float.e3b]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -652,8 +607,8 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %f32: type = class_type @Float, @Float(%int_32) [concrete]
-// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32.97e [concrete]
 // CHECK:STDOUT:   %Float64ToFloat32.type: type = fn_type @Float64ToFloat32 [concrete]
 // CHECK:STDOUT:   %Float64ToFloat32: %Float64ToFloat32.type = struct_value () [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
@@ -694,53 +649,53 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %a.patt: %pattern_type.201 = binding_pattern a [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc16_8: type = splice_block %f32.loc16 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc16: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc16: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc11_8: type = splice_block %f32.loc11 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc11: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc11: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc16_37.1: %f32 = value_of_initializer @__global_init.%Float64ToFloat32.call
-// CHECK:STDOUT:   %.loc16_37.2: %f32 = converted @__global_init.%Float64ToFloat32.call, %.loc16_37.1
-// CHECK:STDOUT:   %a: %f32 = bind_name a, %.loc16_37.2
+// CHECK:STDOUT:   %.loc11_37.1: %f32.97e = value_of_initializer @__global_init.%Float64ToFloat32.call [concrete = <error>]
+// CHECK:STDOUT:   %.loc11_37.2: %f32.97e = converted @__global_init.%Float64ToFloat32.call, %.loc11_37.1 [concrete = <error>]
+// CHECK:STDOUT:   %a: %f32.97e = bind_name a, %.loc11_37.2 [concrete = <error>]
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %b.patt: %pattern_type.201 = binding_pattern b [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc17_8: type = splice_block %f32.loc17 [concrete = constants.%f32] {
-// CHECK:STDOUT:     %int_32.loc17: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
-// CHECK:STDOUT:     %f32.loc17: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32]
+// CHECK:STDOUT:   %.loc16_8: type = splice_block %f32.loc16 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc16: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc16: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc17_42.1: %f32 = value_of_initializer @__global_init.%FloatLiteralToFloat32.call
-// CHECK:STDOUT:   %.loc17_42.2: %f32 = converted @__global_init.%FloatLiteralToFloat32.call, %.loc17_42.1
-// CHECK:STDOUT:   %b: %f32 = bind_name b, %.loc17_42.2
+// CHECK:STDOUT:   %.loc16_42.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call [concrete = <error>]
+// CHECK:STDOUT:   %.loc16_42.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call, %.loc16_42.1 [concrete = <error>]
+// CHECK:STDOUT:   %b: %f32.97e = bind_name b, %.loc16_42.2 [concrete = <error>]
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %c.patt: %pattern_type.0ae = binding_pattern c [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc22_8: type = splice_block %f64 [concrete = constants.%f64.d77] {
+// CHECK:STDOUT:   %.loc21_8: type = splice_block %f64 [concrete = constants.%f64.d77] {
 // CHECK:STDOUT:     %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
 // CHECK:STDOUT:     %f64: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc22_43.1: %f64.d77 = value_of_initializer @__global_init.%FloatLiteralToFloat64.call [concrete = <error>]
-// CHECK:STDOUT:   %.loc22_43.2: %f64.d77 = converted @__global_init.%FloatLiteralToFloat64.call, %.loc22_43.1 [concrete = <error>]
-// CHECK:STDOUT:   %c: %f64.d77 = bind_name c, %.loc22_43.2 [concrete = <error>]
+// CHECK:STDOUT:   %.loc21_43.1: %f64.d77 = value_of_initializer @__global_init.%FloatLiteralToFloat64.call [concrete = <error>]
+// CHECK:STDOUT:   %.loc21_43.2: %f64.d77 = converted @__global_init.%FloatLiteralToFloat64.call, %.loc21_43.1 [concrete = <error>]
+// CHECK:STDOUT:   %c: %f64.d77 = bind_name c, %.loc21_43.2 [concrete = <error>]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Float64ToFloat32.ref: %Float64ToFloat32.type = name_ref Float64ToFloat32, imports.%Main.Float64ToFloat32 [concrete = constants.%Float64ToFloat32]
-// CHECK:STDOUT:   %float.loc16: Core.FloatLiteral = float_literal_value 10e38 [concrete = constants.%float.bfd691.1]
+// CHECK:STDOUT:   %float.loc11: Core.FloatLiteral = float_literal_value 10e38 [concrete = constants.%float.bfd691.1]
 // CHECK:STDOUT:   %impl.elem0: %.678 = impl_witness_access constants.%ImplicitAs.impl_witness.857, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.a03]
-// CHECK:STDOUT:   %bound_method.loc16_31.1: <bound method> = bound_method %float.loc16, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %bound_method.loc11_31.1: <bound method> = bound_method %float.loc11, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound]
 // CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc16_31.2: <bound method> = bound_method %float.loc16, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc16_31.2(%float.loc16) [concrete = constants.%float.c37]
-// CHECK:STDOUT:   %.loc16_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.c37]
-// CHECK:STDOUT:   %.loc16_31.2: %f64.d77 = converted %float.loc16, %.loc16_31.1 [concrete = constants.%float.c37]
-// CHECK:STDOUT:   %Float64ToFloat32.call: init %f32 = call %Float64ToFloat32.ref(%.loc16_31.2)
+// CHECK:STDOUT:   %bound_method.loc11_31.2: <bound method> = bound_method %float.loc11, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc11_31.2(%float.loc11) [concrete = constants.%float.c37]
+// CHECK:STDOUT:   %.loc11_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.c37]
+// CHECK:STDOUT:   %.loc11_31.2: %f64.d77 = converted %float.loc11, %.loc11_31.1 [concrete = constants.%float.c37]
+// CHECK:STDOUT:   %Float64ToFloat32.call: init %f32.97e = call %Float64ToFloat32.ref(%.loc11_31.2) [concrete = <error>]
 // CHECK:STDOUT:   %FloatLiteralToFloat32.ref: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32]
-// CHECK:STDOUT:   %float.loc17: Core.FloatLiteral = float_literal_value 10e38 [concrete = constants.%float.bfd691.2]
-// CHECK:STDOUT:   %FloatLiteralToFloat32.call: init %f32 = call %FloatLiteralToFloat32.ref(%float.loc17)
+// CHECK:STDOUT:   %float.loc16: Core.FloatLiteral = float_literal_value 10e38 [concrete = constants.%float.bfd691.2]
+// CHECK:STDOUT:   %FloatLiteralToFloat32.call: init %f32.97e = call %FloatLiteralToFloat32.ref(%float.loc16) [concrete = <error>]
 // CHECK:STDOUT:   %FloatLiteralToFloat64.ref: %FloatLiteralToFloat64.type = name_ref FloatLiteralToFloat64, imports.%Main.FloatLiteralToFloat64 [concrete = constants.%FloatLiteralToFloat64]
-// CHECK:STDOUT:   %float.loc22: Core.FloatLiteral = float_literal_value 10e308 [concrete = constants.%float.bb5]
-// CHECK:STDOUT:   %FloatLiteralToFloat64.call: init %f64.d77 = call %FloatLiteralToFloat64.ref(%float.loc22) [concrete = <error>]
+// CHECK:STDOUT:   %float.loc21: Core.FloatLiteral = float_literal_value 10e308 [concrete = constants.%float.bb5]
+// CHECK:STDOUT:   %FloatLiteralToFloat64.call: init %f64.d77 = call %FloatLiteralToFloat64.ref(%float.loc21) [concrete = <error>]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -753,10 +708,10 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   %Float32ToFloat64.type: type = fn_type @Float32ToFloat64 [concrete]
 // CHECK:STDOUT:   %Float32ToFloat64: %Float32ToFloat64.type = struct_value () [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %f32: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
 // CHECK:STDOUT:   %float.674: Core.FloatLiteral = float_literal_value 10e-1 [concrete]
-// CHECK:STDOUT:   %ImplicitAs.type.31d: type = facet_type <@ImplicitAs, @ImplicitAs(%f32)> [concrete]
-// CHECK:STDOUT:   %ImplicitAs.Convert.type.b8c: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%f32) [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.31d: type = facet_type <@ImplicitAs, @ImplicitAs(%f32.97e)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.b8c: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%f32.97e) [concrete]
 // CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.644: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.b45: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.644 = struct_value () [symbolic]
@@ -768,11 +723,13 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.6d0: <bound method> = bound_method %float.674, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3 [concrete]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
 // CHECK:STDOUT:   %bound_method.6b4: <bound method> = bound_method %float.674, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %float.3b9: %f32 = float_value 1 [concrete]
+// CHECK:STDOUT:   %float.e3b: %f32.97e = float_value 1 [concrete]
+// CHECK:STDOUT:   %float.d20: %f64.d77 = float_value 1 [concrete]
 // CHECK:STDOUT:   %float.9bd: Core.FloatLiteral = float_literal_value 10e29 [concrete]
 // CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.a5c: <bound method> = bound_method %float.9bd, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3 [concrete]
 // CHECK:STDOUT:   %bound_method.3e4: <bound method> = bound_method %float.9bd, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %float.82f: %f32 = float_value 1.0E+30 [concrete]
+// CHECK:STDOUT:   %float.7d4: %f32.97e = float_value 1.00000002E+30 [concrete]
+// CHECK:STDOUT:   %float.6a7: %f64.d77 = float_value 1.0000000150474662E+30 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -789,8 +746,8 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:     %int_64.loc6: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
 // CHECK:STDOUT:     %f64.loc6: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc6_34.1: %f64.d77 = value_of_initializer @__global_init.%Float32ToFloat64.call.loc6
-// CHECK:STDOUT:   %.loc6_34.2: %f64.d77 = converted @__global_init.%Float32ToFloat64.call.loc6, %.loc6_34.1
+// CHECK:STDOUT:   %.loc6_34.1: %f64.d77 = value_of_initializer @__global_init.%Float32ToFloat64.call.loc6 [concrete = constants.%float.d20]
+// CHECK:STDOUT:   %.loc6_34.2: %f64.d77 = converted @__global_init.%Float32ToFloat64.call.loc6, %.loc6_34.1 [concrete = constants.%float.d20]
 // CHECK:STDOUT:   %a: %f64.d77 = bind_name a, %.loc6_34.2
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %b.patt: %pattern_type.0ae = binding_pattern b [concrete]
@@ -799,8 +756,8 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:     %int_64.loc7: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
 // CHECK:STDOUT:     %f64.loc7: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc7_37.1: %f64.d77 = value_of_initializer @__global_init.%Float32ToFloat64.call.loc7
-// CHECK:STDOUT:   %.loc7_37.2: %f64.d77 = converted @__global_init.%Float32ToFloat64.call.loc7, %.loc7_37.1
+// CHECK:STDOUT:   %.loc7_37.1: %f64.d77 = value_of_initializer @__global_init.%Float32ToFloat64.call.loc7 [concrete = constants.%float.6a7]
+// CHECK:STDOUT:   %.loc7_37.2: %f64.d77 = converted @__global_init.%Float32ToFloat64.call.loc7, %.loc7_37.1 [concrete = constants.%float.6a7]
 // CHECK:STDOUT:   %b: %f64.d77 = bind_name b, %.loc7_37.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -812,20 +769,20 @@ let convert_not_constant: f64 = Float64ToFloat64(not_constant_64);
 // CHECK:STDOUT:   %bound_method.loc6_31.1: <bound method> = bound_method %float.loc6, %impl.elem0.loc6 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.6d0]
 // CHECK:STDOUT:   %specific_fn.loc6: <specific function> = specific_function %impl.elem0.loc6, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
 // CHECK:STDOUT:   %bound_method.loc6_31.2: <bound method> = bound_method %float.loc6, %specific_fn.loc6 [concrete = constants.%bound_method.6b4]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %f32 = call %bound_method.loc6_31.2(%float.loc6) [concrete = constants.%float.3b9]
-// CHECK:STDOUT:   %.loc6_31.1: %f32 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%float.3b9]
-// CHECK:STDOUT:   %.loc6_31.2: %f32 = converted %float.loc6, %.loc6_31.1 [concrete = constants.%float.3b9]
-// CHECK:STDOUT:   %Float32ToFloat64.call.loc6: init %f64.d77 = call %Float32ToFloat64.ref.loc6(%.loc6_31.2)
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %f32.97e = call %bound_method.loc6_31.2(%float.loc6) [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc6_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc6_31.2: %f32.97e = converted %float.loc6, %.loc6_31.1 [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %Float32ToFloat64.call.loc6: init %f64.d77 = call %Float32ToFloat64.ref.loc6(%.loc6_31.2) [concrete = constants.%float.d20]
 // CHECK:STDOUT:   %Float32ToFloat64.ref.loc7: %Float32ToFloat64.type = name_ref Float32ToFloat64, imports.%Main.Float32ToFloat64 [concrete = constants.%Float32ToFloat64]
 // CHECK:STDOUT:   %float.loc7: Core.FloatLiteral = float_literal_value 10e29 [concrete = constants.%float.9bd]
 // CHECK:STDOUT:   %impl.elem0.loc7: %.a5d = impl_witness_access constants.%ImplicitAs.impl_witness.e3f, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1a3]
 // CHECK:STDOUT:   %bound_method.loc7_31.1: <bound method> = bound_method %float.loc7, %impl.elem0.loc7 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.a5c]
 // CHECK:STDOUT:   %specific_fn.loc7: <specific function> = specific_function %impl.elem0.loc7, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
 // CHECK:STDOUT:   %bound_method.loc7_31.2: <bound method> = bound_method %float.loc7, %specific_fn.loc7 [concrete = constants.%bound_method.3e4]
-// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %f32 = call %bound_method.loc7_31.2(%float.loc7) [concrete = constants.%float.82f]
-// CHECK:STDOUT:   %.loc7_31.1: %f32 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%float.82f]
-// CHECK:STDOUT:   %.loc7_31.2: %f32 = converted %float.loc7, %.loc7_31.1 [concrete = constants.%float.82f]
-// CHECK:STDOUT:   %Float32ToFloat64.call.loc7: init %f64.d77 = call %Float32ToFloat64.ref.loc7(%.loc7_31.2)
+// CHECK:STDOUT:   %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %f32.97e = call %bound_method.loc7_31.2(%float.loc7) [concrete = constants.%float.7d4]
+// CHECK:STDOUT:   %.loc7_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%float.7d4]
+// CHECK:STDOUT:   %.loc7_31.2: %f32.97e = converted %float.loc7, %.loc7_31.1 [concrete = constants.%float.7d4]
+// CHECK:STDOUT:   %Float32ToFloat64.call.loc7: init %f64.d77 = call %Float32ToFloat64.ref.loc7(%.loc7_31.2) [concrete = constants.%float.6a7]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 7
toolchain/check/testdata/builtins/float/div_assign.carbon

@@ -30,13 +30,6 @@ library "[[@TEST_NAME]]";
 // CHECK:STDERR:
 fn NotPtr(a: f64, b: f64) = "float.div_assign";
 
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+8]]:24: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.div_assign";
-// CHECK:STDERR:                        ^~~~~~
-// CHECK:STDERR:
 // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div_assign" [InvalidBuiltinSignature]
 // CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.div_assign";
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 10 - 7
toolchain/check/testdata/builtins/float/make_type.carbon

@@ -22,7 +22,10 @@ library "[[@TEST_NAME]]";
 
 import library "types";
 
-var f: Float(64);
+var a: Float(16);
+var b: Float(32);
+var c: Float(64);
+var d: Float(128);
 
 fn GetFloat(dyn_size: i32) -> type {
   //@dump-sem-ir-begin
@@ -36,7 +39,7 @@ library "[[@TEST_NAME]]";
 
 import library "types";
 
-// CHECK:STDERR: fail_invalid_size.carbon:[[@LINE+4]]:20: error: bit width must be 64 [CompileTimeFloatBitWidth]
+// CHECK:STDERR: fail_invalid_size.carbon:[[@LINE+4]]:20: error: unsupported floating-point bit width 931 [CompileTimeFloatBitWidth]
 // CHECK:STDERR: var invalid_float: Float(931);
 // CHECK:STDERR:                    ^~~~~~~~~~
 // CHECK:STDERR:
@@ -60,8 +63,8 @@ var dyn: Float(dyn_size);
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %Float.type: type = fn_type @Float [concrete]
 // CHECK:STDOUT:   %Float: %Float.type = struct_value () [concrete]
-// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %int_32.be0: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32.be0) [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -73,8 +76,8 @@ var dyn: Float(dyn_size);
 // CHECK:STDOUT:   %Float.ref: %Float.type = name_ref Float, imports.%Main.Float [concrete = constants.%Float]
 // CHECK:STDOUT:   %dyn_size.ref: %i32 = name_ref dyn_size, %dyn_size
 // CHECK:STDOUT:   %Float.call: init type = call %Float.ref(%dyn_size.ref)
-// CHECK:STDOUT:   %.loc10_25.1: type = value_of_initializer %Float.call
-// CHECK:STDOUT:   %.loc10_25.2: type = converted %Float.call, %.loc10_25.1
-// CHECK:STDOUT:   return %.loc10_25.2
+// CHECK:STDOUT:   %.loc13_25.1: type = value_of_initializer %Float.call
+// CHECK:STDOUT:   %.loc13_25.2: type = converted %Float.call, %.loc13_25.1
+// CHECK:STDOUT:   return %.loc13_25.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 7
toolchain/check/testdata/builtins/float/mul_assign.carbon

@@ -30,13 +30,6 @@ library "[[@TEST_NAME]]";
 // CHECK:STDERR:
 fn NotPtr(a: f64, b: f64) = "float.mul_assign";
 
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+8]]:24: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.mul_assign";
-// CHECK:STDERR:                        ^~~~~~
-// CHECK:STDERR:
 // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul_assign" [InvalidBuiltinSignature]
 // CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.mul_assign";
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 0 - 7
toolchain/check/testdata/builtins/float/sub_assign.carbon

@@ -30,13 +30,6 @@ library "[[@TEST_NAME]]";
 // CHECK:STDERR:
 fn NotPtr(a: f64, b: f64) = "float.sub_assign";
 
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+8]]:24: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.sub_assign";
-// CHECK:STDERR:                        ^~~~~~
-// CHECK:STDERR:
 // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub_assign" [InvalidBuiltinSignature]
 // CHECK:STDERR: fn MixedTypes(a: f64*, b: f32) = "float.sub_assign";
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 3 - 3
toolchain/check/testdata/deduce/int_float.carbon

@@ -194,8 +194,8 @@ fn G(a: f64) -> Core.IntLiteral() {
 // CHECK:STDOUT:   %pattern_type.0ae: type = pattern_type %f64.d77 [concrete]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
-// CHECK:STDOUT:   %f64.cae: type = float_type %int_64 [concrete]
-// CHECK:STDOUT:   %complete_type.3f9: <witness> = complete_type_witness %f64.cae [concrete]
+// CHECK:STDOUT:   %f64.794: type = float_type %int_64, f64 [concrete]
+// CHECK:STDOUT:   %complete_type.7b9: <witness> = complete_type_witness %f64.794 [concrete]
 // CHECK:STDOUT:   %F.specific_fn: <specific function> = specific_function %F, @F(%int_64) [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -309,6 +309,6 @@ fn G(a: f64) -> Core.IntLiteral() {
 // CHECK:STDOUT:   %pattern_type => constants.%pattern_type.0ae
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete => constants.%complete_type.3f9
+// CHECK:STDOUT:   %require_complete => constants.%complete_type.7b9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 286 - 0
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
+// EXTRA-ARGS: --target=x86_64-linux-gnu
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -357,6 +358,46 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// _Float16 param
+// ============================================================================
+
+// --- float16_param.h
+
+auto foo(_Float16 a) -> void;
+
+// --- import_float16_param.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "float16_param.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.foo(0.8 as f16);
+  //@dump-sem-ir-end
+}
+
+// ============================================================================
+// float param
+// ============================================================================
+
+// --- float_param.h
+
+auto foo(float a) -> void;
+
+// --- import_float_param.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "float_param.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.foo(0.8 as f32);
+  //@dump-sem-ir-end
+}
+
 // ============================================================================
 // double param
 // ============================================================================
@@ -377,6 +418,26 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// __float128 param
+// ============================================================================
+
+// --- float128_param.h
+
+auto foo(__float128 a) -> void;
+
+// --- import_float128_param.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "float128_param.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.foo(0.8 as f128);
+  //@dump-sem-ir-end
+}
+
 // ============================================================================
 // bool return
 // ============================================================================
@@ -1331,6 +1392,156 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- import_float16_param.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %int_16: Core.IntLiteral = int_value 16 [concrete]
+// CHECK:STDOUT:   %f16.a6a: type = class_type @Float, @Float(%int_16) [concrete]
+// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
+// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.823: type = ptr_type %f16.a6a [concrete]
+// CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
+// CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %float.c64: Core.FloatLiteral = float_literal_value 8e-1 [concrete]
+// CHECK:STDOUT:   %As.type.c58: type = facet_type <@As, @As(%f16.a6a)> [concrete]
+// CHECK:STDOUT:   %As.Convert.type.c23: type = fn_type @As.Convert, @As(%f16.a6a) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.type.6a5: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.6af: %Core.FloatLiteral.as.As.impl.Convert.type.6a5 = struct_value () [symbolic]
+// CHECK:STDOUT:   %As.impl_witness.bc0: <witness> = impl_witness imports.%As.impl_witness_table, @Core.FloatLiteral.as.As.impl(%int_16) [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.type.f15: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%int_16) [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.e2a: %Core.FloatLiteral.as.As.impl.Convert.type.f15 = struct_value () [concrete]
+// CHECK:STDOUT:   %As.facet: %As.type.c58 = facet_value Core.FloatLiteral, (%As.impl_witness.bc0) [concrete]
+// CHECK:STDOUT:   %.178: type = fn_type_with_self_type %As.Convert.type.c23, %As.facet [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.e2a [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.As.impl.Convert.e2a, @Core.FloatLiteral.as.As.impl.Convert(%int_16) [concrete]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %float.032: %f16.a6a = float_value 0.7998 [concrete]
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.type.cf1: type = fn_type @Float.as.Destroy.impl.Op, @Float.as.Destroy.impl(%int_16) [concrete]
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.85f: %Float.as.Destroy.impl.Op.type.cf1 = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .foo = %foo.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import_ref.2c9: @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert.type (%Core.FloatLiteral.as.As.impl.Convert.type.6a5) = import_ref Core//prelude/parts/float, loc20_41, loaded [symbolic = @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert (constants.%Core.FloatLiteral.as.As.impl.Convert.6af)]
+// CHECK:STDOUT:   %As.impl_witness_table = impl_witness_table (%Core.import_ref.2c9), @Core.FloatLiteral.as.As.impl [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 8e-1 [concrete = constants.%float.c64]
+// CHECK:STDOUT:   %int_16: Core.IntLiteral = int_value 16 [concrete = constants.%int_16]
+// CHECK:STDOUT:   %f16: type = class_type @Float, @Float(constants.%int_16) [concrete = constants.%f16.a6a]
+// CHECK:STDOUT:   %impl.elem0: %.178 = impl_witness_access constants.%As.impl_witness.bc0, element0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.e2a]
+// CHECK:STDOUT:   %bound_method.loc8_15.1: <bound method> = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.FloatLiteral.as.As.impl.Convert(constants.%int_16) [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_15.2: <bound method> = bound_method %float, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.call: init %f16.a6a = call %bound_method.loc8_15.2(%float) [concrete = constants.%float.032]
+// CHECK:STDOUT:   %.loc8_15.1: %f16.a6a = value_of_initializer %Core.FloatLiteral.as.As.impl.Convert.call [concrete = constants.%float.032]
+// CHECK:STDOUT:   %.loc8_15.2: %f16.a6a = converted %float, %.loc8_15.1 [concrete = constants.%float.032]
+// CHECK:STDOUT:   %.loc8_15.3: ref %f16.a6a = temporary_storage
+// CHECK:STDOUT:   %.loc8_15.4: ref %f16.a6a = temporary %.loc8_15.3, %.loc8_15.2
+// CHECK:STDOUT:   %addr.loc8_21: %ptr.823 = addr_of %.loc8_15.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_21)
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.3, constants.%Float.as.Destroy.impl.Op.85f
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc8_15.3: <bound method> = bound_method %.loc8_15.3, %Float.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_15: %ptr.823 = addr_of %.loc8_15.3
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_15.3(%addr.loc8_15)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_float_param.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
+// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.0bc: type = ptr_type %f32.97e [concrete]
+// CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
+// CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %float.c64: Core.FloatLiteral = float_literal_value 8e-1 [concrete]
+// CHECK:STDOUT:   %As.type.114: type = facet_type <@As, @As(%f32.97e)> [concrete]
+// CHECK:STDOUT:   %As.Convert.type.f5e: type = fn_type @As.Convert, @As(%f32.97e) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.type.6a5: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.6af: %Core.FloatLiteral.as.As.impl.Convert.type.6a5 = struct_value () [symbolic]
+// CHECK:STDOUT:   %As.impl_witness.1e4: <witness> = impl_witness imports.%As.impl_witness_table, @Core.FloatLiteral.as.As.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.type.73e: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.7b2: %Core.FloatLiteral.as.As.impl.Convert.type.73e = struct_value () [concrete]
+// CHECK:STDOUT:   %As.facet: %As.type.114 = facet_value Core.FloatLiteral, (%As.impl_witness.1e4) [concrete]
+// CHECK:STDOUT:   %.b33: type = fn_type_with_self_type %As.Convert.type.f5e, %As.facet [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.7b2 [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.As.impl.Convert.7b2, @Core.FloatLiteral.as.As.impl.Convert(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %float.4cb: %f32.97e = float_value 0.800000011 [concrete]
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.type.1b1: type = fn_type @Float.as.Destroy.impl.Op, @Float.as.Destroy.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.199: %Float.as.Destroy.impl.Op.type.1b1 = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .foo = %foo.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import_ref.2c9: @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert.type (%Core.FloatLiteral.as.As.impl.Convert.type.6a5) = import_ref Core//prelude/parts/float, loc20_41, loaded [symbolic = @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert (constants.%Core.FloatLiteral.as.As.impl.Convert.6af)]
+// CHECK:STDOUT:   %As.impl_witness_table = impl_witness_table (%Core.import_ref.2c9), @Core.FloatLiteral.as.As.impl [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 8e-1 [concrete = constants.%float.c64]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %f32: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
+// CHECK:STDOUT:   %impl.elem0: %.b33 = impl_witness_access constants.%As.impl_witness.1e4, element0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.7b2]
+// CHECK:STDOUT:   %bound_method.loc8_15.1: <bound method> = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.FloatLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_15.2: <bound method> = bound_method %float, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.call: init %f32.97e = call %bound_method.loc8_15.2(%float) [concrete = constants.%float.4cb]
+// CHECK:STDOUT:   %.loc8_15.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.As.impl.Convert.call [concrete = constants.%float.4cb]
+// CHECK:STDOUT:   %.loc8_15.2: %f32.97e = converted %float, %.loc8_15.1 [concrete = constants.%float.4cb]
+// CHECK:STDOUT:   %.loc8_15.3: ref %f32.97e = temporary_storage
+// CHECK:STDOUT:   %.loc8_15.4: ref %f32.97e = temporary %.loc8_15.3, %.loc8_15.2
+// CHECK:STDOUT:   %addr.loc8_21: %ptr.0bc = addr_of %.loc8_15.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_21)
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.3, constants.%Float.as.Destroy.impl.Op.199
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc8_15.3: <bound method> = bound_method %.loc8_15.3, %Float.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_15: %ptr.0bc = addr_of %.loc8_15.3
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_15.3(%addr.loc8_15)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_double_param.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -1406,6 +1617,81 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- import_float128_param.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %int_128: Core.IntLiteral = int_value 128 [concrete]
+// CHECK:STDOUT:   %f128.b8c: type = class_type @Float, @Float(%int_128) [concrete]
+// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
+// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.402: type = ptr_type %f128.b8c [concrete]
+// CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
+// CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %float.c64: Core.FloatLiteral = float_literal_value 8e-1 [concrete]
+// CHECK:STDOUT:   %As.type.d2d: type = facet_type <@As, @As(%f128.b8c)> [concrete]
+// CHECK:STDOUT:   %As.Convert.type.3bf: type = fn_type @As.Convert, @As(%f128.b8c) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.type.6a5: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.6af: %Core.FloatLiteral.as.As.impl.Convert.type.6a5 = struct_value () [symbolic]
+// CHECK:STDOUT:   %As.impl_witness.8a8: <witness> = impl_witness imports.%As.impl_witness_table, @Core.FloatLiteral.as.As.impl(%int_128) [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.type.22a: type = fn_type @Core.FloatLiteral.as.As.impl.Convert, @Core.FloatLiteral.as.As.impl(%int_128) [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.e57: %Core.FloatLiteral.as.As.impl.Convert.type.22a = struct_value () [concrete]
+// CHECK:STDOUT:   %As.facet: %As.type.d2d = facet_value Core.FloatLiteral, (%As.impl_witness.8a8) [concrete]
+// CHECK:STDOUT:   %.df5: type = fn_type_with_self_type %As.Convert.type.3bf, %As.facet [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.e57 [concrete]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.As.impl.Convert.e57, @Core.FloatLiteral.as.As.impl.Convert(%int_128) [concrete]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %float.c64, %Core.FloatLiteral.as.As.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %float.709: %f128.b8c = float_value 0.800000000000000000000000000000000039 [concrete]
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.type.0e0: type = fn_type @Float.as.Destroy.impl.Op, @Float.as.Destroy.impl(%int_128) [concrete]
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.052: %Float.as.Destroy.impl.Op.type.0e0 = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .foo = %foo.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import_ref.2c9: @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert.type (%Core.FloatLiteral.as.As.impl.Convert.type.6a5) = import_ref Core//prelude/parts/float, loc20_41, loaded [symbolic = @Core.FloatLiteral.as.As.impl.%Core.FloatLiteral.as.As.impl.Convert (constants.%Core.FloatLiteral.as.As.impl.Convert.6af)]
+// CHECK:STDOUT:   %As.impl_witness_table = impl_witness_table (%Core.import_ref.2c9), @Core.FloatLiteral.as.As.impl [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 8e-1 [concrete = constants.%float.c64]
+// CHECK:STDOUT:   %int_128: Core.IntLiteral = int_value 128 [concrete = constants.%int_128]
+// CHECK:STDOUT:   %f128: type = class_type @Float, @Float(constants.%int_128) [concrete = constants.%f128.b8c]
+// CHECK:STDOUT:   %impl.elem0: %.df5 = impl_witness_access constants.%As.impl_witness.8a8, element0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.e57]
+// CHECK:STDOUT:   %bound_method.loc8_15.1: <bound method> = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.FloatLiteral.as.As.impl.Convert(constants.%int_128) [concrete = constants.%Core.FloatLiteral.as.As.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_15.2: <bound method> = bound_method %float, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.FloatLiteral.as.As.impl.Convert.call: init %f128.b8c = call %bound_method.loc8_15.2(%float) [concrete = constants.%float.709]
+// CHECK:STDOUT:   %.loc8_15.1: %f128.b8c = value_of_initializer %Core.FloatLiteral.as.As.impl.Convert.call [concrete = constants.%float.709]
+// CHECK:STDOUT:   %.loc8_15.2: %f128.b8c = converted %float, %.loc8_15.1 [concrete = constants.%float.709]
+// CHECK:STDOUT:   %.loc8_15.3: ref %f128.b8c = temporary_storage
+// CHECK:STDOUT:   %.loc8_15.4: ref %f128.b8c = temporary %.loc8_15.3, %.loc8_15.2
+// CHECK:STDOUT:   %addr.loc8_22: %ptr.402 = addr_of %.loc8_15.4
+// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr.loc8_22)
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_15.3, constants.%Float.as.Destroy.impl.Op.052
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc8_15.3: <bound method> = bound_method %.loc8_15.3, %Float.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc8_15: %ptr.402 = addr_of %.loc8_15.3
+// CHECK:STDOUT:   %Float.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_15.3(%addr.loc8_15)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_bool_return.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 14 - 14
toolchain/check/testdata/interop/cpp/function/param_unsupported.carbon

@@ -16,7 +16,7 @@
 
 // --- unsupported_primitive_type.h
 
-auto foo(float a) -> void;
+auto foo(_BitInt(23) a) -> void;
 
 // --- fail_todo_import_unsupported_primitive_type.carbon
 
@@ -26,14 +26,14 @@ import Cpp library "unsupported_primitive_type.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: float` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(1.1);
+  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: _BitInt(23)` [SemanticsTodo]
+  // CHECK:STDERR:   Cpp.foo(11);
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR: fail_todo_import_unsupported_primitive_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
-  // CHECK:STDERR:   Cpp.foo(1.1);
+  // CHECK:STDERR:   Cpp.foo(11);
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:
-  Cpp.foo(1.1);
+  Cpp.foo(11);
   //@dump-sem-ir-end
 }
 
@@ -43,7 +43,7 @@ fn F() {
 
 // --- unsupported_primitive_type_among_params.h
 
-auto foo(int a, float b) -> void;
+auto foo(int a, _BitInt(23) b) -> void;
 
 // --- fail_todo_import_unsupported_primitive_type_among_params.carbon
 
@@ -53,21 +53,21 @@ import Cpp library "unsupported_primitive_type_among_params.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type_among_params.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: float` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(1, 2.0);
+  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type_among_params.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: _BitInt(23)` [SemanticsTodo]
+  // CHECK:STDERR:   Cpp.foo(1, 20);
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR: fail_todo_import_unsupported_primitive_type_among_params.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
-  // CHECK:STDERR:   Cpp.foo(1, 2.0);
+  // CHECK:STDERR:   Cpp.foo(1, 20);
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:
-  Cpp.foo(1, 2.0);
+  Cpp.foo(1, 20);
   //@dump-sem-ir-end
 }
 
 // CHECK:STDOUT: --- fail_todo_import_unsupported_primitive_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 11e-1 [concrete]
+// CHECK:STDOUT:   %int_11: Core.IntLiteral = int_value 11 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -81,7 +81,7 @@ fn F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
-// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 11e-1 [concrete = constants.%float]
+// CHECK:STDOUT:   %int_11: Core.IntLiteral = int_value 11 [concrete = constants.%int_11]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -89,7 +89,7 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete]
-// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 20e-1 [concrete]
+// CHECK:STDOUT:   %int_20: Core.IntLiteral = int_value 20 [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -104,7 +104,7 @@ fn F() {
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
 // CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
-// CHECK:STDOUT:   %float: Core.FloatLiteral = float_literal_value 20e-1 [concrete = constants.%float]
+// CHECK:STDOUT:   %int_20: Core.IntLiteral = int_value 20 [concrete = constants.%int_20]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 5 - 5
toolchain/check/testdata/primitives/numeric_literals.carbon

@@ -92,7 +92,7 @@ let max_rounds_down_hex: f64 = 0x1.FFFFFFFFFFFFF7FFFFFFFFFFp+1023;
 
 library "[[@TEST_NAME]]";
 
-// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:22: error: value 399999999999999999999999999999999999999999999999999999999999999999999990*10^237 too large for floating-point type `f64` [FloatTooLargeForType]
+// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:22: error: value 399999999999999999999999999999999999999999999999999999999999999999999990*10^237 too large for floating-point type `f64` [FloatLiteralTooLargeForType]
 // CHECK:STDERR: let too_large: f64 = 39999999999999999999999999999999999999999999999999999999999999999999999.0e238;
 // CHECK:STDERR:                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
@@ -105,19 +105,19 @@ let max_plus_one: f64 = 17976931348623157081452742373170435679807056752584499659
 let max_plus_a_bit_hex: f64 = 0x1.FFFFFFFFFFFFF00000000000000000000001p+1023;
 
 // The smallest number that would round to having an over-large exponent.
-// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:30: error: value 1797693134862315807937289714053034150799341327100378269361737789804449682927647509466490179775872070963302864166928879109465555478519404026306574886715058206819089020007083836762738548458177115317644757302700698555713669596228429148198608349364752927190741684443655107043427115596995080930428801779041744977920*10^-1 too large for floating-point type `f64` [FloatTooLargeForType]
+// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:30: error: value 1797693134862315807937289714053034150799341327100378269361737789804449682927647509466490179775872070963302864166928879109465555478519404026306574886715058206819089020007083836762738548458177115317644757302700698555713669596228429148198608349364752927190741684443655107043427115596995080930428801779041744977920*10^-1 too large for floating-point type `f64` [FloatLiteralTooLargeForType]
 // CHECK:STDERR: let min_rounds_to_inf: f64 = 179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792.0;
 // CHECK:STDERR:                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 let min_rounds_to_inf: f64 = 179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792.0;
 
-// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:41: error: value 179769313486231580793728971405303415079934132710037826936173778980444969*10^237 too large for floating-point type `f64` [FloatTooLargeForType]
+// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:41: error: value 179769313486231580793728971405303415079934132710037826936173778980444969*10^237 too large for floating-point type `f64` [FloatLiteralTooLargeForType]
 // CHECK:STDERR: let min_rounds_to_inf_rounded_up: f64 = 17976931348623158079372897140530341507993413271003782693617377898044496.9e238;
 // CHECK:STDERR:                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 let min_rounds_to_inf_rounded_up: f64 = 17976931348623158079372897140530341507993413271003782693617377898044496.9e238;
 
-// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:34: error: value 144115188075855864*2^967 too large for floating-point type `f64` [FloatTooLargeForType]
+// CHECK:STDERR: fail_overflow_very_large_mantissa.carbon:[[@LINE+4]]:34: error: value 144115188075855864*2^967 too large for floating-point type `f64` [FloatLiteralTooLargeForType]
 // CHECK:STDERR: let min_rounds_to_inf_hex: f64 = 0x1.FFFFFFFFFFFFF8p+1023;
 // CHECK:STDERR:                                  ^~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
@@ -127,7 +127,7 @@ let min_rounds_to_inf_hex: f64 = 0x1.FFFFFFFFFFFFF8p+1023;
 
 library "[[@TEST_NAME]]";
 
-// CHECK:STDERR: fail_overflow_very_large_exponent.carbon:[[@LINE+4]]:14: error: value 50*10^39999999999999999992 too large for floating-point type `f64` [FloatTooLargeForType]
+// CHECK:STDERR: fail_overflow_very_large_exponent.carbon:[[@LINE+4]]:14: error: value 50*10^39999999999999999992 too large for floating-point type `f64` [FloatLiteralTooLargeForType]
 // CHECK:STDERR: let e: f64 = 5.0e39999999999999999993;
 // CHECK:STDERR:              ^~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:

+ 56 - 34
toolchain/check/testdata/primitives/type_literals.carbon

@@ -34,37 +34,11 @@ var test_u64: u64;
 library "[[@TEST_NAME]]";
 
 //@dump-sem-ir-begin
-var test_f64: f64;
-//@dump-sem-ir-end
-
-// --- fail_todo_fN_unsupported.carbon
-library "[[@TEST_NAME]]";
-
-// TODO: Some or all of these should eventually work.
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_fN_unsupported.carbon:[[@LINE+4]]:15: note: in `f16` used here [ResolvingSpecificHere]
-// CHECK:STDERR: var test_f16: f16;
-// CHECK:STDERR:               ^~~
-// CHECK:STDERR:
 var test_f16: f16;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_fN_unsupported.carbon:[[@LINE+4]]:15: note: in `f32` used here [ResolvingSpecificHere]
-// CHECK:STDERR: var test_f32: f32;
-// CHECK:STDERR:               ^~~
-// CHECK:STDERR:
 var test_f32: f32;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
-// CHECK:STDERR:   adapt MakeFloat(N);
-// CHECK:STDERR:         ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_fN_unsupported.carbon:[[@LINE+4]]:16: note: in `f128` used here [ResolvingSpecificHere]
-// CHECK:STDERR: var test_f128: f128;
-// CHECK:STDERR:                ^~~~
-// CHECK:STDERR:
+var test_f64: f64;
 var test_f128: f128;
+//@dump-sem-ir-end
 
 // --- string.carbon
 
@@ -164,7 +138,7 @@ library "[[@TEST_NAME]]";
 // CHECK:STDERR:              ^~
 // CHECK:STDERR:
 var test_f0: f0;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
+// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: unsupported floating-point bit width 1 [CompileTimeFloatBitWidth]
 // CHECK:STDERR:   adapt MakeFloat(N);
 // CHECK:STDERR:         ^~~~~~~~~~~~
 // CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:14: note: in `f1` used here [ResolvingSpecificHere]
@@ -172,7 +146,7 @@ var test_f0: f0;
 // CHECK:STDERR:              ^~
 // CHECK:STDERR:
 var test_f1: f1;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
+// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: unsupported floating-point bit width 15 [CompileTimeFloatBitWidth]
 // CHECK:STDERR:   adapt MakeFloat(N);
 // CHECK:STDERR:         ^~~~~~~~~~~~
 // CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:15: note: in `f15` used here [ResolvingSpecificHere]
@@ -180,7 +154,16 @@ var test_f1: f1;
 // CHECK:STDERR:               ^~~
 // CHECK:STDERR:
 var test_f15: f15;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
+// TODO: Decide if we want to expose the x87 80-bit floating point type this way.
+// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: unsupported floating-point bit width 80 [CompileTimeFloatBitWidth]
+// CHECK:STDERR:   adapt MakeFloat(N);
+// CHECK:STDERR:         ^~~~~~~~~~~~
+// CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:15: note: in `f80` used here [ResolvingSpecificHere]
+// CHECK:STDERR: var test_f80: f80;
+// CHECK:STDERR:               ^~~
+// CHECK:STDERR:
+var test_f80: f80;
+// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: unsupported floating-point bit width 100 [CompileTimeFloatBitWidth]
 // CHECK:STDERR:   adapt MakeFloat(N);
 // CHECK:STDERR:         ^~~~~~~~~~~~
 // CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:16: note: in `f100` used here [ResolvingSpecificHere]
@@ -188,7 +171,7 @@ var test_f15: f15;
 // CHECK:STDERR:                ^~~~
 // CHECK:STDERR:
 var test_f100: f100;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
+// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: unsupported floating-point bit width 1000000000 [CompileTimeFloatBitWidth]
 // CHECK:STDERR:   adapt MakeFloat(N);
 // CHECK:STDERR:         ^~~~~~~~~~~~
 // CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:23: note: in `f1000000000` used here [ResolvingSpecificHere]
@@ -196,7 +179,7 @@ var test_f100: f100;
 // CHECK:STDERR:                       ^~~~~~~~~~~
 // CHECK:STDERR:
 var test_f1000000000: f1000000000;
-// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: bit width must be 64 [CompileTimeFloatBitWidth]
+// CHECK:STDERR: min_prelude/parts/float.carbon:11:9: error: unsupported floating-point bit width 1000000000000 [CompileTimeFloatBitWidth]
 // CHECK:STDERR:   adapt MakeFloat(N);
 // CHECK:STDERR:         ^~~~~~~~~~~~
 // CHECK:STDERR: fail_fN_bad_width.carbon:[[@LINE+4]]:26: note: in `f1000000000000` used here [ResolvingSpecificHere]
@@ -347,9 +330,18 @@ var x: type = 42;
 // CHECK:STDOUT: --- fN.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %int_16: Core.IntLiteral = int_value 16 [concrete]
+// CHECK:STDOUT:   %f16.a6a: type = class_type @Float, @Float(%int_16) [concrete]
+// CHECK:STDOUT:   %pattern_type.cd1: type = pattern_type %f16.a6a [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32.97e [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %f64.d77: type = class_type @Float, @Float(%int_64) [concrete]
 // CHECK:STDOUT:   %pattern_type.0ae: type = pattern_type %f64.d77 [concrete]
+// CHECK:STDOUT:   %int_128: Core.IntLiteral = int_value 128 [concrete]
+// CHECK:STDOUT:   %f128.b8c: type = class_type @Float, @Float(%int_128) [concrete]
+// CHECK:STDOUT:   %pattern_type.22c: type = pattern_type %f128.b8c [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -357,15 +349,45 @@ var x: type = 42;
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %test_f16.patt: %pattern_type.cd1 = binding_pattern test_f16 [concrete]
+// CHECK:STDOUT:     %test_f16.var_patt: %pattern_type.cd1 = var_pattern %test_f16.patt [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %test_f16.var: ref %f16.a6a = var %test_f16.var_patt [concrete]
+// CHECK:STDOUT:   %.loc4: type = splice_block %f16 [concrete = constants.%f16.a6a] {
+// CHECK:STDOUT:     %int_16: Core.IntLiteral = int_value 16 [concrete = constants.%int_16]
+// CHECK:STDOUT:     %f16: type = class_type @Float, @Float(constants.%int_16) [concrete = constants.%f16.a6a]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %test_f16: ref %f16.a6a = bind_name test_f16, %test_f16.var [concrete = %test_f16.var]
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %test_f32.patt: %pattern_type.201 = binding_pattern test_f32 [concrete]
+// CHECK:STDOUT:     %test_f32.var_patt: %pattern_type.201 = var_pattern %test_f32.patt [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %test_f32.var: ref %f32.97e = var %test_f32.var_patt [concrete]
+// CHECK:STDOUT:   %.loc5: type = splice_block %f32 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %test_f32: ref %f32.97e = bind_name test_f32, %test_f32.var [concrete = %test_f32.var]
+// CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %test_f64.patt: %pattern_type.0ae = binding_pattern test_f64 [concrete]
 // CHECK:STDOUT:     %test_f64.var_patt: %pattern_type.0ae = var_pattern %test_f64.patt [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %test_f64.var: ref %f64.d77 = var %test_f64.var_patt [concrete]
-// CHECK:STDOUT:   %.loc4: type = splice_block %f64 [concrete = constants.%f64.d77] {
+// CHECK:STDOUT:   %.loc6: type = splice_block %f64 [concrete = constants.%f64.d77] {
 // CHECK:STDOUT:     %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
 // CHECK:STDOUT:     %f64: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %test_f64: ref %f64.d77 = bind_name test_f64, %test_f64.var [concrete = %test_f64.var]
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %test_f128.patt: %pattern_type.22c = binding_pattern test_f128 [concrete]
+// CHECK:STDOUT:     %test_f128.var_patt: %pattern_type.22c = var_pattern %test_f128.patt [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %test_f128.var: ref %f128.b8c = var %test_f128.var_patt [concrete]
+// CHECK:STDOUT:   %.loc7: type = splice_block %f128 [concrete = constants.%f128.b8c] {
+// CHECK:STDOUT:     %int_128: Core.IntLiteral = int_value 128 [concrete = constants.%int_128]
+// CHECK:STDOUT:     %f128: type = class_type @Float, @Float(constants.%int_128) [concrete = constants.%f128.b8c]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %test_f128: ref %f128.b8c = bind_name test_f128, %test_f128.var [concrete = %test_f128.var]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- string.carbon

+ 45 - 20
toolchain/check/type.cpp

@@ -8,10 +8,10 @@
 #include "toolchain/check/facet_type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/sem_ir/facet_type_info.h"
+#include "toolchain/sem_ir/ids.h"
 
 namespace Carbon::Check {
 
-// Enforces that an integer type has a valid bit width.
 auto ValidateIntType(Context& context, SemIR::LocId loc_id,
                      SemIR::IntType result) -> bool {
   auto bit_width =
@@ -44,29 +44,54 @@ auto ValidateIntType(Context& context, SemIR::LocId loc_id,
   return true;
 }
 
-// Enforces that the bit width is 64 for a float.
-auto ValidateFloatBitWidth(Context& context, SemIR::LocId loc_id,
-                           SemIR::InstId inst_id) -> bool {
-  auto inst = context.insts().GetAs<SemIR::IntValue>(inst_id);
-  if (context.ints().Get(inst.int_id) == 64) {
+auto ValidateFloatTypeAndSetKind(Context& context, SemIR::LocId loc_id,
+                                 SemIR::FloatType& result) -> bool {
+  // Get the bit width value.
+  auto bit_width_inst =
+      context.insts().TryGetAs<SemIR::IntValue>(result.bit_width_id);
+  if (!bit_width_inst) {
+    // Symbolic bit width. Defer checking until we have a concrete value.
     return true;
   }
+  auto bit_width = context.ints().Get(bit_width_inst->int_id);
+
+  // If no kind is specified, infer kind from width.
+  if (!result.float_kind.has_value()) {
+    switch (bit_width.getLimitedValue()) {
+      case 16:
+        result.float_kind = SemIR::FloatKind::Binary16;
+        break;
+      case 32:
+        result.float_kind = SemIR::FloatKind::Binary32;
+        break;
+      case 64:
+        result.float_kind = SemIR::FloatKind::Binary64;
+        break;
+      case 128:
+        result.float_kind = SemIR::FloatKind::Binary128;
+        break;
+      default:
+        CARBON_DIAGNOSTIC(CompileTimeFloatBitWidth, Error,
+                          "unsupported floating-point bit width {0}", TypedInt);
+        context.emitter().Emit(loc_id, CompileTimeFloatBitWidth,
+                               TypedInt(bit_width_inst->type_id, bit_width));
+        return false;
+    }
+  }
 
-  CARBON_DIAGNOSTIC(CompileTimeFloatBitWidth, Error, "bit width must be 64");
-  context.emitter().Emit(loc_id, CompileTimeFloatBitWidth);
-  return false;
-}
-
-// Enforces that a float type has a valid bit width.
-auto ValidateFloatType(Context& context, SemIR::LocId loc_id,
-                       SemIR::FloatType result) -> bool {
-  auto bit_width =
-      context.insts().TryGetAs<SemIR::IntValue>(result.bit_width_id);
-  if (!bit_width) {
-    // Symbolic bit width.
-    return true;
+  if (llvm::APFloat::semanticsSizeInBits(result.float_kind.Semantics()) !=
+      bit_width) {
+    // This can't currently happen because we don't provide any way to set the
+    // float kind other than through the bit width.
+    // TODO: Add a float_type.make builtin that takes a float kind, and add a
+    // diagnostic here if the size is wrong.
+    context.TODO(loc_id, "wrong size for float type");
+    return false;
   }
-  return ValidateFloatBitWidth(context, loc_id, result.bit_width_id);
+
+  // TODO: Diagnose if the floating-point type is not supported on this target?
+
+  return true;
 }
 
 // Gets or forms a type_id for a type, given the instruction kind and arguments.

+ 4 - 7
toolchain/check/type.h

@@ -15,13 +15,10 @@ namespace Carbon::Check {
 auto ValidateIntType(Context& context, SemIR::LocId loc_id,
                      SemIR::IntType result) -> bool;
 
-// Enforces that the bit width is 64 for a float.
-auto ValidateFloatBitWidth(Context& context, SemIR::LocId loc_id,
-                           SemIR::InstId inst_id) -> bool;
-
-// Enforces that a float type has a valid bit width.
-auto ValidateFloatType(Context& context, SemIR::LocId loc_id,
-                       SemIR::FloatType result) -> bool;
+// Enforces that a float type has a valid bit width. If the `float_kind` field
+// is `None`, sets it to a suitable kind for the bit width.
+auto ValidateFloatTypeAndSetKind(Context& context, SemIR::LocId loc_id,
+                                 SemIR::FloatType& result) -> bool;
 
 // Gets the type to use for an unbound associated entity declared in this
 // interface. For example, this is the type of `I.T` after

+ 1 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -423,6 +423,7 @@ CARBON_DIAGNOSTIC_KIND(IncompleteTypeInValueConversion)
 CARBON_DIAGNOSTIC_KIND(InCopy)
 CARBON_DIAGNOSTIC_KIND(CharTooLargeForType)
 CARBON_DIAGNOSTIC_KIND(IntTooLargeForType)
+CARBON_DIAGNOSTIC_KIND(FloatLiteralTooLargeForType)
 CARBON_DIAGNOSTIC_KIND(FloatTooLargeForType)
 CARBON_DIAGNOSTIC_KIND(IntWidthNotMultipleOf8)
 CARBON_DIAGNOSTIC_KIND(IntWidthNotPositive)

+ 3 - 3
toolchain/lower/file_context.cpp

@@ -794,10 +794,10 @@ static auto BuildTypeForInst(FileContext& /*context*/,
   return nullptr;
 }
 
-static auto BuildTypeForInst(FileContext& context, SemIR::FloatType /*inst*/)
+static auto BuildTypeForInst(FileContext& context, SemIR::FloatType inst)
     -> llvm::Type* {
-  // TODO: Handle different sizes.
-  return llvm::Type::getDoubleTy(context.llvm_context());
+  return llvm::Type::getFloatingPointTy(context.llvm_context(),
+                                        inst.float_kind.Semantics());
 }
 
 static auto BuildTypeForInst(FileContext& context, SemIR::IntType inst)

+ 64 - 0
toolchain/lower/testdata/builtins/float.carbon

@@ -63,6 +63,22 @@ fn TestMul(a: f64*, b: f64) { Mul(a, b); }
 fn Div(a: f64*, b: f64) = "float.div_assign";
 fn TestDiv(a: f64*, b: f64) { Div(a, b); }
 
+// --- types.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F16(a: f16, b: f16) -> f16 = "float.add";
+fn TestF16(a: f16, b: f16) -> f16 { return F16(a, b); }
+
+fn F32(a: f32, b: f32) -> f32 = "float.add";
+fn TestF32(a: f32, b: f32) -> f32 { return F32(a, b); }
+
+fn F64(a: f64, b: f64) -> f64 = "float.add";
+fn TestF64(a: f64, b: f64) -> f64 { return F64(a, b); }
+
+fn F128(a: f128, b: f128) -> f128 = "float.add";
+fn TestF128(a: f128, b: f128) -> f128 { return F128(a, b); }
+
 // CHECK:STDOUT: ; ModuleID = 'basic.carbon'
 // CHECK:STDOUT: source_filename = "basic.carbon"
 // CHECK:STDOUT:
@@ -230,3 +246,51 @@ fn TestDiv(a: f64*, b: f64) { Div(a, b); }
 // CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestDiv", linkageName: "_CTestDiv.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !16 = !DILocation(line: 14, column: 31, scope: !15)
 // CHECK:STDOUT: !17 = !DILocation(line: 14, column: 1, scope: !15)
+// CHECK:STDOUT: ; ModuleID = 'types.carbon'
+// CHECK:STDOUT: source_filename = "types.carbon"
+// CHECK:STDOUT:
+// CHECK:STDOUT: define half @_CTestF16.Main(half %a, half %b) !dbg !4 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %F16.call = fadd half %a, %b, !dbg !7
+// CHECK:STDOUT:   ret half %F16.call, !dbg !8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define float @_CTestF32.Main(float %a, float %b) !dbg !9 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %F32.call = fadd float %a, %b, !dbg !10
+// CHECK:STDOUT:   ret float %F32.call, !dbg !11
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define double @_CTestF64.Main(double %a, double %b) !dbg !12 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %F64.call = fadd double %a, %b, !dbg !13
+// CHECK:STDOUT:   ret double %F64.call, !dbg !14
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: define fp128 @_CTestF128.Main(fp128 %a, fp128 %b) !dbg !15 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %F128.call = fadd fp128 %a, %b, !dbg !16
+// CHECK:STDOUT:   ret fp128 %F128.call, !dbg !17
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !3 = !DIFile(filename: "types.carbon", directory: "")
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TestF16", linkageName: "_CTestF16.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
+// CHECK:STDOUT: !6 = !{}
+// CHECK:STDOUT: !7 = !DILocation(line: 5, column: 44, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 37, scope: !4)
+// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "TestF32", linkageName: "_CTestF32.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !10 = !DILocation(line: 8, column: 44, scope: !9)
+// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 37, scope: !9)
+// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TestF64", linkageName: "_CTestF64.Main", scope: null, file: !3, line: 11, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 44, scope: !12)
+// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 37, scope: !12)
+// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "TestF128", linkageName: "_CTestF128.Main", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !16 = !DILocation(line: 14, column: 48, scope: !15)
+// CHECK:STDOUT: !17 = !DILocation(line: 14, column: 41, scope: !15)

+ 34 - 16
toolchain/lower/testdata/builtins/types.carbon

@@ -16,11 +16,17 @@ fn Int(size: IntLiteral()) -> type = "int.make_type_signed";
 fn Float(size: IntLiteral()) -> type = "float.make_type";
 fn Bool() -> type = "bool.make_type";
 fn AsI32(n: IntLiteral()) -> Int(32) = "int.convert_checked";
+fn AsF16(f: FloatLiteral()) -> Float(16) = "float.convert_checked";
+fn AsF32(f: FloatLiteral()) -> Float(32) = "float.convert_checked";
 fn AsF64(f: FloatLiteral()) -> Float(64) = "float.convert_checked";
+fn AsF128(f: FloatLiteral()) -> Float(128) = "float.convert_checked";
 
 fn F() {
-  var i: Int(32) = AsI32(0);
-  var f: Float(64) = AsF64(0.0);
+  var i: Int(32) = AsI32(1);
+  var e: Float(16) = AsF16(1.0);
+  var f: Float(32) = AsF32(1.0);
+  var g: Float(64) = AsF64(1.0);
+  var h: Float(128) = AsF128(1.0);
   var b: Bool() = false;
 }
 
@@ -30,22 +36,31 @@ fn F() {
 // CHECK:STDOUT: define void @_CF.Main() !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %i.var = alloca i32, align 4, !dbg !7
-// CHECK:STDOUT:   %f.var = alloca double, align 8, !dbg !8
-// CHECK:STDOUT:   %b.var = alloca i1, align 1, !dbg !9
+// CHECK:STDOUT:   %e.var = alloca half, align 2, !dbg !8
+// CHECK:STDOUT:   %f.var = alloca float, align 4, !dbg !9
+// CHECK:STDOUT:   %g.var = alloca double, align 8, !dbg !10
+// CHECK:STDOUT:   %h.var = alloca fp128, align 16, !dbg !11
+// CHECK:STDOUT:   %b.var = alloca i1, align 1, !dbg !12
 // CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %i.var), !dbg !7
-// CHECK:STDOUT:   store i32 0, ptr %i.var, align 4, !dbg !7
-// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %f.var), !dbg !8
-// CHECK:STDOUT:   store double 0.000000e+00, ptr %f.var, align 8, !dbg !8
-// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %b.var), !dbg !9
-// CHECK:STDOUT:   store i1 false, ptr %b.var, align 1, !dbg !9
-// CHECK:STDOUT:   ret void, !dbg !10
+// CHECK:STDOUT:   store i32 1, ptr %i.var, align 4, !dbg !7
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 2, ptr %e.var), !dbg !8
+// CHECK:STDOUT:   store half 0xH3C00, ptr %e.var, align 2, !dbg !8
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 4, ptr %f.var), !dbg !9
+// CHECK:STDOUT:   store float 1.000000e+00, ptr %f.var, align 4, !dbg !9
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 8, ptr %g.var), !dbg !10
+// CHECK:STDOUT:   store double 1.000000e+00, ptr %g.var, align 8, !dbg !10
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 16, ptr %h.var), !dbg !11
+// CHECK:STDOUT:   store fp128 0xL00000000000000003FFF000000000000, ptr %h.var, align 16, !dbg !11
+// CHECK:STDOUT:   call void @llvm.lifetime.start.p0(i64 1, ptr %b.var), !dbg !12
+// CHECK:STDOUT:   store i1 false, ptr %b.var, align 1, !dbg !12
+// CHECK:STDOUT:   ret void, !dbg !13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 2, 1, 0 }
+// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:
@@ -56,10 +71,13 @@ fn F() {
 // CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
 // CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
 // CHECK:STDOUT: !3 = !DIFile(filename: "types.carbon", directory: "")
-// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 21, type: !5, spFlags: DISPFlagDefinition, unit: !2)
+// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 24, type: !5, spFlags: DISPFlagDefinition, unit: !2)
 // CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
 // CHECK:STDOUT: !6 = !{}
-// CHECK:STDOUT: !7 = !DILocation(line: 22, column: 3, scope: !4)
-// CHECK:STDOUT: !8 = !DILocation(line: 23, column: 3, scope: !4)
-// CHECK:STDOUT: !9 = !DILocation(line: 24, column: 3, scope: !4)
-// CHECK:STDOUT: !10 = !DILocation(line: 21, column: 1, scope: !4)
+// CHECK:STDOUT: !7 = !DILocation(line: 25, column: 3, scope: !4)
+// CHECK:STDOUT: !8 = !DILocation(line: 26, column: 3, scope: !4)
+// CHECK:STDOUT: !9 = !DILocation(line: 27, column: 3, scope: !4)
+// CHECK:STDOUT: !10 = !DILocation(line: 28, column: 3, scope: !4)
+// CHECK:STDOUT: !11 = !DILocation(line: 29, column: 3, scope: !4)
+// CHECK:STDOUT: !12 = !DILocation(line: 30, column: 3, scope: !4)
+// CHECK:STDOUT: !13 = !DILocation(line: 24, column: 1, scope: !4)

+ 48 - 0
toolchain/sem_ir/ids.cpp

@@ -78,6 +78,54 @@ auto IntKind::Print(llvm::raw_ostream& out) const -> void {
   }
 }
 
+static auto FloatKindToStringLiteral(FloatKind kind) -> llvm::StringLiteral {
+  switch (kind.index) {
+    case FloatKind::None.index:
+      return "<none>";
+    case FloatKind::Binary16.index:
+      return "f16";
+    case FloatKind::Binary32.index:
+      return "f32";
+    case FloatKind::Binary64.index:
+      return "f64";
+    case FloatKind::Binary128.index:
+      return "f128";
+    case FloatKind::BFloat16.index:
+      return "f16_brain";
+    case FloatKind::X87Float80.index:
+      return "f80_x87";
+    case FloatKind::PPCFloat128.index:
+      return "f128_ppc";
+    default:
+      return "<invalid>";
+  }
+}
+
+auto FloatKind::Print(llvm::raw_ostream& out) const -> void {
+  out << FloatKindToStringLiteral(*this);
+}
+
+auto FloatKind::Semantics() const -> const llvm::fltSemantics& {
+  switch (this->index) {
+    case Binary16.index:
+      return llvm::APFloat::IEEEhalf();
+    case Binary32.index:
+      return llvm::APFloat::IEEEsingle();
+    case Binary64.index:
+      return llvm::APFloat::IEEEdouble();
+    case Binary128.index:
+      return llvm::APFloat::IEEEquad();
+    case BFloat16.index:
+      return llvm::APFloat::BFloat();
+    case X87Float80.index:
+      return llvm::APFloat::x87DoubleExtended();
+    case PPCFloat128.index:
+      return llvm::APFloat::PPCDoubleDouble();
+    default:
+      CARBON_FATAL("Unexpected float kind {0}", *this);
+  }
+}
+
 // Double-check the special value mapping and constexpr evaluation.
 static_assert(NameId::SpecialNameId::Vptr == *NameId::Vptr.AsSpecialNameId());
 

+ 39 - 2
toolchain/sem_ir/ids.h

@@ -9,6 +9,7 @@
 
 #include "common/check.h"
 #include "common/ostream.h"
+#include "llvm/ADT/APFloat.h"
 #include "toolchain/base/index_base.h"
 #include "toolchain/base/value_ids.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
@@ -503,16 +504,52 @@ struct IntKind : public IdBase<IntKind> {
 constexpr IntKind IntKind::Unsigned = IntKind(0);
 constexpr IntKind IntKind::Signed = IntKind(1);
 
-// A float kind value.
+// A float kind value. This describes the semantics of the floating-point type.
+// This represents very similar information to the bit-width, but is more
+// precise. In particular, there is in general more than one floating-point type
+// with a given bit-width, and while only one such type can be named with the
+// `fN` notation, the others should still be modeled as `FloatType`s.
 struct FloatKind : public IdBase<FloatKind> {
   // Not used by `Print`, but for `IdKind`.
   static constexpr llvm::StringLiteral Label = "float_kind";
 
+  // An explicitly absent kind. Used when the kind has not been determined.
+  static const FloatKind None;
+
+  // Supported IEEE-754 interchange formats. These correspond to Carbon `fN`
+  // type literal syntax.
+  static const FloatKind Binary16;
+  static const FloatKind Binary32;
+  static const FloatKind Binary64;
+  static const FloatKind Binary128;
+  // Note, binary256 is not supported by LLVM and hence not by us.
+
+  // Other formats supported by LLVM. Support for these may be
+  // target-dependent.
+  // TODO: Add a mechanism to use these types from Carbon code.
+  static const FloatKind BFloat16;
+  static const FloatKind X87Float80;
+  static const FloatKind PPCFloat128;
+
   using IdBase::IdBase;
 
-  auto Print(llvm::raw_ostream& out) const -> void { out << "float"; }
+  auto Print(llvm::raw_ostream& out) const -> void;
+
+  // Query the LLVM semantics model associated with this kind of floating-point
+  // type. This kind must be concrete.
+  auto Semantics() const -> const llvm::fltSemantics&;
 };
 
+constexpr FloatKind FloatKind::None = FloatKind(NoneIndex);
+
+constexpr FloatKind FloatKind::Binary16 = FloatKind(0);
+constexpr FloatKind FloatKind::Binary32 = FloatKind(1);
+constexpr FloatKind FloatKind::Binary64 = FloatKind(2);
+constexpr FloatKind FloatKind::Binary128 = FloatKind(3);
+constexpr FloatKind FloatKind::BFloat16 = FloatKind(4);
+constexpr FloatKind FloatKind::X87Float80 = FloatKind(5);
+constexpr FloatKind FloatKind::PPCFloat128 = FloatKind(6);
+
 // An X-macro for special names. Uses should look like:
 //
 //   #define CARBON_SPECIAL_NAME_ID_FOR_XYZ(Name) ...

+ 1 - 0
toolchain/sem_ir/typed_insts.h

@@ -734,6 +734,7 @@ struct FloatType {
   // TODO: Consider adding a more compact way of representing either a small
   // float bit width or an inst_id.
   InstId bit_width_id;
+  FloatKind float_kind;
 };
 
 // A floating point value.