diff --git a/toolchain/check/cpp/operators.cpp b/toolchain/check/cpp/operators.cpp index 8170006bf631..a76de87b8e93 100644 --- a/toolchain/check/cpp/operators.cpp +++ b/toolchain/check/cpp/operators.cpp @@ -13,6 +13,7 @@ #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/overload_resolution.h" #include "toolchain/check/cpp/type_mapping.h" +#include "toolchain/check/custom_witness.h" #include "toolchain/check/function.h" #include "toolchain/check/inst.h" #include "toolchain/check/pattern.h" @@ -21,6 +22,7 @@ #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/clang_decl.h" #include "toolchain/sem_ir/cpp_initializer_list.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" @@ -481,10 +483,141 @@ static auto LookupCppConversion(Context& context, SemIR::LocId loc_id, return SemIR::InstId::None; } -static auto FindClangOperator(Context& context, SemIR::LocId loc_id, - clang::OverloadedOperatorKind op_kind, - llvm::ArrayRef arg_exprs) - -> SemIR::InstId; +namespace { +// Information about a C++ overloaded operator that we might map into a Carbon +// builtin function. +struct OverloadedOperatorInfo { + enum ReturnType { FirstArgType, Bool }; + + // The name for the function used to implement this operator. This is usually + // `Op`. This mostly only affects the mangled name, but might show up in + // diagnostics. + CoreIdentifier op_name = CoreIdentifier::Op; + + // The builtin function used to implement this operator. For now we're only + // supporting enum types, so this should be an int builtin. + SemIR::BuiltinFunctionKind builtin_kind = SemIR::BuiltinFunctionKind::None; + + // The return type to produce for the overloaded operator. + ReturnType return_type; +}; +} // namespace + +// Determine what kind of Carbon builtin function should be used to represent +// the given C++ overloaded operator. +static auto GetBuiltinOperatorInfo(clang::OverloadedOperatorKind kind) + -> OverloadedOperatorInfo { + using OperatorTable = + std::array; + static constexpr OperatorTable OpTable = [] { + OperatorTable table = {}; + + // Bitwise operators. In C++, the return type is computed with the usual + // arithmetic conversions, but we will just use the type of the arguments. + table[clang::OO_Amp] = { + .builtin_kind = SemIR::BuiltinFunctionKind::IntAnd, + .return_type = OverloadedOperatorInfo::ReturnType::FirstArgType}; + table[clang::OO_Pipe] = { + .builtin_kind = SemIR::BuiltinFunctionKind::IntOr, + .return_type = OverloadedOperatorInfo::ReturnType::FirstArgType}; + table[clang::OO_Caret] = { + .builtin_kind = SemIR::BuiltinFunctionKind::IntXor, + .return_type = OverloadedOperatorInfo::ReturnType::FirstArgType}; + table[clang::OO_Tilde] = { + .builtin_kind = SemIR::BuiltinFunctionKind::IntComplement, + .return_type = OverloadedOperatorInfo::ReturnType::FirstArgType}; + + // Comparison operators. + table[clang::OO_EqualEqual] = { + .op_name = CoreIdentifier::Equal, + .builtin_kind = SemIR::BuiltinFunctionKind::IntEq, + .return_type = OverloadedOperatorInfo::ReturnType::Bool}; + table[clang::OO_ExclaimEqual] = { + .op_name = CoreIdentifier::NotEqual, + .builtin_kind = SemIR::BuiltinFunctionKind::IntNeq, + .return_type = OverloadedOperatorInfo::ReturnType::Bool}; + table[clang::OO_Less] = { + .op_name = CoreIdentifier::Less, + .builtin_kind = SemIR::BuiltinFunctionKind::IntLess, + .return_type = OverloadedOperatorInfo::ReturnType::Bool}; + table[clang::OO_LessEqual] = { + .op_name = CoreIdentifier::LessOrEquivalent, + .builtin_kind = SemIR::BuiltinFunctionKind::IntLessEq, + .return_type = OverloadedOperatorInfo::ReturnType::Bool}; + table[clang::OO_Greater] = { + .op_name = CoreIdentifier::Greater, + .builtin_kind = SemIR::BuiltinFunctionKind::IntGreater, + .return_type = OverloadedOperatorInfo::ReturnType::Bool}; + table[clang::OO_GreaterEqual] = { + .op_name = CoreIdentifier::GreaterOrEquivalent, + .builtin_kind = SemIR::BuiltinFunctionKind::IntGreaterEq, + .return_type = OverloadedOperatorInfo::ReturnType::Bool}; + + return table; + }(); + return OpTable[kind]; +} + +// Builds a Carbon builtin function declaration corresponding to an overload +// candidate that selected a C++ builtin operator. Returns None if no +// corresponding builtin function could or should be built. +static auto TryBuildBuiltinOperator( + Context& context, SemIR::LocId loc_id, + clang::OverloadedOperatorKind op_kind, + clang::OverloadCandidateSet::iterator candidate) -> SemIR::InstId { + auto info = GetBuiltinOperatorInfo(op_kind); + if (info.builtin_kind == SemIR::BuiltinFunctionKind::None) { + return SemIR::InstId::None; + } + + // Import the argument types. For now, we only accept enum types. + // TODO: Consider expanding this to other types. + llvm::SmallVector arg_type_ids; + for (const auto& conversion : candidate->Conversions) { + // Get the type of the argument that overload resolution wanted to pass to + // the overload candidate, after any user-defined implicit conversions but + // before the final standard conversion sequence, to find an enum type prior + // to promotion. + clang::QualType converted_type; + if (conversion.isStandard()) { + converted_type = conversion.Standard.getFromType(); + } else if (conversion.isUserDefined()) { + converted_type = conversion.UserDefined.After.getFromType(); + } else { + // Unexpected kind of conversion sequence. + return SemIR::InstId::None; + } + if (!converted_type->isEnumeralType()) { + return SemIR::InstId::None; + } + auto arg_type_id = ImportCppType(context, loc_id, converted_type).type_id; + if (!arg_type_id.has_value() || arg_type_id == SemIR::ErrorInst::TypeId) { + return SemIR::InstId::None; + } + arg_type_ids.push_back(arg_type_id); + } + CARBON_CHECK(arg_type_ids.size() == 1 || arg_type_ids.size() == 2); + + // For now we only accept homogeneous operators. + if (arg_type_ids.size() == 2 && arg_type_ids[0] != arg_type_ids[1]) { + return SemIR::InstId::None; + } + + // Compute the return type. + auto return_type_id = SemIR::TypeId::None; + switch (info.return_type) { + case OverloadedOperatorInfo::FirstArgType: + return_type_id = arg_type_ids[0]; + break; + case OverloadedOperatorInfo::Bool: + return_type_id = + context.types().GetTypeIdForTypeInstId(SemIR::BoolType::TypeInstId); + break; + } + + return MakeBuiltinOperatorFunction(context, arg_type_ids, return_type_id, + info.op_name, info.builtin_kind); +} namespace { struct DiagnoseIncompleteOperandTypeInCppOperatorLookup { @@ -503,6 +636,11 @@ struct DiagnoseIncompleteOperandTypeInCppOperatorLookup { }; } // namespace +static auto FindClangOperator(Context& context, SemIR::LocId loc_id, + clang::OverloadedOperatorKind op_kind, + llvm::ArrayRef arg_exprs) + -> SemIR::InstId; + auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op, llvm::ArrayRef arg_type_ids) -> SemIR::InstId { @@ -554,7 +692,6 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op, if (arg_type_ids.size() == 1) { return FindClangOperator(context, loc_id, *op_kind, {&arg0.expression}); } - CARBON_CHECK(arg_type_ids.size() == 2); cpp_type = MapToCppType(context, arg_type_ids[1]); if (cpp_type.isNull()) { @@ -649,7 +786,10 @@ static auto FindClangOperator(Context& context, SemIR::LocId loc_id, if (!best_viable_fn->Function) { // The best viable candidate was a builtin. Let the Carbon operator // machinery handle that. - return SemIR::InstId::None; + CARBON_CHECK(!best_viable_fn->RewriteKind, + "Rewrite targeted builtin operator"); + return TryBuildBuiltinOperator(context, loc_id, op_kind, + best_viable_fn); } if (best_viable_fn->RewriteKind) { context.TODO( diff --git a/toolchain/check/custom_witness.cpp b/toolchain/check/custom_witness.cpp index fa64a5eccf8c..2373e0ad4875 100644 --- a/toolchain/check/custom_witness.cpp +++ b/toolchain/check/custom_witness.cpp @@ -44,24 +44,30 @@ static auto GetFacetAsType(Context& context, return context.types().GetTypeIdForTypeInstId(facet_or_type_id); } -// Returns a manufactured `Copy.Op` function with the `self` parameter typed -// to `self_type_id`. -static auto MakeCopyOpFunction(Context& context, SemIR::LocId loc_id, - SemIR::TypeId self_type_id, - SemIR::NameScopeId parent_scope_id) +// Returns a manufactured operator function. +auto MakeBuiltinOperatorFunction(Context& context, + llvm::ArrayRef param_types, + SemIR::TypeId return_type_id, + CoreIdentifier op_name, + SemIR::BuiltinFunctionKind builtin_kind, + SemIR::NameScopeId parent_scope_id) -> SemIR::InstId { - auto name_id = context.core_identifiers().AddNameId(CoreIdentifier::Op); + CARBON_CHECK(!param_types.empty()); + auto self_type_id = param_types.front(); + auto name_id = context.core_identifiers().AddNameId(op_name); auto [decl_id, function_id] = - MakeGeneratedFunctionDecl(context, loc_id, + MakeGeneratedFunctionDecl(context, SemIR::LocId::None, {.parent_scope_id = parent_scope_id, .name_id = name_id, .self_type_id = self_type_id, .self_kind = ParamPatternKind::Value, - .return_type_id = self_type_id}); + .param_type_ids = param_types.drop_front(), + .param_kind = ParamPatternKind::Value, + .return_type_id = return_type_id}); auto& function = context.functions().Get(function_id); - function.SetCoreWitness(SemIR::BuiltinFunctionKind::PrimitiveCopy); + function.SetCoreWitness(builtin_kind); return decl_id; } @@ -586,8 +592,9 @@ auto BuildPrimitiveCopyWitness( SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId { auto self_type_id = GetFacetAsType(context, query_self_const_id); - auto op_id = - MakeCopyOpFunction(context, loc_id, self_type_id, parent_scope_id); + auto op_id = MakeBuiltinOperatorFunction( + context, {self_type_id}, self_type_id, CoreIdentifier::Op, + SemIR::BuiltinFunctionKind::PrimitiveCopy, parent_scope_id); return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {op_id}); } diff --git a/toolchain/check/custom_witness.h b/toolchain/check/custom_witness.h index d44717e74979..96a2c3b9ee74 100644 --- a/toolchain/check/custom_witness.h +++ b/toolchain/check/custom_witness.h @@ -6,6 +6,7 @@ #define CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ #include "toolchain/check/context.h" +#include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { @@ -25,6 +26,17 @@ auto BuildPrimitiveCopyWitness( SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> SemIR::InstId; +// Returns a manufactured operator function. +// `param_types` contains the parameter types. The first element of +// `param_types` is treated as the `self` type, and any subsequent elements +// are treated as the types of the remaining explicit parameters. +auto MakeBuiltinOperatorFunction( + Context& context, llvm::ArrayRef param_types, + SemIR::TypeId return_type_id, CoreIdentifier op_name, + SemIR::BuiltinFunctionKind builtin_kind, + SemIR::NameScopeId parent_scope_id = SemIR::NameScopeId::None) + -> SemIR::InstId; + // Builds a witness that the given type is trivially destroyable. auto BuildTrivialDestroyWitness( Context& context, SemIR::LocId loc_id, diff --git a/toolchain/check/testdata/interop/cpp/enum/copy.carbon b/toolchain/check/testdata/interop/cpp/enum/copy.carbon index 1b6bb7ae305e..b6803057c78e 100644 --- a/toolchain/check/testdata/interop/cpp/enum/copy.carbon +++ b/toolchain/check/testdata/interop/cpp/enum/copy.carbon @@ -99,7 +99,5 @@ fn F() { // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @Enum.Op(%self.param: %Enum) -> out %return.param: %Enum = "primitive_copy"; -// CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Enum) = "no_op"; // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/enum/eq.carbon b/toolchain/check/testdata/interop/cpp/enum/eq.carbon new file mode 100644 index 000000000000..0cece24baaa6 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/enum/eq.carbon @@ -0,0 +1,87 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/enum/eq.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/enum/eq.carbon + +// --- enum.h + +enum Unscoped { A, B }; +enum class Scoped { X, Y }; + +// --- overloaded.h + +enum CustomEnum { X, Y }; +auto operator==(CustomEnum lhs, CustomEnum rhs) -> bool; +auto operator!=(CustomEnum lhs, CustomEnum rhs) -> bool; + +enum HeterogeneousUnscoped { A, B }; +enum class HeterogeneousScoped { X, Y }; +auto operator==(HeterogeneousScoped lhs, HeterogeneousUnscoped rhs) -> bool; +auto operator!=(HeterogeneousScoped lhs, HeterogeneousUnscoped rhs) -> bool; + +// --- eq.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "enum.h"; + +fn CompareGeneric[U:! type, T:! Core.EqWith(U)](x: T, y: U) -> bool { + return x == y; +} +fn CompareUnscoped(x: Cpp.Unscoped, y: Cpp.Unscoped) -> bool { + return x == y; +} + +fn CompareScoped(x: Cpp.Scoped, y: Cpp.Scoped) -> bool { + return x == y; +} + +fn CallCompareGeneric(x: Cpp.Unscoped, y: Cpp.Unscoped) -> bool { + return CompareGeneric(x, y); +} + +// --- overloaded_op.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "overloaded.h"; + +fn CompareGeneric[U:! type, T:! Core.EqWith(U)](x: T, y: U) -> bool { + return x == y; +} + +fn CompareCustom(x: Cpp.CustomEnum, y: Cpp.CustomEnum) -> bool { + return x == y; +} + +fn CompareHeterogeneous(x: Cpp.HeterogeneousScoped, y: Cpp.HeterogeneousUnscoped) -> bool { + return CompareGeneric(x, y); +} + +// --- fail_heterogeneous.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "enum.h"; + +fn CompareGeneric[U:! type, T:! Core.EqWith(U)](x: T, y: U) -> bool { + return x == y; +} + +fn CompareHeterogeneousFail(x: Cpp.Scoped, y: Cpp.Unscoped) -> bool { + // CHECK:STDERR: fail_heterogeneous.carbon:[[@LINE+7]]:10: error: cannot convert type `Cpp.Scoped` into type implementing `Core.EqWith(Cpp.Unscoped)` [ConversionFailureTypeToFacet] + // CHECK:STDERR: return CompareGeneric(x, y); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_heterogeneous.carbon:[[@LINE-8]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] + // CHECK:STDERR: fn CompareGeneric[U:! type, T:! Core.EqWith(U)](x: T, y: U) -> bool { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + return CompareGeneric(x, y); +} diff --git a/toolchain/check/testdata/interop/cpp/operators/builtin_candidates.carbon b/toolchain/check/testdata/interop/cpp/operators/builtin_candidates.carbon new file mode 100644 index 000000000000..d97a6329f27b --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/operators/builtin_candidates.carbon @@ -0,0 +1,307 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/operators/builtin_candidates.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/operators/builtin_candidates.carbon + +// --- enum_operators.carbon + +library "[[@TEST_NAME]]"; + +import Cpp inline '''c++ +enum E { e1, e2, e3 }; +enum class EC { e1, e2, e3 }; +'''; + +fn TestEnum(a: Cpp.E, b: Cpp.E) { + let _: Cpp.E = a & b; + let _: Cpp.E = a | b; + let _: Cpp.E = a ^ b; + let _: Cpp.E = ^a; + + let _: bool = a == b; + let _: bool = a != b; + let _: bool = a < b; + let _: bool = a <= b; + let _: bool = a > b; + let _: bool = a >= b; +} + +fn TestEnumClass(a: Cpp.EC, b: Cpp.EC) { + let _: bool = a == b; + let _: bool = a != b; + let _: bool = a < b; + let _: bool = a <= b; + let _: bool = a > b; + let _: bool = a >= b; +} + +// --- fail_enum_class_bitwise_operators.carbon + +library "[[@TEST_NAME]]"; + +import Cpp inline '''c++ +enum class EC { e1, e2, e3 }; +'''; + +fn TestEnumClass(a: Cpp.EC, b: Cpp.EC) { + // CHECK:STDERR: fail_enum_class_bitwise_operators.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.BitAndWith(Cpp.EC)` in type `Cpp.EC` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: Cpp.EC = a & b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: Cpp.EC = a & b; + // CHECK:STDERR: fail_enum_class_bitwise_operators.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.BitOrWith(Cpp.EC)` in type `Cpp.EC` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: Cpp.EC = a | b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: Cpp.EC = a | b; + // CHECK:STDERR: fail_enum_class_bitwise_operators.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.BitXorWith(Cpp.EC)` in type `Cpp.EC` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: Cpp.EC = a ^ b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: Cpp.EC = a ^ b; + // CHECK:STDERR: fail_enum_class_bitwise_operators.carbon:[[@LINE+4]]:19: error: cannot access member of interface `Core.BitComplement` in type `Cpp.EC` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: Cpp.EC = ^a; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + let _: Cpp.EC = ^a; +} + +// --- enum_operators_with_conversion.carbon + +import Cpp inline '''c++ +enum E {}; +struct ConvertibleToEnum { + operator E() const; +}; +'''; + +fn TestEnumVsConvertibleToEnum(a: Cpp.E, b: Cpp.ConvertibleToEnum) { + let _: Cpp.E = a & b; + let _: Cpp.E = a | b; + let _: Cpp.E = a ^ b; + let _: Cpp.E = ^a; + + let _: bool = a == b; + let _: bool = a != b; + let _: bool = a < b; + let _: bool = a <= b; + let _: bool = a > b; + let _: bool = a >= b; +} + +fn TestConvertibleToEnumVsEnum(a: Cpp.ConvertibleToEnum, b: Cpp.E) { + let _: Cpp.E = a & b; + let _: Cpp.E = a | b; + let _: Cpp.E = a ^ b; + let _: Cpp.E = ^a; + + let _: bool = a == b; + let _: bool = a != b; + let _: bool = a < b; + let _: bool = a <= b; + let _: bool = a > b; + let _: bool = a >= b; +} + +// TODO: Should we allow these? For consistency with the cases in the next +// split, we should not, but we currently do. +fn TestConversionOnBothOperands(a: Cpp.ConvertibleToEnum, b: Cpp.E) { + let _: Cpp.E = a & b; + let _: Cpp.E = a | b; + let _: Cpp.E = a ^ b; + let _: Cpp.E = ^a; + + let _: bool = a == b; + let _: bool = a != b; + let _: bool = a < b; + let _: bool = a <= b; + let _: bool = a > b; + let _: bool = a >= b; +} + +// --- fail_convert_all_operands.carbon + +// We do not currently support calls to builtin candidates where all operands +// are converted from class type to a primitive type. This is done to match +// Carbon rules. + +library "[[@TEST_NAME]]"; + +import Cpp inline '''c++ +struct ConvertibleToInt { + operator int() const; +}; +struct ConvertibleToFloat { + operator float() const; +}; +'''; + +fn TestInteger(a: Cpp.ConvertibleToInt, b: Cpp.ConvertibleToInt) { + // Test arithmetic. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.AddWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a + b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a + b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.SubWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a - b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a - b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.MulWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a * b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a * b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.DivWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a / b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a / b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.ModWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a % b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a % b; + + // Test bitwise. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.BitAndWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a & b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a & b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.BitOrWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a | b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a | b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.BitXorWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a ^ b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: i32 = a ^ b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.LeftShiftWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a << b; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: i32 = a << b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.RightShiftWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = a >> b; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: i32 = a >> b; + + // Test comparison. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.EqWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a == b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a == b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.EqWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a != b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a != b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a < b); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: bool = (a < b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a <= b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a <= b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a > b); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: bool = (a > b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToInt)` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a >= b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a >= b); + + // Test unary. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.Negate` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = -a; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + let _: i32 = -a; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.BitComplement` in type `Cpp.ConvertibleToInt` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: i32 = ^a; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + let _: i32 = ^a; +} + +fn TestFloat(a: Cpp.ConvertibleToFloat, b: Cpp.ConvertibleToFloat) { + // Test arithmetic. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.AddWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: f32 = a + b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: f32 = a + b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.SubWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: f32 = a - b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: f32 = a - b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.MulWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: f32 = a * b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: f32 = a * b; + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.DivWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: f32 = a / b; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: f32 = a / b; + + // Test comparison. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.EqWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a == b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a == b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.EqWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a != b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a != b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a < b); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: bool = (a < b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a <= b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a <= b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a > b); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + let _: bool = (a > b); + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:18: error: cannot access member of interface `Core.OrderedWith(Cpp.ConvertibleToFloat)` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: bool = (a >= b); + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + let _: bool = (a >= b); + + // Test unary. + // CHECK:STDERR: fail_convert_all_operands.carbon:[[@LINE+4]]:16: error: cannot access member of interface `Core.Negate` in type `Cpp.ConvertibleToFloat` that does not implement that interface [MissingImplInMemberAccess] + // CHECK:STDERR: let _: f32 = -a; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + let _: f32 = -a; +} diff --git a/toolchain/lower/testdata/interop/cpp/enum.carbon b/toolchain/lower/testdata/interop/cpp/enum.carbon index b4ee11c39e9c..710fb3ea006d 100644 --- a/toolchain/lower/testdata/interop/cpp/enum.carbon +++ b/toolchain/lower/testdata/interop/cpp/enum.carbon @@ -74,11 +74,86 @@ library "[[@TEST_NAME]]"; import Cpp library "bitmask.h"; -// TODO: We should be able to use `|` below rather than declaring our own builtin. -fn BitOr(a: Cpp.Bits, b: Cpp.Bits) -> Cpp.Bits = "int.or"; +fn CompileTime() { + Cpp.Take(Cpp.A & Cpp.C); + Cpp.Take(Cpp.A | Cpp.C); + Cpp.Take(Cpp.A ^ Cpp.C); + Cpp.Take(^Cpp.A); +} -fn Call() { - Cpp.Take(BitOr(Cpp.A, Cpp.C)); +fn Runtime(a: Cpp.Bits, b: Cpp.Bits) { + Cpp.Take(a & b); + Cpp.Take(a | b); + Cpp.Take(a ^ b); + // TODO: This produces a value with bits set that are not in the range of + // representable values of the enumeration. Should we produce `^a & 7` + // instead? + Cpp.Take(^a); +} + +// --- compare.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "enum_member.h"; + +fn Compare(a: Cpp.C.E, b: Cpp.C.E) -> bool { + return a == b; +} + +fn CompareGeneric[U:! type, T:! Core.EqWith(U)](x: T, y: U) -> bool { + return x == y; +} + +fn CallCompareGeneric(a: Cpp.C.E, b: Cpp.C.E) -> bool { + return CompareGeneric(a, b); +} + +// --- compare_class.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "enum_member.h"; + +inline Cpp ''' +enum E2 {}; +struct ConvertToEnum { + operator C::E() const; + operator E2() const; +}; +struct ConvertToEnum2 { + operator C::E() const; +}; +'''; + +fn Compare(a: Cpp.C.E, b: Cpp.ConvertToEnum) -> bool { + return a == b; +} + +fn Compare2(a: Cpp.E2, b: Cpp.ConvertToEnum) -> bool { + return a == b; +} + +fn Compare3(a: Cpp.C.E, b: Cpp.ConvertToEnum2) -> bool { + return a == b; +} + +fn CompareGeneric[U:! type, T:! Core.EqWith(U)](x: T, y: U) -> bool { + return x == y; +} + +// Vary both arguments to ensure they're both included in the fingerprint for +// the thunk. +fn CallCompareGeneric(a: Cpp.C.E, b: Cpp.ConvertToEnum) -> bool { + return CompareGeneric(a, b); +} + +fn CallCompareGeneric2(a: Cpp.E2, b: Cpp.ConvertToEnum) -> bool { + return CompareGeneric(a, b); +} + +fn CallCompareGeneric3(a: Cpp.C.E, b: Cpp.ConvertToEnum2) -> bool { + return CompareGeneric(a, b); } // CHECK:STDOUT: ; ModuleID = 'pass_as_arg.carbon' @@ -275,14 +350,34 @@ fn Call() { // CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CCall.Main() #0 !dbg !11 { +// CHECK:STDOUT: define void @_CCompileTime.Main() #0 !dbg !11 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @_Z4Take4Bits(i32 5), !dbg !14 -// CHECK:STDOUT: ret void, !dbg !15 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 0), !dbg !14 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 5), !dbg !15 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 5), !dbg !16 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 -2), !dbg !17 +// CHECK:STDOUT: ret void, !dbg !18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: declare void @_Z4Take4Bits(i32 noundef) #1 // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CRuntime.Main(i32 %a, i32 %b) #0 !dbg !19 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %Op.call.loc14 = and i32 %a, %b, !dbg !26 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 %Op.call.loc14), !dbg !27 +// CHECK:STDOUT: %Op.call.loc15 = or i32 %a, %b, !dbg !28 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 %Op.call.loc15), !dbg !29 +// CHECK:STDOUT: %Op.call.loc16 = xor i32 %a, %b, !dbg !30 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 %Op.call.loc16), !dbg !31 +// CHECK:STDOUT: %Op.call.loc20 = xor i32 -1, %a, !dbg !32 +// CHECK:STDOUT: call void @_Z4Take4Bits(i32 %Op.call.loc20), !dbg !33 +// CHECK:STDOUT: ret void, !dbg !34 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_Z4Take4Bits, { 0, 1, 2, 3, 7, 6, 5, 4 } +// CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } // CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } // CHECK:STDOUT: @@ -301,8 +396,403 @@ fn Call() { // CHECK:STDOUT: !8 = !{!"int", !9, i64 0} // CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} // CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} -// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Call", linkageName: "_CCall.Main", scope: null, file: !1, line: 9, type: !12, spFlags: DISPFlagDefinition, unit: !0) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "CompileTime", linkageName: "_CCompileTime.Main", scope: null, file: !1, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !0) // CHECK:STDOUT: !12 = !DISubroutineType(types: !13) // CHECK:STDOUT: !13 = !{null} -// CHECK:STDOUT: !14 = !DILocation(line: 10, column: 3, scope: !11) -// CHECK:STDOUT: !15 = !DILocation(line: 9, column: 1, scope: !11) +// CHECK:STDOUT: !14 = !DILocation(line: 7, column: 3, scope: !11) +// CHECK:STDOUT: !15 = !DILocation(line: 8, column: 3, scope: !11) +// CHECK:STDOUT: !16 = !DILocation(line: 9, column: 3, scope: !11) +// CHECK:STDOUT: !17 = !DILocation(line: 10, column: 3, scope: !11) +// CHECK:STDOUT: !18 = !DILocation(line: 6, column: 1, scope: !11) +// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "Runtime", linkageName: "_CRuntime.Main", scope: null, file: !1, line: 13, type: !20, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !23) +// CHECK:STDOUT: !20 = !DISubroutineType(types: !21) +// CHECK:STDOUT: !21 = !{null, !22, !22} +// CHECK:STDOUT: !22 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_unsigned) +// CHECK:STDOUT: !23 = !{!24, !25} +// CHECK:STDOUT: !24 = !DILocalVariable(arg: 1, scope: !19, type: !22) +// CHECK:STDOUT: !25 = !DILocalVariable(arg: 2, scope: !19, type: !22) +// CHECK:STDOUT: !26 = !DILocation(line: 14, column: 12, scope: !19) +// CHECK:STDOUT: !27 = !DILocation(line: 14, column: 3, scope: !19) +// CHECK:STDOUT: !28 = !DILocation(line: 15, column: 12, scope: !19) +// CHECK:STDOUT: !29 = !DILocation(line: 15, column: 3, scope: !19) +// CHECK:STDOUT: !30 = !DILocation(line: 16, column: 12, scope: !19) +// CHECK:STDOUT: !31 = !DILocation(line: 16, column: 3, scope: !19) +// CHECK:STDOUT: !32 = !DILocation(line: 20, column: 12, scope: !19) +// CHECK:STDOUT: !33 = !DILocation(line: 20, column: 3, scope: !19) +// CHECK:STDOUT: !34 = !DILocation(line: 13, column: 1, scope: !19) +// CHECK:STDOUT: ; ModuleID = 'compare.carbon' +// CHECK:STDOUT: source_filename = "compare.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCompare.Main(i16 %a, i16 %b) #0 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %Equal.call = icmp eq i16 %a, %b, !dbg !19 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !20 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCallCompareGeneric.Main(i16 %a, i16 %b) #0 !dbg !21 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %CompareGeneric.call = call i1 @_CCompareGeneric.Main.80c3fc0239809458(i16 %a, i16 %b), !dbg !25 +// CHECK:STDOUT: ret i1 %CompareGeneric.call, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i1 @_CCompareGeneric.Main.80c3fc0239809458(i16 %x, i16 %y) #0 !dbg !27 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %EqWith.WithSelf.Equal.call = icmp eq i16 %x, %y, !dbg !31 +// CHECK:STDOUT: ret i1 %EqWith.WithSelf.Equal.call, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3, !4, !5, !6} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "compare.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !6 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Compare", linkageName: "_CCompare.Main", scope: null, file: !1, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !16) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{!14, !15, !15} +// CHECK:STDOUT: !14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !15 = !DIBasicType(name: "int", size: 16, encoding: DW_ATE_signed) +// CHECK:STDOUT: !16 = !{!17, !18} +// CHECK:STDOUT: !17 = !DILocalVariable(arg: 1, scope: !11, type: !15) +// CHECK:STDOUT: !18 = !DILocalVariable(arg: 2, scope: !11, type: !15) +// CHECK:STDOUT: !19 = !DILocation(line: 7, column: 10, scope: !11) +// CHECK:STDOUT: !20 = !DILocation(line: 7, column: 3, scope: !11) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "CallCompareGeneric", linkageName: "_CCallCompareGeneric.Main", scope: null, file: !1, line: 14, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !22) +// CHECK:STDOUT: !22 = !{!23, !24} +// CHECK:STDOUT: !23 = !DILocalVariable(arg: 1, scope: !21, type: !15) +// CHECK:STDOUT: !24 = !DILocalVariable(arg: 2, scope: !21, type: !15) +// CHECK:STDOUT: !25 = !DILocation(line: 15, column: 10, scope: !21) +// CHECK:STDOUT: !26 = !DILocation(line: 15, column: 3, scope: !21) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "CompareGeneric", linkageName: "_CCompareGeneric.Main.80c3fc0239809458", scope: null, file: !1, line: 10, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !28) +// CHECK:STDOUT: !28 = !{!29, !30} +// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !27, type: !15) +// CHECK:STDOUT: !30 = !DILocalVariable(arg: 2, scope: !27, type: !15) +// CHECK:STDOUT: !31 = !DILocation(line: 11, column: 10, scope: !27) +// CHECK:STDOUT: !32 = !DILocation(line: 11, column: 3, scope: !27) +// CHECK:STDOUT: ; ModuleID = 'compare_class.carbon' +// CHECK:STDOUT: source_filename = "compare_class.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCompare.Main(i16 %a, ptr %b) #0 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc18_15.1.temp = alloca i16, align 2, !dbg !19 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc18_15.1.temp), !dbg !19 +// CHECK:STDOUT: call void @_ZNK13ConvertToEnumcvN1C1EEEv.carbon_thunk._(ptr %b, ptr %.loc18_15.1.temp), !dbg !19 +// CHECK:STDOUT: %.loc18_15.5 = load i16, ptr %.loc18_15.1.temp, align 2, !dbg !19 +// CHECK:STDOUT: %Equal.call = icmp eq i16 %a, %.loc18_15.5, !dbg !20 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable +// CHECK:STDOUT: define internal void @_ZNK13ConvertToEnumcvN1C1EEEv.carbon_thunk._(ptr noundef nonnull align 1 dereferenceable(1) %this, ptr noundef %return) #1 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: %return.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !22 +// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8, !tbaa !25 +// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8, !tbaa !25 +// CHECK:STDOUT: %1 = load ptr, ptr %this.addr, align 8, !tbaa !22, !nonnull !26 +// CHECK:STDOUT: %call = call noundef signext i16 @_ZNK13ConvertToEnumcvN1C1EEEv(ptr noundef nonnull align 1 dereferenceable(1) %1) +// CHECK:STDOUT: store i16 %call, ptr %0, align 2, !tbaa !27 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCompare2.Main(i32 %a, ptr %b) #0 !dbg !29 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %__carbon_thunk.call = call i32 @_ZNK13ConvertToEnumcv2E2Ev.carbon_thunk._(ptr %b), !dbg !36 +// CHECK:STDOUT: %Equal.call = icmp eq i32 %a, %__carbon_thunk.call, !dbg !37 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !38 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable +// CHECK:STDOUT: define internal noundef i32 @_ZNK13ConvertToEnumcv2E2Ev.carbon_thunk._(ptr noundef nonnull align 1 dereferenceable(1) %this) #1 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !22 +// CHECK:STDOUT: %0 = load ptr, ptr %this.addr, align 8, !tbaa !22, !nonnull !26 +// CHECK:STDOUT: %call = call noundef i32 @_ZNK13ConvertToEnumcv2E2Ev(ptr noundef nonnull align 1 dereferenceable(1) %0) +// CHECK:STDOUT: ret i32 %call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCompare3.Main(i16 %a, ptr %b) #0 !dbg !39 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc26_15.1.temp = alloca i16, align 2, !dbg !43 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc26_15.1.temp), !dbg !43 +// CHECK:STDOUT: call void @_ZNK14ConvertToEnum2cvN1C1EEEv.carbon_thunk._(ptr %b, ptr %.loc26_15.1.temp), !dbg !43 +// CHECK:STDOUT: %.loc26_15.5 = load i16, ptr %.loc26_15.1.temp, align 2, !dbg !43 +// CHECK:STDOUT: %Equal.call = icmp eq i16 %a, %.loc26_15.5, !dbg !44 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !45 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable +// CHECK:STDOUT: define internal void @_ZNK14ConvertToEnum2cvN1C1EEEv.carbon_thunk._(ptr noundef nonnull align 1 dereferenceable(1) %this, ptr noundef %return) #1 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: %return.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !46 +// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8, !tbaa !25 +// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8, !tbaa !25 +// CHECK:STDOUT: %1 = load ptr, ptr %this.addr, align 8, !tbaa !46, !nonnull !26 +// CHECK:STDOUT: %call = call noundef signext i16 @_ZNK14ConvertToEnum2cvN1C1EEEv(ptr noundef nonnull align 1 dereferenceable(1) %1) +// CHECK:STDOUT: store i16 %call, ptr %0, align 2, !tbaa !27 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCallCompareGeneric.Main(i16 %a, ptr %b) #0 !dbg !48 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %CompareGeneric.call = call i1 @_CCompareGeneric.Main.215a23369ed4d589(i16 %a, ptr %b), !dbg !52 +// CHECK:STDOUT: ret i1 %CompareGeneric.call, !dbg !53 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define i1 @"_CEqual:thunk:EqWith.83b9626e83cf3dcc.Core:enclosed"(i16 %self, ptr %other) #2 !dbg !54 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.4.temp = alloca i16, align 2, !dbg !58 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.4.temp), !dbg !58 +// CHECK:STDOUT: call void @_ZNK13ConvertToEnumcvN1C1EEEv.carbon_thunk._(ptr %other, ptr %.4.temp), !dbg !58 +// CHECK:STDOUT: %.8 = load i16, ptr %.4.temp, align 2, !dbg !58 +// CHECK:STDOUT: %Equal.call = icmp eq i16 %self, %.8, !dbg !58 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !58 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define i1 @"_CNotEqual:thunk:EqWith.83b9626e83cf3dcc.Core:enclosed"(i16 %self, ptr %other) #2 !dbg !59 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.4.temp = alloca i16, align 2, !dbg !63 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.4.temp), !dbg !63 +// CHECK:STDOUT: call void @_ZNK13ConvertToEnumcvN1C1EEEv.carbon_thunk._(ptr %other, ptr %.4.temp), !dbg !63 +// CHECK:STDOUT: %.8 = load i16, ptr %.4.temp, align 2, !dbg !63 +// CHECK:STDOUT: %NotEqual.call = icmp ne i16 %self, %.8, !dbg !63 +// CHECK:STDOUT: ret i1 %NotEqual.call, !dbg !63 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCallCompareGeneric2.Main(i32 %a, ptr %b) #0 !dbg !64 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %CompareGeneric.call = call i1 @_CCompareGeneric.Main.a5489e454367d917(i32 %a, ptr %b), !dbg !68 +// CHECK:STDOUT: ret i1 %CompareGeneric.call, !dbg !69 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define i1 @"_CEqual:thunk:EqWith.e8fe48de943d9652.Core:enclosed"(i32 %self, ptr %other) #2 !dbg !70 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %__carbon_thunk.call = call i32 @_ZNK13ConvertToEnumcv2E2Ev.carbon_thunk._(ptr %other), !dbg !74 +// CHECK:STDOUT: %Equal.call = icmp eq i32 %self, %__carbon_thunk.call, !dbg !74 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !74 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define i1 @"_CNotEqual:thunk:EqWith.e8fe48de943d9652.Core:enclosed"(i32 %self, ptr %other) #2 !dbg !75 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %__carbon_thunk.call = call i32 @_ZNK13ConvertToEnumcv2E2Ev.carbon_thunk._(ptr %other), !dbg !79 +// CHECK:STDOUT: %NotEqual.call = icmp ne i32 %self, %__carbon_thunk.call, !dbg !79 +// CHECK:STDOUT: ret i1 %NotEqual.call, !dbg !79 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i1 @_CCallCompareGeneric3.Main(i16 %a, ptr %b) #0 !dbg !80 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %CompareGeneric.call = call i1 @_CCompareGeneric.Main.64b737572447efad(i16 %a, ptr %b), !dbg !84 +// CHECK:STDOUT: ret i1 %CompareGeneric.call, !dbg !85 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define i1 @"_CEqual:thunk:EqWith.a294139a6615e163.Core:enclosed"(i16 %self, ptr %other) #2 !dbg !86 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.4.temp = alloca i16, align 2, !dbg !90 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.4.temp), !dbg !90 +// CHECK:STDOUT: call void @_ZNK14ConvertToEnum2cvN1C1EEEv.carbon_thunk._(ptr %other, ptr %.4.temp), !dbg !90 +// CHECK:STDOUT: %.8 = load i16, ptr %.4.temp, align 2, !dbg !90 +// CHECK:STDOUT: %Equal.call = icmp eq i16 %self, %.8, !dbg !90 +// CHECK:STDOUT: ret i1 %Equal.call, !dbg !90 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define i1 @"_CNotEqual:thunk:EqWith.a294139a6615e163.Core:enclosed"(i16 %self, ptr %other) #2 !dbg !91 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.4.temp = alloca i16, align 2, !dbg !95 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.4.temp), !dbg !95 +// CHECK:STDOUT: call void @_ZNK14ConvertToEnum2cvN1C1EEEv.carbon_thunk._(ptr %other, ptr %.4.temp), !dbg !95 +// CHECK:STDOUT: %.8 = load i16, ptr %.4.temp, align 2, !dbg !95 +// CHECK:STDOUT: %NotEqual.call = icmp ne i16 %self, %.8, !dbg !95 +// CHECK:STDOUT: ret i1 %NotEqual.call, !dbg !95 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #3 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i1 @_CCompareGeneric.Main.215a23369ed4d589(i16 %x, ptr %y) #0 !dbg !96 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %EqWith.WithSelf.Equal.call = call i1 @"_CEqual:thunk:EqWith.83b9626e83cf3dcc.Core:enclosed"(i16 %x, ptr %y), !dbg !100 +// CHECK:STDOUT: ret i1 %EqWith.WithSelf.Equal.call, !dbg !101 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i1 @_CCompareGeneric.Main.a5489e454367d917(i32 %x, ptr %y) #0 !dbg !102 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %EqWith.WithSelf.Equal.call = call i1 @"_CEqual:thunk:EqWith.e8fe48de943d9652.Core:enclosed"(i32 %x, ptr %y), !dbg !106 +// CHECK:STDOUT: ret i1 %EqWith.WithSelf.Equal.call, !dbg !107 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i1 @_CCompareGeneric.Main.64b737572447efad(i16 %x, ptr %y) #0 !dbg !108 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %EqWith.WithSelf.Equal.call = call i1 @"_CEqual:thunk:EqWith.a294139a6615e163.Core:enclosed"(i16 %x, ptr %y), !dbg !112 +// CHECK:STDOUT: ret i1 %EqWith.WithSelf.Equal.call, !dbg !113 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare noundef signext i16 @_ZNK13ConvertToEnumcvN1C1EEEv(ptr noundef nonnull align 1 dereferenceable(1)) #4 +// CHECK:STDOUT: +// CHECK:STDOUT: declare noundef i32 @_ZNK13ConvertToEnumcv2E2Ev(ptr noundef nonnull align 1 dereferenceable(1)) #4 +// CHECK:STDOUT: +// CHECK:STDOUT: declare noundef signext i16 @_ZNK14ConvertToEnum2cvN1C1EEEv(ptr noundef nonnull align 1 dereferenceable(1)) #4 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 5, 4, 3, 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #2 = { alwaysinline nounwind } +// CHECK:STDOUT: attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #4 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!0} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3, !4, !5, !6} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !1 = !DIFile(filename: "compare_class.carbon", directory: "") +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !6 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Compare", linkageName: "_CCompare.Main", scope: null, file: !1, line: 17, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !16) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{!14, !15, !14} +// CHECK:STDOUT: !14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !15 = !DIBasicType(name: "int", size: 16, encoding: DW_ATE_signed) +// CHECK:STDOUT: !16 = !{!17, !18} +// CHECK:STDOUT: !17 = !DILocalVariable(arg: 1, scope: !11, type: !15) +// CHECK:STDOUT: !18 = !DILocalVariable(arg: 2, scope: !11, type: !14) +// CHECK:STDOUT: !19 = !DILocation(line: 18, column: 15, scope: !11) +// CHECK:STDOUT: !20 = !DILocation(line: 18, column: 10, scope: !11) +// CHECK:STDOUT: !21 = !DILocation(line: 18, column: 3, scope: !11) +// CHECK:STDOUT: !22 = !{!23, !23, i64 0} +// CHECK:STDOUT: !23 = !{!"p1 _ZTS13ConvertToEnum", !24, i64 0} +// CHECK:STDOUT: !24 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !25 = !{!24, !24, i64 0} +// CHECK:STDOUT: !26 = !{} +// CHECK:STDOUT: !27 = !{!28, !28, i64 0} +// CHECK:STDOUT: !28 = !{!"_ZTSN1C1EE", !9, i64 0} +// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Compare2", linkageName: "_CCompare2.Main", scope: null, file: !1, line: 21, type: !30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +// CHECK:STDOUT: !30 = !DISubroutineType(types: !31) +// CHECK:STDOUT: !31 = !{!14, !32, !14} +// CHECK:STDOUT: !32 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_unsigned) +// CHECK:STDOUT: !33 = !{!34, !35} +// CHECK:STDOUT: !34 = !DILocalVariable(arg: 1, scope: !29, type: !32) +// CHECK:STDOUT: !35 = !DILocalVariable(arg: 2, scope: !29, type: !14) +// CHECK:STDOUT: !36 = !DILocation(line: 22, column: 15, scope: !29) +// CHECK:STDOUT: !37 = !DILocation(line: 22, column: 10, scope: !29) +// CHECK:STDOUT: !38 = !DILocation(line: 22, column: 3, scope: !29) +// CHECK:STDOUT: !39 = distinct !DISubprogram(name: "Compare3", linkageName: "_CCompare3.Main", scope: null, file: !1, line: 25, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !40) +// CHECK:STDOUT: !40 = !{!41, !42} +// CHECK:STDOUT: !41 = !DILocalVariable(arg: 1, scope: !39, type: !15) +// CHECK:STDOUT: !42 = !DILocalVariable(arg: 2, scope: !39, type: !14) +// CHECK:STDOUT: !43 = !DILocation(line: 26, column: 15, scope: !39) +// CHECK:STDOUT: !44 = !DILocation(line: 26, column: 10, scope: !39) +// CHECK:STDOUT: !45 = !DILocation(line: 26, column: 3, scope: !39) +// CHECK:STDOUT: !46 = !{!47, !47, i64 0} +// CHECK:STDOUT: !47 = !{!"p1 _ZTS14ConvertToEnum2", !24, i64 0} +// CHECK:STDOUT: !48 = distinct !DISubprogram(name: "CallCompareGeneric", linkageName: "_CCallCompareGeneric.Main", scope: null, file: !1, line: 35, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !49) +// CHECK:STDOUT: !49 = !{!50, !51} +// CHECK:STDOUT: !50 = !DILocalVariable(arg: 1, scope: !48, type: !15) +// CHECK:STDOUT: !51 = !DILocalVariable(arg: 2, scope: !48, type: !14) +// CHECK:STDOUT: !52 = !DILocation(line: 36, column: 10, scope: !48) +// CHECK:STDOUT: !53 = !DILocation(line: 36, column: 3, scope: !48) +// CHECK:STDOUT: !54 = distinct !DISubprogram(name: "Equal", linkageName: "_CEqual:thunk:EqWith.83b9626e83cf3dcc.Core:enclosed", scope: null, file: !1, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !55) +// CHECK:STDOUT: !55 = !{!56, !57} +// CHECK:STDOUT: !56 = !DILocalVariable(arg: 1, scope: !54, type: !15) +// CHECK:STDOUT: !57 = !DILocalVariable(arg: 2, scope: !54, type: !14) +// CHECK:STDOUT: !58 = !DILocation(line: 0, scope: !54) +// CHECK:STDOUT: !59 = distinct !DISubprogram(name: "NotEqual", linkageName: "_CNotEqual:thunk:EqWith.83b9626e83cf3dcc.Core:enclosed", scope: null, file: !1, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !60) +// CHECK:STDOUT: !60 = !{!61, !62} +// CHECK:STDOUT: !61 = !DILocalVariable(arg: 1, scope: !59, type: !15) +// CHECK:STDOUT: !62 = !DILocalVariable(arg: 2, scope: !59, type: !14) +// CHECK:STDOUT: !63 = !DILocation(line: 0, scope: !59) +// CHECK:STDOUT: !64 = distinct !DISubprogram(name: "CallCompareGeneric2", linkageName: "_CCallCompareGeneric2.Main", scope: null, file: !1, line: 39, type: !30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !65) +// CHECK:STDOUT: !65 = !{!66, !67} +// CHECK:STDOUT: !66 = !DILocalVariable(arg: 1, scope: !64, type: !32) +// CHECK:STDOUT: !67 = !DILocalVariable(arg: 2, scope: !64, type: !14) +// CHECK:STDOUT: !68 = !DILocation(line: 40, column: 10, scope: !64) +// CHECK:STDOUT: !69 = !DILocation(line: 40, column: 3, scope: !64) +// CHECK:STDOUT: !70 = distinct !DISubprogram(name: "Equal", linkageName: "_CEqual:thunk:EqWith.e8fe48de943d9652.Core:enclosed", scope: null, file: !1, type: !30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !71) +// CHECK:STDOUT: !71 = !{!72, !73} +// CHECK:STDOUT: !72 = !DILocalVariable(arg: 1, scope: !70, type: !32) +// CHECK:STDOUT: !73 = !DILocalVariable(arg: 2, scope: !70, type: !14) +// CHECK:STDOUT: !74 = !DILocation(line: 0, scope: !70) +// CHECK:STDOUT: !75 = distinct !DISubprogram(name: "NotEqual", linkageName: "_CNotEqual:thunk:EqWith.e8fe48de943d9652.Core:enclosed", scope: null, file: !1, type: !30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !76) +// CHECK:STDOUT: !76 = !{!77, !78} +// CHECK:STDOUT: !77 = !DILocalVariable(arg: 1, scope: !75, type: !32) +// CHECK:STDOUT: !78 = !DILocalVariable(arg: 2, scope: !75, type: !14) +// CHECK:STDOUT: !79 = !DILocation(line: 0, scope: !75) +// CHECK:STDOUT: !80 = distinct !DISubprogram(name: "CallCompareGeneric3", linkageName: "_CCallCompareGeneric3.Main", scope: null, file: !1, line: 43, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !81) +// CHECK:STDOUT: !81 = !{!82, !83} +// CHECK:STDOUT: !82 = !DILocalVariable(arg: 1, scope: !80, type: !15) +// CHECK:STDOUT: !83 = !DILocalVariable(arg: 2, scope: !80, type: !14) +// CHECK:STDOUT: !84 = !DILocation(line: 44, column: 10, scope: !80) +// CHECK:STDOUT: !85 = !DILocation(line: 44, column: 3, scope: !80) +// CHECK:STDOUT: !86 = distinct !DISubprogram(name: "Equal", linkageName: "_CEqual:thunk:EqWith.a294139a6615e163.Core:enclosed", scope: null, file: !1, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !87) +// CHECK:STDOUT: !87 = !{!88, !89} +// CHECK:STDOUT: !88 = !DILocalVariable(arg: 1, scope: !86, type: !15) +// CHECK:STDOUT: !89 = !DILocalVariable(arg: 2, scope: !86, type: !14) +// CHECK:STDOUT: !90 = !DILocation(line: 0, scope: !86) +// CHECK:STDOUT: !91 = distinct !DISubprogram(name: "NotEqual", linkageName: "_CNotEqual:thunk:EqWith.a294139a6615e163.Core:enclosed", scope: null, file: !1, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !92) +// CHECK:STDOUT: !92 = !{!93, !94} +// CHECK:STDOUT: !93 = !DILocalVariable(arg: 1, scope: !91, type: !15) +// CHECK:STDOUT: !94 = !DILocalVariable(arg: 2, scope: !91, type: !14) +// CHECK:STDOUT: !95 = !DILocation(line: 0, scope: !91) +// CHECK:STDOUT: !96 = distinct !DISubprogram(name: "CompareGeneric", linkageName: "_CCompareGeneric.Main.215a23369ed4d589", scope: null, file: !1, line: 29, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !97) +// CHECK:STDOUT: !97 = !{!98, !99} +// CHECK:STDOUT: !98 = !DILocalVariable(arg: 1, scope: !96, type: !15) +// CHECK:STDOUT: !99 = !DILocalVariable(arg: 2, scope: !96, type: !14) +// CHECK:STDOUT: !100 = !DILocation(line: 30, column: 10, scope: !96) +// CHECK:STDOUT: !101 = !DILocation(line: 30, column: 3, scope: !96) +// CHECK:STDOUT: !102 = distinct !DISubprogram(name: "CompareGeneric", linkageName: "_CCompareGeneric.Main.a5489e454367d917", scope: null, file: !1, line: 29, type: !30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !103) +// CHECK:STDOUT: !103 = !{!104, !105} +// CHECK:STDOUT: !104 = !DILocalVariable(arg: 1, scope: !102, type: !32) +// CHECK:STDOUT: !105 = !DILocalVariable(arg: 2, scope: !102, type: !14) +// CHECK:STDOUT: !106 = !DILocation(line: 30, column: 10, scope: !102) +// CHECK:STDOUT: !107 = !DILocation(line: 30, column: 3, scope: !102) +// CHECK:STDOUT: !108 = distinct !DISubprogram(name: "CompareGeneric", linkageName: "_CCompareGeneric.Main.64b737572447efad", scope: null, file: !1, line: 29, type: !12, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !109) +// CHECK:STDOUT: !109 = !{!110, !111} +// CHECK:STDOUT: !110 = !DILocalVariable(arg: 1, scope: !108, type: !15) +// CHECK:STDOUT: !111 = !DILocalVariable(arg: 2, scope: !108, type: !14) +// CHECK:STDOUT: !112 = !DILocation(line: 30, column: 10, scope: !108) +// CHECK:STDOUT: !113 = !DILocation(line: 30, column: 3, scope: !108)