Files
carbon-lang/toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon
T
Dana Jansens 6c6552ce57 Consistently return runtime phase if the operands contain a runtime (#5729)
Currently if the first operand contains an error, we will return error,
even though the second operands contains a runtime, and it has a
stronger priority (the phase always goes up if possible).

Import is only allowed on instructions with compile-time values, so we
crash if we ever try to import a runtime value. Importable instructions
must diagnose unexpected runtime values and produce errors in the semir
from which they would be imported so that runtime values are never
imported by another semir.

If we had an instruction where you had an error value from the first
operand, and runtime from the second, and we imported it:
- Before https://github.com/carbon-language/carbon-lang/pull/5728 we
would crash in import, but only because we treated errors as runtime
- After https://github.com/carbon-language/carbon-lang/pull/5728 we
would import ErrorInst because we propagate errors. This is desirable
for cases with compile-time values and errors present only.
- After this PR, we would crash again, cuz you're importing a runtime
thing.

This change means that instructions containing an
`InstConstantKind::Never` instruction like`ValueParam` will consistently
evaluate to a runtime value, even if there are errors present. This is
visible in the `BindName` instructions changing in the semir, where they
became constant `ErrorInst` values previously but no longer do.
2025-07-02 19:21:41 +00:00

120 lines
6.6 KiB
Plaintext

// 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/int.carbon
// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon
fn Deref(n: i32) {
// CHECK:STDERR: fail_deref_not_pointer.carbon:[[@LINE+4]]:3: error: cannot dereference operand of non-pointer type `i32` [DerefOfNonPointer]
// CHECK:STDERR: *n;
// CHECK:STDERR: ^
// CHECK:STDERR:
*n;
// CHECK:STDERR: fail_deref_not_pointer.carbon:[[@LINE+4]]:4: error: cannot apply `->` operator to non-pointer type `i32` [ArrowOperatorOfNonPointer]
// CHECK:STDERR: n->foo;
// CHECK:STDERR: ^~
// CHECK:STDERR:
n->foo;
// CHECK:STDERR: fail_deref_not_pointer.carbon:[[@LINE+4]]:3: error: cannot dereference operand of non-pointer type `()` [DerefOfNonPointer]
// CHECK:STDERR: *();
// CHECK:STDERR: ^
// CHECK:STDERR:
*();
// CHECK:STDERR: fail_deref_not_pointer.carbon:[[@LINE+4]]:5: error: cannot apply `->` operator to non-pointer type `()` [ArrowOperatorOfNonPointer]
// CHECK:STDERR: ()->foo;
// CHECK:STDERR: ^~
// CHECK:STDERR:
()->foo;
// CHECK:STDERR: fail_deref_not_pointer.carbon:[[@LINE+4]]:3: error: cannot dereference operand of non-pointer type `{}` [DerefOfNonPointer]
// CHECK:STDERR: *{};
// CHECK:STDERR: ^
// CHECK:STDERR:
*{};
// CHECK:STDERR: fail_deref_not_pointer.carbon:[[@LINE+4]]:5: error: cannot apply `->` operator to non-pointer type `{}` [ArrowOperatorOfNonPointer]
// CHECK:STDERR: {}->foo;
// CHECK:STDERR: ^~
// CHECK:STDERR:
{}->foo;
}
// CHECK:STDOUT: --- fail_deref_not_pointer.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %Deref.type: type = fn_type @Deref [concrete]
// CHECK:STDOUT: %Deref: %Deref.type = struct_value () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: .Int = %Core.Int
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .Deref = %Deref.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %Deref.decl: %Deref.type = fn_decl @Deref [concrete = constants.%Deref] {
// CHECK:STDOUT: %n.patt: %pattern_type.7ce = binding_pattern n [concrete]
// CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %n.param: %i32 = value_param call_param0
// CHECK:STDOUT: %.loc15: type = splice_block %i32 [concrete = constants.%i32] {
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: }
// CHECK:STDOUT: %n: %i32 = bind_name n, %n.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Deref(%n.param: %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %n.ref.loc20: %i32 = name_ref n, %n
// CHECK:STDOUT: %.loc20: ref <error> = deref %n.ref.loc20
// CHECK:STDOUT: %n.ref.loc25: %i32 = name_ref n, %n
// CHECK:STDOUT: %.loc25: ref <error> = deref %n.ref.loc25
// CHECK:STDOUT: %foo.ref.loc25: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %.loc30_5.1: %empty_tuple.type = tuple_literal ()
// CHECK:STDOUT: %empty_tuple.loc30: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc30_5.2: %empty_tuple.type = converted %.loc30_5.1, %empty_tuple.loc30 [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc30_3: ref <error> = deref %.loc30_5.2 [concrete = <error>]
// CHECK:STDOUT: %.loc35_4.1: %empty_tuple.type = tuple_literal ()
// CHECK:STDOUT: %empty_tuple.loc35: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc35_4.2: %empty_tuple.type = converted %.loc35_4.1, %empty_tuple.loc35 [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc35_5: ref <error> = deref %.loc35_4.2 [concrete = <error>]
// CHECK:STDOUT: %foo.ref.loc35: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %.loc40_5.1: %empty_struct_type = struct_literal ()
// CHECK:STDOUT: %empty_struct.loc40: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
// CHECK:STDOUT: %.loc40_5.2: %empty_struct_type = converted %.loc40_5.1, %empty_struct.loc40 [concrete = constants.%empty_struct]
// CHECK:STDOUT: %.loc40_3: ref <error> = deref %.loc40_5.2 [concrete = <error>]
// CHECK:STDOUT: %.loc45_4.1: %empty_struct_type = struct_literal ()
// CHECK:STDOUT: %empty_struct.loc45: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
// CHECK:STDOUT: %.loc45_4.2: %empty_struct_type = converted %.loc45_4.1, %empty_struct.loc45 [concrete = constants.%empty_struct]
// CHECK:STDOUT: %.loc45_5: ref <error> = deref %.loc45_4.2 [concrete = <error>]
// CHECK:STDOUT: %foo.ref.loc45: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: