diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index a56b508c0827..7815e67964ca 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -31,6 +31,7 @@ #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/typed_insts.h" // TODO: This contains a lot of recursion. Consider removing it in order to @@ -767,6 +768,58 @@ static auto CanUseValueOfInitializer(const SemIR::File& sem_ir, return InitReprIsCopyOfValueRepr(sem_ir, type_id); } +// Determine whether the given set of qualifiers can be added by a conversion +// of an expression of the given category. +static auto CanAddQualifiers(SemIR::TypeQualifiers quals, + SemIR::ExprCategory cat) -> bool { + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::MaybeUnformed) && + !SemIR::IsRefCategory(cat)) { + // `MaybeUnformed(T)` may have a different value representation or + // initializing representation from `T`, so only allow it to be added for a + // reference expression. + // TODO: We should allow converting an initializing expression of type `T` + // to `MaybeUnformed(T)`. `PerformBuiltinConversion` will need to generate + // an `InPlaceInit` instruction when needed. + // NOLINTNEXTLINE(readability-simplify-boolean-expr) + return false; + } + + // `const` and `partial` can always be added. + return true; +} + +// Determine whether the given set of qualifiers can be removed by a conversion +// of an expression of the given category. +static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals, + SemIR::ExprCategory cat, bool allow_unsafe) + -> bool { + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Const) && !allow_unsafe && + SemIR::IsRefCategory(cat)) { + // Removing `const` is an unsafe conversion for a reference expression. + return false; + } + + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Partial) && + (!allow_unsafe || cat == SemIR::ExprCategory::Initializing)) { + // TODO: Allow removing `partial` for initializing expressions as a safe + // conversion. `PerformBuiltinConversion` will need to initialize the vptr + // as part of the conversion. + return false; + } + + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::MaybeUnformed) && + (!allow_unsafe || !SemIR::IsRefCategory(cat))) { + // As an unsafe conversion, `MaybeUnformed` can be removed from a reference + // expression. + // TODO: We should allow this for any kind of expression, and convert the + // result as needed if the representation of `T` differs from that of + // `MaybeUnformed(T)`. + return false; + } + + return true; +} + static auto DiagnoseConversionFailureToConstraintValue( Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId target_type_id) -> void { @@ -914,24 +967,32 @@ static auto PerformBuiltinConversion( } } - // T explicitly converts to U if T is compatible with U. + // T explicitly converts to U if T is compatible with U, and we're allowed to + // remove / add any qualifiers that differ. if (target.is_explicit_as() && target.type_id != value_type_id) { - auto target_foundation_id = - context.types().GetTransitiveAdaptedType(target.type_id); - auto value_foundation_id = - context.types().GetTransitiveAdaptedType(value_type_id); + auto [target_foundation_id, target_quals] = + context.types().GetTransitiveUnqualifiedAdaptedType(target.type_id); + auto [value_foundation_id, value_quals] = + context.types().GetTransitiveUnqualifiedAdaptedType(value_type_id); if (target_foundation_id == value_foundation_id) { - // For a struct or tuple literal, perform a category conversion if - // necessary. - if (SemIR::GetExprCategory(context.sem_ir(), value_id) == - SemIR::ExprCategory::Mixed) { - value_id = PerformBuiltinConversion(context, loc_id, value_id, - {.kind = ConversionTarget::Value, - .type_id = value_type_id, - .diagnose = target.diagnose}); + auto category = SemIR::GetExprCategory(context.sem_ir(), value_id); + if (CanAddQualifiers(target_quals & ~value_quals, category) && + CanRemoveQualifiers( + value_quals & ~target_quals, category, + target.kind == ConversionTarget::ExplicitUnsafeAs)) { + // For a struct or tuple literal, perform a category conversion if + // necessary. + if (category == SemIR::ExprCategory::Mixed) { + value_id = PerformBuiltinConversion(context, loc_id, value_id, + {.kind = ConversionTarget::Value, + .type_id = value_type_id, + .diagnose = target.diagnose}); + } + + return AddInst( + context, loc_id, + {.type_id = target.type_id, .source_id = value_id}); } - return AddInst( - context, loc_id, {.type_id = target.type_id, .source_id = value_id}); } } @@ -996,19 +1057,23 @@ static auto PerformBuiltinConversion( } } - // A pointer T* converts to [const] U* if T is the same as U, or is a class - // derived from U. + // A pointer T* converts to [qualified] U* if T is the same as U, or is a + // class derived from U. if (auto target_pointer_type = target_type_inst.TryAs()) { if (auto src_pointer_type = sem_ir.types().TryGetAs(value_type_id)) { + auto target_pointee_id = context.types().GetTypeIdForTypeInstId( + target_pointer_type->pointee_id); + auto src_pointee_id = + context.types().GetTypeIdForTypeInstId(src_pointer_type->pointee_id); + // Try to complete the pointee types so that we can walk through adapters + // to their adapted types. + TryToCompleteType(context, target_pointee_id, loc_id); + TryToCompleteType(context, src_pointee_id, loc_id); auto [unqual_target_pointee_type_id, target_quals] = - sem_ir.types().GetUnqualifiedTypeAndQualifiers( - context.types().GetTypeIdForTypeInstId( - target_pointer_type->pointee_id)); + sem_ir.types().GetTransitiveUnqualifiedAdaptedType(target_pointee_id); auto [unqual_src_pointee_type_id, src_quals] = - sem_ir.types().GetUnqualifiedTypeAndQualifiers( - context.types().GetTypeIdForTypeInstId( - src_pointer_type->pointee_id)); + sem_ir.types().GetTransitiveUnqualifiedAdaptedType(src_pointee_id); // If the qualifiers are incompatible, we can't perform a conversion, // except with `unsafe as`. @@ -1366,25 +1431,30 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, if (!target.diagnose) { return context.emitter().BuildSuppressed(); } + int target_kind_for_diag = + target.kind == ConversionTarget::ExplicitAs ? 1 + : target.kind == ConversionTarget::ExplicitUnsafeAs ? 2 + : 0; if (target.type_id == SemIR::TypeType::TypeId || sem_ir.types().Is(target.type_id)) { CARBON_DIAGNOSTIC( ConversionFailureNonTypeToFacet, Error, - "cannot{0:| implicitly} convert non-type value of type {1} " - "{2:to|into type implementing} {3}{0: with `as`|}", - Diagnostics::BoolAsSelect, TypeOfInstId, Diagnostics::BoolAsSelect, + "cannot{0:=0: implicitly|:} convert non-type value of type {1} " + "{2:to|into type implementing} {3}" + "{0:=1: with `as`|=2: with `unsafe as`|:}", + Diagnostics::IntAsSelect, TypeOfInstId, Diagnostics::BoolAsSelect, SemIR::TypeId); return context.emitter().Build( - loc_id, ConversionFailureNonTypeToFacet, target.is_explicit_as(), + loc_id, ConversionFailureNonTypeToFacet, target_kind_for_diag, expr_id, target.type_id == SemIR::TypeType::TypeId, target.type_id); } else { - CARBON_DIAGNOSTIC(ConversionFailure, Error, - "cannot{0:| implicitly} convert expression of type " - "{1} to {2}{0: with `as`|}", - Diagnostics::BoolAsSelect, TypeOfInstId, - SemIR::TypeId); + CARBON_DIAGNOSTIC( + ConversionFailure, Error, + "cannot{0:=0: implicitly|:} convert expression of type " + "{1} to {2}{0:=1: with `as`|=2: with `unsafe as`|:}", + Diagnostics::IntAsSelect, TypeOfInstId, SemIR::TypeId); return context.emitter().Build(loc_id, ConversionFailure, - target.is_explicit_as(), expr_id, + target_kind_for_diag, expr_id, target.type_id); } }); diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 671e696ee4e4..4e8123221e78 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -26,6 +26,7 @@ #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/import_ir.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/inst_categories.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" @@ -1823,14 +1824,16 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, .object_repr_type_inst_id = object_repr_type_inst_id}); } -static auto TryResolveTypedInst(ImportRefResolver& resolver, - SemIR::ConstType inst) -> ResolveResult { +template + requires SemIR::Internal::HasInstCategory +static auto TryResolveTypedInst(ImportRefResolver& resolver, InstT inst) + -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto inner_id = GetLocalTypeInstId(resolver, inst.inner_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } - return ResolveAsDeduplicated( + return ResolveAsDeduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_id}); } @@ -3183,12 +3186,18 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver, case CARBON_KIND(SemIR::IntType inst): { return TryResolveTypedInst(resolver, inst); } + case CARBON_KIND(SemIR::MaybeUnformedType inst): { + return TryResolveTypedInst(resolver, inst); + } case CARBON_KIND(SemIR::Namespace inst): { return TryResolveTypedInst(resolver, inst, inst_id); } case CARBON_KIND(SemIR::OutParamPattern inst): { return TryResolveTypedInst(resolver, inst, inst_id); } + case CARBON_KIND(SemIR::PartialType inst): { + return TryResolveTypedInst(resolver, inst); + } case CARBON_KIND(SemIR::PatternType inst): { return TryResolveTypedInst(resolver, inst); } diff --git a/toolchain/check/testdata/as/const.carbon b/toolchain/check/testdata/as/const.carbon new file mode 100644 index 000000000000..9584067e3aca --- /dev/null +++ b/toolchain/check/testdata/as/const.carbon @@ -0,0 +1,303 @@ +// 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/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/const.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/const.carbon + +// --- add_const.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +fn Init() -> X; +let value: X = Init(); +var reference: X; +let ptr: X* = &reference; + +fn Use() { + // TODO: Should some of these be valid without the `as`? + //@dump-sem-ir-begin + var i: const X = Init() as const X; + let v: const X = value as const X; + let a: const X* = &(reference as const X); + let b: const X* = ptr as const X*; + //@dump-sem-ir-end +} + +// --- remove_const.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +fn Init() -> const X; +let value: const X = Init(); + +fn Use() { + // TODO: Should some of these be valid without the `as`? + //@dump-sem-ir-begin + var i: X = Init() as X; + let v: X = value as X; + //@dump-sem-ir-end +} + +// --- fail_cannot_remove_const.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +var reference: const X; +let ptr: const X* = &reference; + +fn Use() { + // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+7]]:17: error: cannot convert expression of type `const X` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: let a: X* = &(reference as X); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+4]]:17: note: type `const X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let a: X* = &(reference as X); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: + let a: X* = &(reference as X); + // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+7]]:15: error: cannot convert expression of type `const X*` to `X*` with `as` [ConversionFailure] + // CHECK:STDERR: let b: X* = ptr as X*; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+4]]:15: note: type `const X*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let b: X* = ptr as X*; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + let b: X* = ptr as X*; +} + +// --- unsafe_remove_const.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +var reference: const X; +let ptr: const X* = &reference; + +fn Use() { + //@dump-sem-ir-begin + let a: X* = &(reference unsafe as X); + let b: X* = ptr unsafe as X*; + //@dump-sem-ir-end +} + +// CHECK:STDOUT: --- add_const.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] +// CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] +// CHECK:STDOUT: %const: type = const_type %X [concrete] +// CHECK:STDOUT: %pattern_type.d7f: type = pattern_type %const [concrete] +// CHECK:STDOUT: %ptr.cbd: type = ptr_type %const [concrete] +// CHECK:STDOUT: %pattern_type.855: type = pattern_type %ptr.cbd [concrete] +// CHECK:STDOUT: %reference.var: ref %const = var file.%reference.var_patt [concrete] +// CHECK:STDOUT: %addr.0c5: %ptr.cbd = addr_of %reference.var [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.f3e: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%const) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.4e8: %T.as.Destroy.impl.Op.type.f3e = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %i.patt: %pattern_type.d7f = binding_pattern i [concrete] +// CHECK:STDOUT: %i.var_patt: %pattern_type.d7f = var_pattern %i.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i.var: ref %const = var %i.var_patt +// CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc14_3: ref %const = splice_block %i.var {} +// CHECK:STDOUT: %Init.call: init %X = call %Init.ref() to %.loc14_3 +// CHECK:STDOUT: %X.ref.loc14_36: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc14_30: type = const_type %X.ref.loc14_36 [concrete = constants.%const] +// CHECK:STDOUT: %.loc14_27.1: init %const = as_compatible %Init.call +// CHECK:STDOUT: %.loc14_27.2: init %const = converted %Init.call, %.loc14_27.1 +// CHECK:STDOUT: assign %i.var, %.loc14_27.2 +// CHECK:STDOUT: %.loc14_10: type = splice_block %const.loc14_10 [concrete = constants.%const] { +// CHECK:STDOUT: %X.ref.loc14_16: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc14_10: type = const_type %X.ref.loc14_16 [concrete = constants.%const] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i: ref %const = bind_name i, %i.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: %pattern_type.d7f = binding_pattern v [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %value.ref: %X = name_ref value, file.%value +// CHECK:STDOUT: %X.ref.loc15_35: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc15_29: type = const_type %X.ref.loc15_35 [concrete = constants.%const] +// CHECK:STDOUT: %.loc15_26.1: %const = as_compatible %value.ref +// CHECK:STDOUT: %.loc15_26.2: %const = converted %value.ref, %.loc15_26.1 +// CHECK:STDOUT: %.loc15_10: type = splice_block %const.loc15_10 [concrete = constants.%const] { +// CHECK:STDOUT: %X.ref.loc15_16: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc15_10: type = const_type %X.ref.loc15_16 [concrete = constants.%const] +// CHECK:STDOUT: } +// CHECK:STDOUT: %v: %const = bind_name v, %.loc15_26.2 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.855 = binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %reference.ref: ref %X = name_ref reference, file.%reference [concrete = file.%reference.var] +// CHECK:STDOUT: %X.ref.loc16_42: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc16_36: type = const_type %X.ref.loc16_42 [concrete = constants.%const] +// CHECK:STDOUT: %.loc16_33.1: ref %const = as_compatible %reference.ref [concrete = constants.%reference.var] +// CHECK:STDOUT: %.loc16_33.2: ref %const = converted %reference.ref, %.loc16_33.1 [concrete = constants.%reference.var] +// CHECK:STDOUT: %addr.loc16: %ptr.cbd = addr_of %.loc16_33.2 [concrete = constants.%addr.0c5] +// CHECK:STDOUT: %.loc16_17: type = splice_block %ptr.loc16 [concrete = constants.%ptr.cbd] { +// CHECK:STDOUT: %X.ref.loc16_16: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc16_10: type = const_type %X.ref.loc16_16 [concrete = constants.%const] +// CHECK:STDOUT: %ptr.loc16: type = ptr_type %const.loc16_10 [concrete = constants.%ptr.cbd] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %ptr.cbd = bind_name a, %addr.loc16 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.855 = binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ptr.ref: %ptr.d17 = name_ref ptr, file.%ptr.loc9_5 +// CHECK:STDOUT: %X.ref.loc17_34: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc17_28: type = const_type %X.ref.loc17_34 [concrete = constants.%const] +// CHECK:STDOUT: %ptr.loc17_35: type = ptr_type %const.loc17_28 [concrete = constants.%ptr.cbd] +// CHECK:STDOUT: %.loc17_25.1: %ptr.cbd = as_compatible %ptr.ref +// CHECK:STDOUT: %.loc17_25.2: %ptr.cbd = converted %ptr.ref, %.loc17_25.1 +// CHECK:STDOUT: %.loc17_17: type = splice_block %ptr.loc17_17 [concrete = constants.%ptr.cbd] { +// CHECK:STDOUT: %X.ref.loc17_16: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %const.loc17_10: type = const_type %X.ref.loc17_16 [concrete = constants.%const] +// CHECK:STDOUT: %ptr.loc17_17: type = ptr_type %const.loc17_10 [concrete = constants.%ptr.cbd] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %ptr.cbd = bind_name b, %.loc17_25.2 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc14_3.1: = bound_method %.loc14_3, constants.%T.as.Destroy.impl.Op.4e8 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc14_3.1: = bound_method %.loc14_3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc14_3.1: %ptr.cbd = addr_of %.loc14_3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc14_3.1: init %empty_tuple.type = call %bound_method.loc14_3.1(%addr.loc14_3.1) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc14_3.2: = bound_method %i.var, constants.%T.as.Destroy.impl.Op.4e8 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc14_3.2: = bound_method %i.var, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc14_3.2: %ptr.cbd = addr_of %i.var +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc14_3.2: init %empty_tuple.type = call %bound_method.loc14_3.2(%addr.loc14_3.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- remove_const.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %X.as.Destroy.impl.Op.type: type = fn_type @X.as.Destroy.impl.Op [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %X.as.Destroy.impl.Op: %X.as.Destroy.impl.Op.type = struct_value () [concrete] +// CHECK:STDOUT: %const: type = const_type %X [concrete] +// CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] +// CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.019: type = pattern_type %X [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %i.patt: %pattern_type.019 = binding_pattern i [concrete] +// CHECK:STDOUT: %i.var_patt: %pattern_type.019 = var_pattern %i.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i.var: ref %X = var %i.var_patt +// CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc12_3: ref %X = splice_block %i.var {} +// CHECK:STDOUT: %Init.call: init %const = call %Init.ref() to %.loc12_3 +// CHECK:STDOUT: %X.ref.loc12_24: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc12_21.1: init %X = as_compatible %Init.call +// CHECK:STDOUT: %.loc12_21.2: init %X = converted %Init.call, %.loc12_21.1 +// CHECK:STDOUT: assign %i.var, %.loc12_21.2 +// CHECK:STDOUT: %X.ref.loc12_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %i: ref %X = bind_name i, %i.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: %pattern_type.019 = binding_pattern v [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %value.ref: %const = name_ref value, file.%value +// CHECK:STDOUT: %X.ref.loc13_23: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc13_20.1: %X = as_compatible %value.ref +// CHECK:STDOUT: %.loc13_20.2: %X = converted %value.ref, %.loc13_20.1 +// CHECK:STDOUT: %X.ref.loc13_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %v: %X = bind_name v, %.loc13_20.2 +// CHECK:STDOUT: %X.as.Destroy.impl.Op.bound.loc12_3.1: = bound_method %.loc12_3, constants.%X.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc12_3.1: %ptr.d17 = addr_of %.loc12_3 +// CHECK:STDOUT: %X.as.Destroy.impl.Op.call.loc12_3.1: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound.loc12_3.1(%addr.loc12_3.1) +// CHECK:STDOUT: %X.as.Destroy.impl.Op.bound.loc12_3.2: = bound_method %i.var, constants.%X.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc12_3.2: %ptr.d17 = addr_of %i.var +// CHECK:STDOUT: %X.as.Destroy.impl.Op.call.loc12_3.2: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound.loc12_3.2(%addr.loc12_3.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- unsafe_remove_const.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %pattern_type.1c6: type = pattern_type %ptr.d17 [concrete] +// CHECK:STDOUT: %const: type = const_type %X [concrete] +// CHECK:STDOUT: %ptr.cbd: type = ptr_type %const [concrete] +// CHECK:STDOUT: %reference.var: ref %X = var file.%reference.var_patt [concrete] +// CHECK:STDOUT: %addr.373: %ptr.d17 = addr_of %reference.var [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.1c6 = binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %reference.ref: ref %const = name_ref reference, file.%reference [concrete = file.%reference.var] +// CHECK:STDOUT: %X.ref.loc11_37: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc11_34.1: ref %X = as_compatible %reference.ref [concrete = constants.%reference.var] +// CHECK:STDOUT: %.loc11_34.2: ref %X = converted %reference.ref, %.loc11_34.1 [concrete = constants.%reference.var] +// CHECK:STDOUT: %addr: %ptr.d17 = addr_of %.loc11_34.2 [concrete = constants.%addr.373] +// CHECK:STDOUT: %.loc11_11: type = splice_block %ptr.loc11 [concrete = constants.%ptr.d17] { +// CHECK:STDOUT: %X.ref.loc11_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc11: type = ptr_type %X.ref.loc11_10 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %ptr.d17 = bind_name a, %addr +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.1c6 = binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ptr.ref: %ptr.cbd = name_ref ptr, file.%ptr.loc7_5 +// CHECK:STDOUT: %X.ref.loc12_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc12_30: type = ptr_type %X.ref.loc12_29 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: %.loc12_26.1: %ptr.d17 = as_compatible %ptr.ref +// CHECK:STDOUT: %.loc12_26.2: %ptr.d17 = converted %ptr.ref, %.loc12_26.1 +// CHECK:STDOUT: %.loc12_11: type = splice_block %ptr.loc12_11 [concrete = constants.%ptr.d17] { +// CHECK:STDOUT: %X.ref.loc12_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc12_11: type = ptr_type %X.ref.loc12_10 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %ptr.d17 = bind_name b, %.loc12_26.2 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/as/maybe_unformed.carbon b/toolchain/check/testdata/as/maybe_unformed.carbon new file mode 100644 index 000000000000..cfae794164fc --- /dev/null +++ b/toolchain/check/testdata/as/maybe_unformed.carbon @@ -0,0 +1,366 @@ +// 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/unformed.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/maybe_unformed.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/maybe_unformed.carbon + +// --- add_unformed.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +var reference: X; +let ptr: X* = &reference; + +fn Use() { + // TODO: Should some of these be valid without the `as`? + //@dump-sem-ir-begin + let a: Core.MaybeUnformed(X)* = &(reference as Core.MaybeUnformed(X)); + let b: Core.MaybeUnformed(X)* = ptr as Core.MaybeUnformed(X)*; + //@dump-sem-ir-end +} + +// --- fail_todo_add_unformed_nonref.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +fn Init() -> X; +let value: X = Init(); + +fn Use() { + // TODO: These should probably be valid. + //@dump-sem-ir-begin + // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+7]]:34: error: cannot convert expression of type `X` to `Core.MaybeUnformed(X)` with `as` [ConversionFailure] + // CHECK:STDERR: var i: Core.MaybeUnformed(X) = Init() as Core.MaybeUnformed(X); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+4]]:34: note: type `X` does not implement interface `Core.As(Core.MaybeUnformed(X))` [MissingImplInMemberAccessNote] + // CHECK:STDERR: var i: Core.MaybeUnformed(X) = Init() as Core.MaybeUnformed(X); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var i: Core.MaybeUnformed(X) = Init() as Core.MaybeUnformed(X); + // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+7]]:34: error: cannot convert expression of type `X` to `Core.MaybeUnformed(X)` with `as` [ConversionFailure] + // CHECK:STDERR: let v: Core.MaybeUnformed(X) = value as Core.MaybeUnformed(X); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+4]]:34: note: type `X` does not implement interface `Core.As(Core.MaybeUnformed(X))` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let v: Core.MaybeUnformed(X) = value as Core.MaybeUnformed(X); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + let v: Core.MaybeUnformed(X) = value as Core.MaybeUnformed(X); + //@dump-sem-ir-end +} + +// --- fail_cannot_remove_unformed.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +fn Init() -> Core.MaybeUnformed(X); +let value: Core.MaybeUnformed(X) = Init(); +var reference: Core.MaybeUnformed(X); +let ptr: Core.MaybeUnformed(X)* = &reference; + +fn Use() { + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: var i: X = Init() as X; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:14: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: var i: X = Init() as X; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + var i: X = Init() as X; + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: let v: X = value as X; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:14: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let v: X = value as X; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: + let v: X = value as X; + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:17: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: let a: X* = &(reference as X); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:17: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let a: X* = &(reference as X); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: + let a: X* = &(reference as X); + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:15: error: cannot convert expression of type `Core.MaybeUnformed(X)*` to `X*` with `as` [ConversionFailure] + // CHECK:STDERR: let b: X* = ptr as X*; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:15: note: type `Core.MaybeUnformed(X)*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let b: X* = ptr as X*; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + let b: X* = ptr as X*; +} + +// --- unsafe_remove_unformed.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +var reference: Core.MaybeUnformed(X); +let ptr: Core.MaybeUnformed(X)* = &reference; + +fn Use() { + //@dump-sem-ir-begin + let a: X* = &(reference unsafe as X); + let b: X* = ptr unsafe as X*; + //@dump-sem-ir-end +} + +// --- fail_todo_remove_unformed_unsafe_notref.carbon + +library "[[@TEST_NAME]]"; + +class X {} + +fn Init() -> Core.MaybeUnformed(X); +let value: Core.MaybeUnformed(X) = Init(); + +fn Use() { + // TODO: These should probably be valid. + // CHECK:STDERR: fail_todo_remove_unformed_unsafe_notref.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `unsafe as` [ConversionFailure] + // CHECK:STDERR: var i: X = Init() unsafe as X; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_remove_unformed_unsafe_notref.carbon:[[@LINE+4]]:14: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.UnsafeAs(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: var i: X = Init() unsafe as X; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var i: X = Init() unsafe as X; + // CHECK:STDERR: fail_todo_remove_unformed_unsafe_notref.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `unsafe as` [ConversionFailure] + // CHECK:STDERR: let v: X = value unsafe as X; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_remove_unformed_unsafe_notref.carbon:[[@LINE+4]]:14: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.UnsafeAs(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let v: X = value unsafe as X; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + let v: X = value unsafe as X; +} + +// CHECK:STDOUT: --- add_unformed.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %MaybeUnformed.type: type = generic_class_type @MaybeUnformed [concrete] +// CHECK:STDOUT: %MaybeUnformed.generic: %MaybeUnformed.type = struct_value () [concrete] +// CHECK:STDOUT: %MaybeUnformed.275: type = class_type @MaybeUnformed, @MaybeUnformed(%X) [concrete] +// CHECK:STDOUT: %ptr.58e: type = ptr_type %MaybeUnformed.275 [concrete] +// CHECK:STDOUT: %pattern_type.460: type = pattern_type %ptr.58e [concrete] +// CHECK:STDOUT: %reference.var: ref %MaybeUnformed.275 = var file.%reference.var_patt [concrete] +// CHECK:STDOUT: %addr.10a: %ptr.58e = addr_of %reference.var [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Destroy = %Core.Destroy +// CHECK:STDOUT: .MaybeUnformed = %Core.MaybeUnformed +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] +// CHECK:STDOUT: %Core.MaybeUnformed: %MaybeUnformed.type = import_ref Core//prelude/parts/maybe_unformed, MaybeUnformed, loaded [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.460 = binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %reference.ref: ref %X = name_ref reference, file.%reference [concrete = file.%reference.var] +// CHECK:STDOUT: %Core.ref.loc12_50: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc12_54: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc12_69: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc12_70: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: %.loc12_47.1: ref %MaybeUnformed.275 = as_compatible %reference.ref [concrete = constants.%reference.var] +// CHECK:STDOUT: %.loc12_47.2: ref %MaybeUnformed.275 = converted %reference.ref, %.loc12_47.1 [concrete = constants.%reference.var] +// CHECK:STDOUT: %addr: %ptr.58e = addr_of %.loc12_47.2 [concrete = constants.%addr.10a] +// CHECK:STDOUT: %.loc12_31: type = splice_block %ptr.loc12 [concrete = constants.%ptr.58e] { +// CHECK:STDOUT: %Core.ref.loc12_10: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc12_14: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc12_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc12_30: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: %ptr.loc12: type = ptr_type %MaybeUnformed.loc12_30 [concrete = constants.%ptr.58e] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %ptr.58e = bind_name a, %addr +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.460 = binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ptr.ref: %ptr.d17 = name_ref ptr, file.%ptr.loc7_5 +// CHECK:STDOUT: %Core.ref.loc13_42: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc13_46: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc13_61: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc13_62: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: %ptr.loc13_63: type = ptr_type %MaybeUnformed.loc13_62 [concrete = constants.%ptr.58e] +// CHECK:STDOUT: %.loc13_39.1: %ptr.58e = as_compatible %ptr.ref +// CHECK:STDOUT: %.loc13_39.2: %ptr.58e = converted %ptr.ref, %.loc13_39.1 +// CHECK:STDOUT: %.loc13_31: type = splice_block %ptr.loc13_31 [concrete = constants.%ptr.58e] { +// CHECK:STDOUT: %Core.ref.loc13_10: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc13_14: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc13_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc13_30: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: %ptr.loc13_31: type = ptr_type %MaybeUnformed.loc13_30 [concrete = constants.%ptr.58e] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %ptr.58e = bind_name b, %.loc13_39.2 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_add_unformed_nonref.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %X.as.Destroy.impl.Op.type: type = fn_type @X.as.Destroy.impl.Op [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %X.as.Destroy.impl.Op: %X.as.Destroy.impl.Op.type = struct_value () [concrete] +// CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] +// CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] +// CHECK:STDOUT: %MaybeUnformed.type: type = generic_class_type @MaybeUnformed [concrete] +// CHECK:STDOUT: %MaybeUnformed.generic: %MaybeUnformed.type = struct_value () [concrete] +// CHECK:STDOUT: %MaybeUnformed.275: type = class_type @MaybeUnformed, @MaybeUnformed(%X) [concrete] +// CHECK:STDOUT: %pattern_type.eb8: type = pattern_type %MaybeUnformed.275 [concrete] +// CHECK:STDOUT: %As.type.90f: type = generic_interface_type @As [concrete] +// CHECK:STDOUT: %As.generic: %As.type.90f = struct_value () [concrete] +// CHECK:STDOUT: %MaybeUnformed.as.Destroy.impl.Op.type.3bf: type = fn_type @MaybeUnformed.as.Destroy.impl.Op, @MaybeUnformed.as.Destroy.impl(%X) [concrete] +// CHECK:STDOUT: %MaybeUnformed.as.Destroy.impl.Op.b85: %MaybeUnformed.as.Destroy.impl.Op.type.3bf = struct_value () [concrete] +// CHECK:STDOUT: %ptr.58e: type = ptr_type %MaybeUnformed.275 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Destroy = %Core.Destroy +// CHECK:STDOUT: .MaybeUnformed = %Core.MaybeUnformed +// CHECK:STDOUT: .As = %Core.As +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] +// CHECK:STDOUT: %Core.MaybeUnformed: %MaybeUnformed.type = import_ref Core//prelude/parts/maybe_unformed, MaybeUnformed, loaded [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %Core.As: %As.type.90f = import_ref Core//prelude/parts/as, As, loaded [concrete = constants.%As.generic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %i.patt: %pattern_type.eb8 = binding_pattern i [concrete] +// CHECK:STDOUT: %i.var_patt: %pattern_type.eb8 = var_pattern %i.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i.var: ref %MaybeUnformed.275 = var %i.var_patt +// CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc19_39: ref %X = temporary_storage +// CHECK:STDOUT: %Init.call: init %X = call %Init.ref() to %.loc19_39 +// CHECK:STDOUT: %Core.ref.loc19_44: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc19_48: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc19_63: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc19_64: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: %.loc19_41: %MaybeUnformed.275 = converted %Init.call, [concrete = ] +// CHECK:STDOUT: assign %i.var, +// CHECK:STDOUT: %.loc19_30: type = splice_block %MaybeUnformed.loc19_30 [concrete = constants.%MaybeUnformed.275] { +// CHECK:STDOUT: %Core.ref.loc19_10: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc19_14: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc19_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc19_30: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i: ref %MaybeUnformed.275 = bind_name i, %i.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: %pattern_type.eb8 = binding_pattern v [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %value.ref: %X = name_ref value, file.%value +// CHECK:STDOUT: %Core.ref.loc27_43: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc27_47: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc27_62: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc27_63: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: %.loc27_40: %MaybeUnformed.275 = converted %value.ref, [concrete = ] +// CHECK:STDOUT: %.loc27_30: type = splice_block %MaybeUnformed.loc27_30 [concrete = constants.%MaybeUnformed.275] { +// CHECK:STDOUT: %Core.ref.loc27_10: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %MaybeUnformed.ref.loc27_14: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] +// CHECK:STDOUT: %X.ref.loc27_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %MaybeUnformed.loc27_30: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.275] +// CHECK:STDOUT: } +// CHECK:STDOUT: %v: %MaybeUnformed.275 = bind_name v, [concrete = ] +// CHECK:STDOUT: %X.as.Destroy.impl.Op.bound: = bound_method %.loc19_39, constants.%X.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc19_39: %ptr.d17 = addr_of %.loc19_39 +// CHECK:STDOUT: %X.as.Destroy.impl.Op.call: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound(%addr.loc19_39) +// CHECK:STDOUT: %MaybeUnformed.as.Destroy.impl.Op.bound: = bound_method %i.var, constants.%MaybeUnformed.as.Destroy.impl.Op.b85 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %i.var, %MaybeUnformed.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %addr.loc19_3: %ptr.58e = addr_of %i.var +// CHECK:STDOUT: %MaybeUnformed.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%addr.loc19_3) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- unsafe_remove_unformed.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %pattern_type.1c6: type = pattern_type %ptr.d17 [concrete] +// CHECK:STDOUT: %MaybeUnformed.275: type = class_type @MaybeUnformed, @MaybeUnformed(%X) [concrete] +// CHECK:STDOUT: %ptr.58e: type = ptr_type %MaybeUnformed.275 [concrete] +// CHECK:STDOUT: %reference.var: ref %X = var file.%reference.var_patt [concrete] +// CHECK:STDOUT: %addr.a46: %ptr.d17 = addr_of %reference.var [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.1c6 = binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %reference.ref: ref %MaybeUnformed.275 = name_ref reference, file.%reference [concrete = file.%reference.var] +// CHECK:STDOUT: %X.ref.loc11_37: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc11_34.1: ref %X = as_compatible %reference.ref [concrete = constants.%reference.var] +// CHECK:STDOUT: %.loc11_34.2: ref %X = converted %reference.ref, %.loc11_34.1 [concrete = constants.%reference.var] +// CHECK:STDOUT: %addr: %ptr.d17 = addr_of %.loc11_34.2 [concrete = constants.%addr.a46] +// CHECK:STDOUT: %.loc11_11: type = splice_block %ptr.loc11 [concrete = constants.%ptr.d17] { +// CHECK:STDOUT: %X.ref.loc11_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc11: type = ptr_type %X.ref.loc11_10 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %ptr.d17 = bind_name a, %addr +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.1c6 = binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ptr.ref: %ptr.58e = name_ref ptr, file.%ptr.loc7_5 +// CHECK:STDOUT: %X.ref.loc12_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc12_30: type = ptr_type %X.ref.loc12_29 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: %.loc12_26.1: %ptr.d17 = as_compatible %ptr.ref +// CHECK:STDOUT: %.loc12_26.2: %ptr.d17 = converted %ptr.ref, %.loc12_26.1 +// CHECK:STDOUT: %.loc12_11: type = splice_block %ptr.loc12_11 [concrete = constants.%ptr.d17] { +// CHECK:STDOUT: %X.ref.loc12_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc12_11: type = ptr_type %X.ref.loc12_10 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %ptr.d17 = bind_name b, %.loc12_26.2 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/as/partial.carbon b/toolchain/check/testdata/as/partial.carbon new file mode 100644 index 000000000000..58e5c20cfc33 --- /dev/null +++ b/toolchain/check/testdata/as/partial.carbon @@ -0,0 +1,380 @@ +// 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/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/partial.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/partial.carbon + +// --- add_partial.carbon + +library "[[@TEST_NAME]]"; + +base class X {} + +fn Init() -> X; +let value: X = Init(); +var reference: X; +let ptr: X* = &reference; + +fn Use() { + // TODO: Should some of these be valid without the `as`? + //@dump-sem-ir-begin + var i: partial X = Init() as partial X; + let v: partial X = value as partial X; + let a: partial X* = &(reference as partial X); + let b: partial X* = ptr as partial X*; + //@dump-sem-ir-end +} + +// --- fail_todo_remove_partial_in_init.carbon + +library "[[@TEST_NAME]]"; + +base class X {} + +fn Init() -> partial X; + +fn Use() { + //@dump-sem-ir-begin + // TODO: This should be valid, and should initialize the vptr. The explicit `as` should probably not be necessary. + // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `partial X` to `X` [ConversionFailure] + // CHECK:STDERR: var i: X = Init(); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+4]]:3: note: type `partial X` does not implement interface `Core.ImplicitAs(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: var i: X = Init(); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + var i: X = Init(); + // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `partial X` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: var j: X = Init() as X; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+4]]:14: note: type `partial X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: var j: X = Init() as X; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + var j: X = Init() as X; + // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `partial X` to `X` with `unsafe as` [ConversionFailure] + // CHECK:STDERR: var k: X = Init() unsafe as X; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_remove_partial_in_init.carbon:[[@LINE+4]]:14: note: type `partial X` does not implement interface `Core.UnsafeAs(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: var k: X = Init() unsafe as X; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + var k: X = Init() unsafe as X; + //@dump-sem-ir-end +} + +// --- fail_cannot_remove_partial.carbon + +library "[[@TEST_NAME]]"; + +base class X {} + +fn Init() -> partial X; + +let value: partial X = Init(); +var reference: partial X; +let ptr: partial X* = &reference; + +fn Use() { + // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+7]]:14: error: cannot convert expression of type `partial X` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: let v: X = value as X; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+4]]:14: note: type `partial X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let v: X = value as X; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: + let v: X = value as X; + // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+7]]:17: error: cannot convert expression of type `partial X` to `X` with `as` [ConversionFailure] + // CHECK:STDERR: let a: X* = &(reference as X); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+4]]:17: note: type `partial X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let a: X* = &(reference as X); + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: + let a: X* = &(reference as X); + // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+7]]:15: error: cannot convert expression of type `partial X*` to `X*` with `as` [ConversionFailure] + // CHECK:STDERR: let b: X* = ptr as X*; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+4]]:15: note: type `partial X*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: let b: X* = ptr as X*; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + let b: X* = ptr as X*; +} + +// --- unsafe_remove_partial.carbon + +library "[[@TEST_NAME]]"; + +base class X {} + +fn Init() -> partial X; + +let value: partial X = Init(); +var reference: partial X; +let ptr: partial X* = &reference; + +fn Use() { + //@dump-sem-ir-begin + let v: X = value unsafe as X; + let a: X* = &(reference unsafe as X); + let b: X* = ptr unsafe as X*; + //@dump-sem-ir-end +} + +// CHECK:STDOUT: --- add_partial.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] +// CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] +// CHECK:STDOUT: %.e71: type = partial_type %X [concrete] +// CHECK:STDOUT: %pattern_type.a53: type = pattern_type %.e71 [concrete] +// CHECK:STDOUT: %ptr.7b2: type = ptr_type %.e71 [concrete] +// CHECK:STDOUT: %pattern_type.46e: type = pattern_type %ptr.7b2 [concrete] +// CHECK:STDOUT: %reference.var: ref %.e71 = var file.%reference.var_patt [concrete] +// CHECK:STDOUT: %addr.e01: %ptr.7b2 = addr_of %reference.var [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.057: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%.e71) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.d31: %T.as.Destroy.impl.Op.type.057 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %i.patt: %pattern_type.a53 = binding_pattern i [concrete] +// CHECK:STDOUT: %i.var_patt: %pattern_type.a53 = var_pattern %i.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i.var: ref %.e71 = var %i.var_patt +// CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc14_3: ref %.e71 = splice_block %i.var {} +// CHECK:STDOUT: %Init.call: init %X = call %Init.ref() to %.loc14_3 +// CHECK:STDOUT: %X.ref.loc14_40: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc14_32: type = partial_type %X.ref.loc14_40 [concrete = constants.%.e71] +// CHECK:STDOUT: %.loc14_29.1: init %.e71 = as_compatible %Init.call +// CHECK:STDOUT: %.loc14_29.2: init %.e71 = converted %Init.call, %.loc14_29.1 +// CHECK:STDOUT: assign %i.var, %.loc14_29.2 +// CHECK:STDOUT: %.loc14_10.1: type = splice_block %.loc14_10.2 [concrete = constants.%.e71] { +// CHECK:STDOUT: %X.ref.loc14_18: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc14_10.2: type = partial_type %X.ref.loc14_18 [concrete = constants.%.e71] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i: ref %.e71 = bind_name i, %i.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: %pattern_type.a53 = binding_pattern v [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %value.ref: %X = name_ref value, file.%value +// CHECK:STDOUT: %X.ref.loc15_39: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc15_31: type = partial_type %X.ref.loc15_39 [concrete = constants.%.e71] +// CHECK:STDOUT: %.loc15_28.1: %.e71 = as_compatible %value.ref +// CHECK:STDOUT: %.loc15_28.2: %.e71 = converted %value.ref, %.loc15_28.1 +// CHECK:STDOUT: %.loc15_10.1: type = splice_block %.loc15_10.2 [concrete = constants.%.e71] { +// CHECK:STDOUT: %X.ref.loc15_18: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc15_10.2: type = partial_type %X.ref.loc15_18 [concrete = constants.%.e71] +// CHECK:STDOUT: } +// CHECK:STDOUT: %v: %.e71 = bind_name v, %.loc15_28.2 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.46e = binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %reference.ref: ref %X = name_ref reference, file.%reference [concrete = file.%reference.var] +// CHECK:STDOUT: %X.ref.loc16_46: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc16_38: type = partial_type %X.ref.loc16_46 [concrete = constants.%.e71] +// CHECK:STDOUT: %.loc16_35.1: ref %.e71 = as_compatible %reference.ref [concrete = constants.%reference.var] +// CHECK:STDOUT: %.loc16_35.2: ref %.e71 = converted %reference.ref, %.loc16_35.1 [concrete = constants.%reference.var] +// CHECK:STDOUT: %addr.loc16: %ptr.7b2 = addr_of %.loc16_35.2 [concrete = constants.%addr.e01] +// CHECK:STDOUT: %.loc16_19: type = splice_block %ptr.loc16 [concrete = constants.%ptr.7b2] { +// CHECK:STDOUT: %X.ref.loc16_18: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc16_10: type = partial_type %X.ref.loc16_18 [concrete = constants.%.e71] +// CHECK:STDOUT: %ptr.loc16: type = ptr_type %.loc16_10 [concrete = constants.%ptr.7b2] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %ptr.7b2 = bind_name a, %addr.loc16 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.46e = binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ptr.ref: %ptr.d17 = name_ref ptr, file.%ptr.loc9_5 +// CHECK:STDOUT: %X.ref.loc17_38: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc17_30: type = partial_type %X.ref.loc17_38 [concrete = constants.%.e71] +// CHECK:STDOUT: %ptr.loc17_39: type = ptr_type %.loc17_30 [concrete = constants.%ptr.7b2] +// CHECK:STDOUT: %.loc17_27.1: %ptr.7b2 = as_compatible %ptr.ref +// CHECK:STDOUT: %.loc17_27.2: %ptr.7b2 = converted %ptr.ref, %.loc17_27.1 +// CHECK:STDOUT: %.loc17_19: type = splice_block %ptr.loc17_19 [concrete = constants.%ptr.7b2] { +// CHECK:STDOUT: %X.ref.loc17_18: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc17_10: type = partial_type %X.ref.loc17_18 [concrete = constants.%.e71] +// CHECK:STDOUT: %ptr.loc17_19: type = ptr_type %.loc17_10 [concrete = constants.%ptr.7b2] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %ptr.7b2 = bind_name b, %.loc17_27.2 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc14_3.1: = bound_method %.loc14_3, constants.%T.as.Destroy.impl.Op.d31 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc14_3.1: = bound_method %.loc14_3, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc14_3.1: %ptr.7b2 = addr_of %.loc14_3 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc14_3.1: init %empty_tuple.type = call %bound_method.loc14_3.1(%addr.loc14_3.1) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc14_3.2: = bound_method %i.var, constants.%T.as.Destroy.impl.Op.d31 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc14_3.2: = bound_method %i.var, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc14_3.2: %ptr.7b2 = addr_of %i.var +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc14_3.2: init %empty_tuple.type = call %bound_method.loc14_3.2(%addr.loc14_3.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_remove_partial_in_init.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %X.as.Destroy.impl.Op.type: type = fn_type @X.as.Destroy.impl.Op [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %X.as.Destroy.impl.Op: %X.as.Destroy.impl.Op.type = struct_value () [concrete] +// CHECK:STDOUT: %.e71: type = partial_type %X [concrete] +// CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] +// CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.019: type = pattern_type %X [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.type.057: type = fn_type @T.as.Destroy.impl.Op, @T.as.Destroy.impl(%.e71) [concrete] +// CHECK:STDOUT: %T.as.Destroy.impl.Op.d31: %T.as.Destroy.impl.Op.type.057 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.7b2: type = ptr_type %.e71 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %i.patt: %pattern_type.019 = binding_pattern i [concrete] +// CHECK:STDOUT: %i.var_patt: %pattern_type.019 = var_pattern %i.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %i.var: ref %X = var %i.var_patt +// CHECK:STDOUT: %Init.ref.loc18: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc18_19: ref %.e71 = temporary_storage +// CHECK:STDOUT: %Init.call.loc18: init %.e71 = call %Init.ref.loc18() to %.loc18_19 +// CHECK:STDOUT: %.loc18_3: %X = converted %Init.call.loc18, [concrete = ] +// CHECK:STDOUT: assign %i.var, +// CHECK:STDOUT: %X.ref.loc18: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %i: ref %X = bind_name i, %i.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %j.patt: %pattern_type.019 = binding_pattern j [concrete] +// CHECK:STDOUT: %j.var_patt: %pattern_type.019 = var_pattern %j.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %j.var: ref %X = var %j.var_patt +// CHECK:STDOUT: %Init.ref.loc26: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc26_19: ref %.e71 = temporary_storage +// CHECK:STDOUT: %Init.call.loc26: init %.e71 = call %Init.ref.loc26() to %.loc26_19 +// CHECK:STDOUT: %X.ref.loc26_24: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc26_21: %X = converted %Init.call.loc26, [concrete = ] +// CHECK:STDOUT: assign %j.var, +// CHECK:STDOUT: %X.ref.loc26_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %j: ref %X = bind_name j, %j.var +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %k.patt: %pattern_type.019 = binding_pattern k [concrete] +// CHECK:STDOUT: %k.var_patt: %pattern_type.019 = var_pattern %k.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %k.var: ref %X = var %k.var_patt +// CHECK:STDOUT: %Init.ref.loc34: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] +// CHECK:STDOUT: %.loc34_19: ref %.e71 = temporary_storage +// CHECK:STDOUT: %Init.call.loc34: init %.e71 = call %Init.ref.loc34() to %.loc34_19 +// CHECK:STDOUT: %X.ref.loc34_31: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc34_28: %X = converted %Init.call.loc34, [concrete = ] +// CHECK:STDOUT: assign %k.var, +// CHECK:STDOUT: %X.ref.loc34_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %k: ref %X = bind_name k, %k.var +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc34: = bound_method %.loc34_19, constants.%T.as.Destroy.impl.Op.d31 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc34: = bound_method %.loc34_19, %T.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc34_19: %ptr.7b2 = addr_of %.loc34_19 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc34: init %empty_tuple.type = call %bound_method.loc34(%addr.loc34_19) +// CHECK:STDOUT: %X.as.Destroy.impl.Op.bound.loc34: = bound_method %k.var, constants.%X.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc34_3: %ptr.d17 = addr_of %k.var +// CHECK:STDOUT: %X.as.Destroy.impl.Op.call.loc34: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound.loc34(%addr.loc34_3) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc26: = bound_method %.loc26_19, constants.%T.as.Destroy.impl.Op.d31 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc26: = bound_method %.loc26_19, %T.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc26_19: %ptr.7b2 = addr_of %.loc26_19 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc26: init %empty_tuple.type = call %bound_method.loc26(%addr.loc26_19) +// CHECK:STDOUT: %X.as.Destroy.impl.Op.bound.loc26: = bound_method %j.var, constants.%X.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc26_3: %ptr.d17 = addr_of %j.var +// CHECK:STDOUT: %X.as.Destroy.impl.Op.call.loc26: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound.loc26(%addr.loc26_3) +// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc18: = bound_method %.loc18_19, constants.%T.as.Destroy.impl.Op.d31 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc18: = bound_method %.loc18_19, %T.as.Destroy.impl.Op.specific_fn.3 +// CHECK:STDOUT: %addr.loc18_19: %ptr.7b2 = addr_of %.loc18_19 +// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc18: init %empty_tuple.type = call %bound_method.loc18(%addr.loc18_19) +// CHECK:STDOUT: %X.as.Destroy.impl.Op.bound.loc18: = bound_method %i.var, constants.%X.as.Destroy.impl.Op +// CHECK:STDOUT: %addr.loc18_3: %ptr.d17 = addr_of %i.var +// CHECK:STDOUT: %X.as.Destroy.impl.Op.call.loc18: init %empty_tuple.type = call %X.as.Destroy.impl.Op.bound.loc18(%addr.loc18_3) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- unsafe_remove_partial.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] +// CHECK:STDOUT: %pattern_type.1c6: type = pattern_type %ptr.d17 [concrete] +// CHECK:STDOUT: %.e71: type = partial_type %X [concrete] +// CHECK:STDOUT: %ptr.7b2: type = ptr_type %.e71 [concrete] +// CHECK:STDOUT: %pattern_type.019: type = pattern_type %X [concrete] +// CHECK:STDOUT: %reference.var: ref %X = var file.%reference.var_patt [concrete] +// CHECK:STDOUT: %addr.c6b: %ptr.d17 = addr_of %reference.var [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %v.patt: %pattern_type.019 = binding_pattern v [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %value.ref: %.e71 = name_ref value, file.%value +// CHECK:STDOUT: %X.ref.loc14_30: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc14_27.1: %X = as_compatible %value.ref +// CHECK:STDOUT: %.loc14_27.2: %X = converted %value.ref, %.loc14_27.1 +// CHECK:STDOUT: %X.ref.loc14_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %v: %X = bind_name v, %.loc14_27.2 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.1c6 = binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %reference.ref: ref %.e71 = name_ref reference, file.%reference [concrete = file.%reference.var] +// CHECK:STDOUT: %X.ref.loc15_37: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %.loc15_34.1: ref %X = as_compatible %reference.ref [concrete = constants.%reference.var] +// CHECK:STDOUT: %.loc15_34.2: ref %X = converted %reference.ref, %.loc15_34.1 [concrete = constants.%reference.var] +// CHECK:STDOUT: %addr: %ptr.d17 = addr_of %.loc15_34.2 [concrete = constants.%addr.c6b] +// CHECK:STDOUT: %.loc15_11: type = splice_block %ptr.loc15 [concrete = constants.%ptr.d17] { +// CHECK:STDOUT: %X.ref.loc15_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc15: type = ptr_type %X.ref.loc15_10 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %ptr.d17 = bind_name a, %addr +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.1c6 = binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %ptr.ref: %ptr.7b2 = name_ref ptr, file.%ptr.loc10_5 +// CHECK:STDOUT: %X.ref.loc16_29: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc16_30: type = ptr_type %X.ref.loc16_29 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: %.loc16_26.1: %ptr.d17 = as_compatible %ptr.ref +// CHECK:STDOUT: %.loc16_26.2: %ptr.d17 = converted %ptr.ref, %.loc16_26.1 +// CHECK:STDOUT: %.loc16_11: type = splice_block %ptr.loc16_11 [concrete = constants.%ptr.d17] { +// CHECK:STDOUT: %X.ref.loc16_10: type = name_ref X, file.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr.loc16_11: type = ptr_type %X.ref.loc16_10 [concrete = constants.%ptr.d17] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %ptr.d17 = bind_name b, %.loc16_26.2 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/as/unsafe_as.carbon b/toolchain/check/testdata/as/unsafe_as.carbon index 7884b5a0c921..aae2797a899d 100644 --- a/toolchain/check/testdata/as/unsafe_as.carbon +++ b/toolchain/check/testdata/as/unsafe_as.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/unformed.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -10,18 +10,6 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/unsafe_as.carbon -// --- qualifiers.carbon - -library "[[@TEST_NAME]]"; - -class X {} - -fn Convert(p: const X*) -> X* { - //@dump-sem-ir-begin - return p unsafe as X*; - //@dump-sem-ir-end -} - // --- fail_no_conversion.carbon library "[[@TEST_NAME]]"; @@ -30,7 +18,7 @@ class A {}; class B {}; fn Convert(a: A) -> B { - // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+7]]:10: error: cannot convert expression of type `A` to `B` with `as` [ConversionFailure] + // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+7]]:10: error: cannot convert expression of type `A` to `B` with `unsafe as` [ConversionFailure] // CHECK:STDERR: return a unsafe as B; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+4]]:10: note: type `A` does not implement interface `Core.UnsafeAs(B)` [MissingImplInMemberAccessNote] @@ -39,43 +27,3 @@ fn Convert(a: A) -> B { // CHECK:STDERR: return a unsafe as B; } - -// --- fail_remove_qualifiers_without_unsafe.carbon - -library "[[@TEST_NAME]]"; - -class X {} - -fn Convert(p: const X*) -> X* { - // CHECK:STDERR: fail_remove_qualifiers_without_unsafe.carbon:[[@LINE+7]]:10: error: cannot convert expression of type `const X*` to `X*` with `as` [ConversionFailure] - // CHECK:STDERR: return p as X*; - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: fail_remove_qualifiers_without_unsafe.carbon:[[@LINE+4]]:10: note: type `const X*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessNote] - // CHECK:STDERR: return p as X*; - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: - return p as X*; -} - -// CHECK:STDOUT: --- qualifiers.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %X: type = class_type @X [concrete] -// CHECK:STDOUT: %ptr.d17: type = ptr_type %X [concrete] -// CHECK:STDOUT: %const: type = const_type %X [concrete] -// CHECK:STDOUT: %ptr.cbd: type = ptr_type %const [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @Convert(%p.param: %ptr.cbd) -> %ptr.d17 { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %p.ref: %ptr.cbd = name_ref p, %p -// CHECK:STDOUT: %X.ref.loc8: type = name_ref X, file.%X.decl [concrete = constants.%X] -// CHECK:STDOUT: %ptr.loc8: type = ptr_type %X.ref.loc8 [concrete = constants.%ptr.d17] -// CHECK:STDOUT: %.loc8_19.1: %ptr.d17 = as_compatible %p.ref -// CHECK:STDOUT: %.loc8_19.2: %ptr.d17 = converted %p.ref, %.loc8_19.1 -// CHECK:STDOUT: return %.loc8_19.2 -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_bad_type.carbon b/toolchain/check/testdata/class/fail_base_bad_type.carbon index 97f7f2436b7b..121c020b5404 100644 --- a/toolchain/check/testdata/class/fail_base_bad_type.carbon +++ b/toolchain/check/testdata/class/fail_base_bad_type.carbon @@ -56,6 +56,13 @@ class DeriveFromi32 { // It's not really important whether this conversion produces an error or not, // but it shouldn't crash. +// CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+7]]:53: error: cannot implicitly convert expression of type `DeriveFromi32*` to `i32*` [ConversionFailure] +// CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; } +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+4]]:53: note: type `DeriveFromi32*` does not implement interface `Core.ImplicitAs(i32*)` [MissingImplInMemberAccessNote] +// CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; } +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; } // CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+4]]:70: error: member name `n` not found in `DeriveFromi32` [MemberNameNotFoundInInstScope] diff --git a/toolchain/check/testdata/interop/cpp/class/base.carbon b/toolchain/check/testdata/interop/cpp/class/base.carbon index 3c9e776dc69f..14f811de90f6 100644 --- a/toolchain/check/testdata/interop/cpp/class/base.carbon +++ b/toolchain/check/testdata/interop/cpp/class/base.carbon @@ -178,6 +178,13 @@ library "[[@TEST_NAME]]"; import Cpp library "virtual_inheritance.h"; fn Convert(p: Cpp.B*) -> Cpp.A* { + // CHECK:STDERR: fail_todo_use_virtual_inheritance.carbon:[[@LINE+21]]:3: error: semantics TODO: `class with virtual bases` [SemanticsTodo] + // CHECK:STDERR: return p; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_todo_use_virtual_inheritance.carbon:[[@LINE+18]]:3: note: while completing C++ type `Cpp.B` [InCppTypeCompletion] + // CHECK:STDERR: return p; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: // CHECK:STDERR: fail_todo_use_virtual_inheritance.carbon:[[@LINE+14]]:3: error: semantics TODO: `class with virtual bases` [SemanticsTodo] // CHECK:STDERR: return p; // CHECK:STDERR: ^~~~~~~~~ diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index 759f0e1b744b..703cf7a35c63 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -138,10 +138,20 @@ auto GetConstType(Context& context, SemIR::TypeInstId inner_type_id) auto GetQualifiedType(Context& context, SemIR::TypeId type_id, SemIR::TypeQualifiers quals) -> SemIR::TypeId { - if ((quals & SemIR::TypeQualifiers::Const) != SemIR::TypeQualifiers::None) { + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Const)) { type_id = GetConstType(context, context.types().GetInstId(type_id)); quals &= ~SemIR::TypeQualifiers::Const; } + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::MaybeUnformed)) { + type_id = GetTypeImpl( + context, context.types().GetInstId(type_id)); + quals &= ~SemIR::TypeQualifiers::MaybeUnformed; + } + if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Partial)) { + type_id = GetTypeImpl( + context, context.types().GetInstId(type_id)); + quals &= ~SemIR::TypeQualifiers::Partial; + } CARBON_CHECK(quals == SemIR::TypeQualifiers::None); return type_id; } diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 921022c1cbce..86be9959a302 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -764,8 +764,7 @@ static auto BuildTypeForInst(FileContext& context, SemIR::ClassType inst) } template - requires(InstT::Kind.template IsAnyOf< - SemIR::ConstType, SemIR::MaybeUnformedType, SemIR::PartialType>()) + requires(SemIR::Internal::HasInstCategory) static auto BuildTypeForInst(FileContext& context, InstT inst) -> llvm::Type* { return context.GetType( context.sem_ir().types().GetTypeIdForTypeInstId(inst.inner_id)); diff --git a/toolchain/sem_ir/inst_categories.h b/toolchain/sem_ir/inst_categories.h index a3454258d29f..454c6c5c473d 100644 --- a/toolchain/sem_ir/inst_categories.h +++ b/toolchain/sem_ir/inst_categories.h @@ -167,6 +167,18 @@ struct AnyParamPattern { CallParamIndex index; }; +// A type qualifier that wraps another type and has the same object +// representation. Qualifiers are arranged so that adding a qualifier is +// generally safe, and removing a qualifier is not necessarily safe or correct. +struct AnyQualifiedType { + using CategoryInfo = CategoryOf; + + InstKind kind; + + TypeId type_id; + TypeInstId inner_id; +}; + // A struct-like type with a list of named fields. struct AnyStructType { using CategoryInfo = CategoryOf; diff --git a/toolchain/sem_ir/type.cpp b/toolchain/sem_ir/type.cpp index fed4dccf256f..ea731f6190e4 100644 --- a/toolchain/sem_ir/type.cpp +++ b/toolchain/sem_ir/type.cpp @@ -95,12 +95,42 @@ auto TypeStore::GetTransitiveAdaptedType(TypeId type_id) const -> TypeId { auto TypeStore::GetUnqualifiedTypeAndQualifiers(TypeId type_id) const -> std::pair { - if (auto const_type = TryGetAs(type_id)) { - return {file_->types().GetTypeIdForTypeInstId(const_type->inner_id), - TypeQualifiers::Const}; + TypeQualifiers quals = TypeQualifiers::None; + while (true) { + if (auto qualified_type = TryGetAs(type_id)) { + type_id = file_->types().GetTypeIdForTypeInstId(qualified_type->inner_id); + switch (qualified_type->kind) { + case ConstType::Kind: + quals |= TypeQualifiers::Const; + break; + case MaybeUnformedType::Kind: + quals |= TypeQualifiers::MaybeUnformed; + break; + case PartialType::Kind: + quals |= TypeQualifiers::Partial; + break; + default: + CARBON_FATAL("Unknown type qualifier {0}", qualified_type->kind); + } + } else { + return {type_id, quals}; + } + } +} + +auto TypeStore::GetTransitiveUnqualifiedAdaptedType(TypeId type_id) const + -> std::pair { + TypeQualifiers quals = TypeQualifiers::None; + while (true) { + type_id = GetTransitiveAdaptedType(type_id); + auto [unqual_type_id, inner_quals] = + GetUnqualifiedTypeAndQualifiers(type_id); + if (unqual_type_id == type_id) { + return {type_id, quals}; + } + type_id = unqual_type_id; + quals |= inner_quals; } - // TODO: Look through PartialType when this is reachable/testable - return {type_id, TypeQualifiers::None}; } auto TypeStore::TryGetIntTypeInfo(TypeId int_type_id) const diff --git a/toolchain/sem_ir/type.h b/toolchain/sem_ir/type.h index a05477dc0682..c76c84113424 100644 --- a/toolchain/sem_ir/type.h +++ b/toolchain/sem_ir/type.h @@ -21,11 +21,18 @@ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); enum class TypeQualifiers { None = 0, Const = 1 << 0, - // TODO: Partial + MaybeUnformed = 1 << 1, + Partial = 1 << 2, - LLVM_MARK_AS_BITMASK_ENUM(Const) + LLVM_MARK_AS_BITMASK_ENUM(Partial) }; +// Returns whether the type qualifier set `quals` contains `qual`. +inline auto HasTypeQualifier(TypeQualifiers quals, TypeQualifiers qual) + -> bool { + return (quals & qual) != TypeQualifiers::None; +} + // Provides a ValueStore wrapper with an API specific to types. class TypeStore : public Yaml::Printable { public: @@ -182,6 +189,11 @@ class TypeStore : public Yaml::Printable { auto GetUnqualifiedTypeAndQualifiers(TypeId type_id) const -> std::pair; + // Returns the non-adapter unqualified type that is compatible with the + // specified type. + auto GetTransitiveUnqualifiedAdaptedType(TypeId type_id) const + -> std::pair; + // Determines whether the given type is a signed integer type. This includes // the case where the type is `Core.IntLiteral` or a class type whose object // representation is a signed integer type. diff --git a/toolchain/testing/testdata/min_prelude/parts/maybe_unformed.carbon b/toolchain/testing/testdata/min_prelude/parts/maybe_unformed.carbon new file mode 100644 index 000000000000..86973d9bac33 --- /dev/null +++ b/toolchain/testing/testdata/min_prelude/parts/maybe_unformed.carbon @@ -0,0 +1,17 @@ +// 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/parts/destroy.carbon + +// --- min_prelude/parts/maybe_unformed.carbon + +package Core library "prelude/parts/maybe_unformed"; + +export import library "prelude/parts/destroy"; + +private fn Make(t: type) -> type = "maybe_unformed.make_type"; + +class MaybeUnformed(T:! type) { + adapt Make(T); +} diff --git a/toolchain/testing/testdata/min_prelude/unformed.carbon b/toolchain/testing/testdata/min_prelude/unformed.carbon new file mode 100644 index 000000000000..f2fff175705e --- /dev/null +++ b/toolchain/testing/testdata/min_prelude/unformed.carbon @@ -0,0 +1,15 @@ +// 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/parts/as.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/parts/maybe_unformed.carbon +// EXTRA-ARGS: --custom-core --exclude-dump-file-prefix=min_prelude/ + +// --- min_prelude/uint.carbon + +// A minimal prelude for test involving `Core.MaybeUnformed(T)`. +package Core library "prelude"; + +export import library "prelude/parts/as"; +export import library "prelude/parts/maybe_unformed";