diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index d957ae6df9d9..5acd6e49fadd 100644 --- a/explorer/data/prelude.carbon +++ b/explorer/data/prelude.carbon @@ -712,9 +712,8 @@ class Optional(T:! type) { } } Assert(false, "Attempted to unwrap empty Optional"); - // TODO: Drop uninitialized variable & return when we can flag unreachable paths. - var y: T; - return y; + // TODO: Drop return when we can flag unreachable paths. + return self.Get(); } var element: OptionalElement(T); diff --git a/explorer/interpreter/BUILD b/explorer/interpreter/BUILD index 6847b363c283..83e52485c586 100644 --- a/explorer/interpreter/BUILD +++ b/explorer/interpreter/BUILD @@ -219,6 +219,7 @@ cc_library( "resolve_unformed.h", ], deps = [ + ":stack_space", "//common:check", "//explorer/ast", "//explorer/ast:static_scope", diff --git a/explorer/interpreter/heap.cpp b/explorer/interpreter/heap.cpp index 196694ced619..27df83ba37fd 100644 --- a/explorer/interpreter/heap.cpp +++ b/explorer/interpreter/heap.cpp @@ -38,6 +38,11 @@ auto Heap::Write(const Address& a, Nonnull v, SourceLocation source_loc) -> ErrorOr { CARBON_RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc)); if (states_[a.allocation_.index_] == ValueState::Uninitialized) { + if (!a.element_path_.IsEmpty()) { + return ProgramError(source_loc) + << "undefined behavior: store to subobject of uninitialized value " + << *values_[a.allocation_.index_]; + } states_[a.allocation_.index_] = ValueState::Alive; } CARBON_ASSIGN_OR_RETURN(values_[a.allocation_.index_], diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 69a0b2d4b12a..20d151460705 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -1596,10 +1596,6 @@ auto Interpreter::StepExp() -> ErrorOr { *print_stream_ << llvm::formatv(format_string); break; case 1: { - if ((*args[1]).kind() == Value::Kind::UninitializedValue) { - return ProgramError(exp.source_loc()) - << "Printing uninitialized value"; - } *print_stream_ << llvm::formatv(format_string, cast(*args[1]).value()); break; @@ -2134,18 +2130,6 @@ auto Interpreter::StepStmt() -> ErrorOr { if (definition.has_init()) { CARBON_ASSIGN_OR_RETURN( v, Convert(act.results()[0], dest_type, stmt.source_loc())); - } else if (dest_type->kind() == Value::Kind::StaticArrayType) { - const auto& array = cast(dest_type); - CARBON_CHECK(array->has_size()); - const auto& element_type = array->element_type(); - const auto size = array->size(); - - std::vector> elements; - elements.reserve(size); - for (size_t i = 0; i < size; i++) { - elements.push_back(arena_->New(&element_type)); - } - v = arena_->New(Value::Kind::TupleValue, elements); } else { v = arena_->New(p); } @@ -2290,7 +2274,6 @@ auto Interpreter::StepDeclaration() -> ErrorOr { switch (decl.kind()) { case DeclarationKind::VariableDeclaration: { const auto& var_decl = cast(decl); - const auto* var_type = &var_decl.binding().static_type(); if (var_decl.has_initializer()) { if (act.pos() == 0) { return todo_.Spawn( @@ -2303,22 +2286,6 @@ auto Interpreter::StepDeclaration() -> ErrorOr { todo_.Initialize(&var_decl.binding(), v); return todo_.FinishAction(); } - } else if (var_type->kind() == Value::Kind::StaticArrayType) { - const auto& array = cast(var_type); - CARBON_CHECK(array->has_size()); - const auto& element_type = array->element_type(); - const auto size = array->size(); - - std::vector> elements; - elements.reserve(size); - for (size_t i = 0; i < size; i++) { - elements.push_back(arena_->New(&element_type)); - } - - Nonnull v = - arena_->New(Value::Kind::TupleValue, elements); - todo_.Initialize(&var_decl.binding(), v); - return todo_.FinishAction(); } else { Nonnull v = arena_->New(&var_decl.binding().value()); diff --git a/explorer/interpreter/resolve_names.cpp b/explorer/interpreter/resolve_names.cpp index e6945ab1f028..c62fce1fbd8a 100644 --- a/explorer/interpreter/resolve_names.cpp +++ b/explorer/interpreter/resolve_names.cpp @@ -276,7 +276,7 @@ auto NameResolver::AddExposedNames(const Declaration& declaration, auto NameResolver::ResolveNames(Expression& expression, const StaticScope& enclosing_scope) -> ErrorOr> { - return RunWithExtraStack>>( + return RunWithExtraStack( [&]() { return ResolveNamesImpl(expression, enclosing_scope); }); } @@ -463,7 +463,7 @@ auto NameResolver::ResolveNamesImpl(Expression& expression, auto NameResolver::ResolveNames(WhereClause& clause, const StaticScope& enclosing_scope) -> ErrorOr { - return RunWithExtraStack>( + return RunWithExtraStack( [&]() { return ResolveNamesImpl(clause, enclosing_scope); }); } @@ -499,7 +499,7 @@ auto NameResolver::ResolveNamesImpl(WhereClause& clause, auto NameResolver::ResolveNames(Pattern& pattern, StaticScope& enclosing_scope) -> ErrorOr { - return RunWithExtraStack>( + return RunWithExtraStack( [&]() { return ResolveNamesImpl(pattern, enclosing_scope); }); } @@ -560,7 +560,7 @@ auto NameResolver::ResolveNamesImpl(Pattern& pattern, auto NameResolver::ResolveNames(Statement& statement, StaticScope& enclosing_scope) -> ErrorOr { - return RunWithExtraStack>( + return RunWithExtraStack( [&]() { return ResolveNamesImpl(statement, enclosing_scope); }); } @@ -703,7 +703,7 @@ auto NameResolver::ResolveNames(Declaration& declaration, StaticScope& enclosing_scope, ResolveFunctionBodies bodies) -> ErrorOr { - return RunWithExtraStack>( + return RunWithExtraStack( [&]() { return ResolveNamesImpl(declaration, enclosing_scope, bodies); }); } @@ -922,7 +922,7 @@ auto NameResolver::ResolveNamesImpl(Declaration& declaration, } auto ResolveNames(AST& ast) -> ErrorOr { - return RunWithExtraStack>([&]() -> ErrorOr { + return RunWithExtraStack([&]() -> ErrorOr { NameResolver resolver; StaticScope file_scope; diff --git a/explorer/interpreter/resolve_unformed.cpp b/explorer/interpreter/resolve_unformed.cpp index 0b048f10d742..58610fd21001 100644 --- a/explorer/interpreter/resolve_unformed.cpp +++ b/explorer/interpreter/resolve_unformed.cpp @@ -11,6 +11,7 @@ #include "explorer/ast/expression.h" #include "explorer/ast/pattern.h" #include "explorer/common/nonnull.h" +#include "explorer/interpreter/stack_space.h" using llvm::cast; @@ -54,22 +55,35 @@ auto FlowFacts::TakeAction(Nonnull node, ActionType action, return Success(); } -// Traverses the sub-AST rooted at the given node, resolving the formed/unformed -// states of local variables within it and updating the flow facts. -static auto ResolveUnformed(Nonnull expression, - FlowFacts& flow_facts, FlowFacts::ActionType action) +static auto ResolveUnformedImpl(Nonnull expression, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr; -static auto ResolveUnformed(Nonnull pattern, - FlowFacts& flow_facts, FlowFacts::ActionType action) +static auto ResolveUnformedImpl(Nonnull pattern, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr; -static auto ResolveUnformed(Nonnull statement, - FlowFacts& flow_facts, FlowFacts::ActionType action) +static auto ResolveUnformedImpl(Nonnull statement, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr; -static auto ResolveUnformed(Nonnull declaration) +static auto ResolveUnformedImpl(Nonnull expression, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr; -static auto ResolveUnformed(Nonnull expression, - FlowFacts& flow_facts, FlowFacts::ActionType action) +// Traverses the sub-AST rooted at the given node, resolving the formed/unformed +// states of local variables within it and updating the flow facts. +template +static auto ResolveUnformed(Nonnull expression, FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr { + return RunWithExtraStack( + [&] { return ResolveUnformedImpl(expression, flow_facts, action); }); +} + +static auto ResolveUnformedImpl(Nonnull expression, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr { switch (expression->kind()) { case ExpressionKind::IdentifierExpression: { @@ -85,6 +99,12 @@ static auto ResolveUnformed(Nonnull expression, ResolveUnformed(&call.argument(), flow_facts, action)); break; } + case ExpressionKind::IntrinsicExpression: { + const auto& intrin = cast(*expression); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&intrin.args(), flow_facts, action)); + break; + } case ExpressionKind::TupleLiteral: for (Nonnull field : cast(*expression).fields()) { @@ -100,6 +120,9 @@ static auto ResolveUnformed(Nonnull expression, // When a variable is taken address of, defer the unformed check to // runtime. A more sound analysis can be implemented when a // points-to analysis is available. + // TODO: This isn't enough to permit &x.y or &x[i] when x is + // uninitialized, because x.y and x[i] both require x to be + // initialized. ResolveUnformed(opt_exp.arguments().front(), flow_facts, FlowFacts::ActionType::Form)); } else { @@ -117,15 +140,35 @@ static auto ResolveUnformed(Nonnull expression, } break; case ExpressionKind::SimpleMemberAccessExpression: - CARBON_RETURN_IF_ERROR(ResolveUnformed( - &cast(*expression).object(), flow_facts, - FlowFacts::ActionType::Check)); + case ExpressionKind::CompoundMemberAccessExpression: + case ExpressionKind::BaseAccessExpression: + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&cast(*expression).object(), + flow_facts, FlowFacts::ActionType::Check)); break; case ExpressionKind::BuiltinConvertExpression: CARBON_RETURN_IF_ERROR(ResolveUnformed( cast(*expression).source_expression(), flow_facts, FlowFacts::ActionType::Check)); break; + case ExpressionKind::IndexExpression: + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&cast(*expression).object(), + flow_facts, FlowFacts::ActionType::Check)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&cast(*expression).offset(), + flow_facts, FlowFacts::ActionType::Check)); + break; + case ExpressionKind::IfExpression: { + const auto& if_exp = cast(*expression); + CARBON_RETURN_IF_ERROR(ResolveUnformed(&if_exp.condition(), flow_facts, + FlowFacts::ActionType::Check)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&if_exp.then_expression(), flow_facts, action)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&if_exp.else_expression(), flow_facts, action)); + break; + } case ExpressionKind::DotSelfExpression: case ExpressionKind::IntLiteral: case ExpressionKind::BoolLiteral: @@ -135,13 +178,8 @@ static auto ResolveUnformed(Nonnull expression, case ExpressionKind::StringTypeLiteral: case ExpressionKind::TypeTypeLiteral: case ExpressionKind::ValueLiteral: - case ExpressionKind::IndexExpression: - case ExpressionKind::CompoundMemberAccessExpression: - case ExpressionKind::BaseAccessExpression: - case ExpressionKind::IfExpression: case ExpressionKind::WhereExpression: case ExpressionKind::StructTypeLiteral: - case ExpressionKind::IntrinsicExpression: case ExpressionKind::UnimplementedExpression: case ExpressionKind::FunctionTypeLiteral: case ExpressionKind::ArrayTypeLiteral: @@ -150,8 +188,9 @@ static auto ResolveUnformed(Nonnull expression, return Success(); } -static auto ResolveUnformed(Nonnull pattern, - FlowFacts& flow_facts, FlowFacts::ActionType action) +static auto ResolveUnformedImpl(Nonnull pattern, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr { switch (pattern->kind()) { case PatternKind::BindingPattern: { @@ -178,8 +217,9 @@ static auto ResolveUnformed(Nonnull pattern, return Success(); } -static auto ResolveUnformed(Nonnull statement, - FlowFacts& flow_facts, FlowFacts::ActionType action) +static auto ResolveUnformedImpl(Nonnull statement, + FlowFacts& flow_facts, + FlowFacts::ActionType action) -> ErrorOr { switch (statement->kind()) { case StatementKind::Block: { @@ -280,15 +320,36 @@ static auto ResolveUnformed(Nonnull statement, } break; } + case StatementKind::For: { + const auto& for_stmt = cast(*statement); + CARBON_RETURN_IF_ERROR(ResolveUnformed( + &for_stmt.loop_target(), flow_facts, FlowFacts::ActionType::Check)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&for_stmt.body(), flow_facts, action)); + break; + } case StatementKind::Break: case StatementKind::Continue: - case StatementKind::For: // do nothing break; } return Success(); } +static auto ResolveUnformed(Nonnull declaration) + -> ErrorOr; + +static auto ResolveUnformed( + llvm::ArrayRef> declarations) + -> ErrorOr { + return RunWithExtraStack([declarations]() -> ErrorOr { + for (Nonnull declaration : declarations) { + CARBON_RETURN_IF_ERROR(ResolveUnformed(declaration)); + } + return Success(); + }); +} + static auto ResolveUnformed(Nonnull declaration) -> ErrorOr { switch (declaration->kind()) { @@ -306,12 +367,7 @@ static auto ResolveUnformed(Nonnull declaration) break; } case DeclarationKind::NamespaceDeclaration: - case DeclarationKind::ClassDeclaration: case DeclarationKind::MixDeclaration: - case DeclarationKind::MixinDeclaration: - case DeclarationKind::InterfaceDeclaration: - case DeclarationKind::ConstraintDeclaration: - case DeclarationKind::ImplDeclaration: case DeclarationKind::MatchFirstDeclaration: case DeclarationKind::ChoiceDeclaration: case DeclarationKind::VariableDeclaration: @@ -322,6 +378,16 @@ static auto ResolveUnformed(Nonnull declaration) case DeclarationKind::AliasDeclaration: // do nothing break; + case DeclarationKind::ClassDeclaration: + return ResolveUnformed(cast(declaration)->members()); + case DeclarationKind::MixinDeclaration: + return ResolveUnformed(cast(declaration)->members()); + case DeclarationKind::InterfaceDeclaration: + case DeclarationKind::ConstraintDeclaration: + return ResolveUnformed( + cast(declaration)->members()); + case DeclarationKind::ImplDeclaration: + return ResolveUnformed(cast(declaration)->members()); } return Success(); } diff --git a/explorer/interpreter/stack_space.h b/explorer/interpreter/stack_space.h index 0d936fd76dd0..8dd224d10a07 100644 --- a/explorer/interpreter/stack_space.h +++ b/explorer/interpreter/stack_space.h @@ -27,11 +27,13 @@ auto RunWithExtraStackHelper(llvm::function_ref fn) -> void; // create the current thread. // // Usage: -// return RunWithExtraStack([&]() -> ReturnType { +// return RunWithExtraStack([&]() -> ReturnType { // // }); -template -auto RunWithExtraStack(llvm::function_ref fn) -> ReturnType { +template +auto RunWithExtraStack(Fn fn) -> decltype(fn()) { + using ReturnType = decltype(fn()); + static_assert(!std::is_reference_v); if (Internal::IsStackSpaceNearlyExhausted()) { std::optional result; Internal::RunWithExtraStackHelper([&] { result = fn(); }); diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index f295d74a0f86..951938f9851c 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2742,8 +2742,7 @@ auto TypeChecker::CheckAddrMeAccess( auto TypeChecker::TypeCheckExp(Nonnull e, const ImplScope& impl_scope) -> ErrorOr { - return RunWithExtraStack>( - [&]() { return TypeCheckExpImpl(e, impl_scope); }); + return RunWithExtraStack([&]() { return TypeCheckExpImpl(e, impl_scope); }); } // NOLINTNEXTLINE(readability-function-size) diff --git a/explorer/parse_and_execute/parse_and_execute.cpp b/explorer/parse_and_execute/parse_and_execute.cpp index 6ffdeba7e0d4..06f52d40b9a0 100644 --- a/explorer/parse_and_execute/parse_and_execute.cpp +++ b/explorer/parse_and_execute/parse_and_execute.cpp @@ -40,7 +40,7 @@ static auto ParseAndExecuteHelper(std::function(Arena*)> parse, Nonnull trace_stream, Nonnull print_stream) -> ErrorOr { - return RunWithExtraStack>([&]() -> ErrorOr { + return RunWithExtraStack([&]() -> ErrorOr { Arena arena; auto cursor = std::chrono::steady_clock::now(); diff --git a/explorer/testdata/array/fail_print_uninitalized_array_element.carbon b/explorer/testdata/array/fail_print_uninitalized_array_element.carbon index 1150cf488196..da61deb605ae 100644 --- a/explorer/testdata/array/fail_print_uninitalized_array_element.carbon +++ b/explorer/testdata/array/fail_print_uninitalized_array_element.carbon @@ -8,7 +8,7 @@ package ExplorerTest impl; fn Main() -> i32 { var my_array : [i32; 1]; - // CHECK:STDERR: RUNTIME ERROR: fail_print_uninitalized_array_element.carbon:[[@LINE+1]]: Printing uninitialized value + // CHECK:STDERR: COMPILATION ERROR: fail_print_uninitalized_array_element.carbon:[[@LINE+1]]: use of uninitialized variable my_array Print("{0}", my_array[0]); return 0; } diff --git a/explorer/testdata/array/uninitialized_local_array_access.carbon b/explorer/testdata/array/fail_store_to_uninitialized_array.carbon similarity index 72% rename from explorer/testdata/array/uninitialized_local_array_access.carbon rename to explorer/testdata/array/fail_store_to_uninitialized_array.carbon index ec2f93a4558f..fafcf5d37787 100644 --- a/explorer/testdata/array/uninitialized_local_array_access.carbon +++ b/explorer/testdata/array/fail_store_to_uninitialized_array.carbon @@ -3,13 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE -// CHECK:STDOUT: 100 -// CHECK:STDOUT: result: 0 package ExplorerTest api; fn Main() -> i32 { var my_array : [i32; 1]; + // CHECK:STDERR: COMPILATION ERROR: fail_store_to_uninitialized_array.carbon:[[@LINE+1]]: use of uninitialized variable my_array my_array[0] = 100; Print("{0}", my_array[0]); return 0; diff --git a/explorer/testdata/array/fail_store_to_uninitialized_global_array.carbon b/explorer/testdata/array/fail_store_to_uninitialized_global_array.carbon new file mode 100644 index 000000000000..a6684f71480b --- /dev/null +++ b/explorer/testdata/array/fail_store_to_uninitialized_global_array.carbon @@ -0,0 +1,16 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +var my_array : [i32; 1]; + +fn Main() -> i32 { + // CHECK:STDERR: RUNTIME ERROR: fail_store_to_uninitialized_global_array.carbon:[[@LINE+1]]: undefined behavior: store to subobject of uninitialized value Uninit> + my_array[0] = 100; + Print("{0}", my_array[0]); + return 0; +} diff --git a/explorer/testdata/choice/generic_choice_nested_in_template_class.carbon b/explorer/testdata/choice/generic_choice_nested_in_template_class.carbon index d52d6c054129..031a7f3573a0 100644 --- a/explorer/testdata/choice/generic_choice_nested_in_template_class.carbon +++ b/explorer/testdata/choice/generic_choice_nested_in_template_class.carbon @@ -31,14 +31,14 @@ class MyOptional(T:! type){ } fn get[self: Self] () -> T { - var y: T; var x: MyOptionalElement(T) = self.element; match(x){ case MyOptionalElement(T).Element( var x: T ) =>{ return x; } } - return y; + // TODO: Mark this as unreachable somehow. + return get(); } var element: MyOptionalElement(T); diff --git a/explorer/testdata/class/fail_store_to_uninitialized_class.carbon b/explorer/testdata/class/fail_store_to_uninitialized_class.carbon new file mode 100644 index 000000000000..e8fee33057c2 --- /dev/null +++ b/explorer/testdata/class/fail_store_to_uninitialized_class.carbon @@ -0,0 +1,23 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +class Point { + var x: i32; + var y: i32; +} + +fn Main() -> i32 { + var p: Point; + if (1 == 0) { + p = {.x = 0, .y = 0}; + } + // CHECK:STDERR: RUNTIME ERROR: fail_store_to_uninitialized_class.carbon:[[@LINE+1]]: undefined behavior: store to subobject of uninitialized value Uninit> + p.x = 1; + p.y = 2; + return p.x; +} diff --git a/explorer/testdata/array/uninitialized_global_array_access.carbon b/explorer/testdata/unformed/fail_array.carbon similarity index 65% rename from explorer/testdata/array/uninitialized_global_array_access.carbon rename to explorer/testdata/unformed/fail_array.carbon index 9ba45db5b2ae..f0efb8b74d9d 100644 --- a/explorer/testdata/array/uninitialized_global_array_access.carbon +++ b/explorer/testdata/unformed/fail_array.carbon @@ -3,15 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE -// CHECK:STDOUT: 100 -// CHECK:STDOUT: result: 0 package ExplorerTest api; -var my_array : [i32; 1]; - fn Main() -> i32 { - my_array[0] = 100; - Print("{0}", my_array[0]); - return 0; + var v: [i32; 2]; + // CHECK:STDERR: COMPILATION ERROR: fail_array.carbon:[[@LINE+1]]: use of uninitialized variable v + return v[0]; } diff --git a/explorer/testdata/unformed/fail_array_dynamic.carbon b/explorer/testdata/unformed/fail_array_dynamic.carbon new file mode 100644 index 000000000000..d12bd732b88f --- /dev/null +++ b/explorer/testdata/unformed/fail_array_dynamic.carbon @@ -0,0 +1,16 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var v: [i32; 2]; + if (0 == 1) { + v = (1, 2); + } + // CHECK:STDERR: RUNTIME ERROR: fail_array_dynamic.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + return v[0]; +} diff --git a/explorer/testdata/unformed/fail_base_access.carbon b/explorer/testdata/unformed/fail_base_access.carbon new file mode 100644 index 000000000000..13e2ca3405f7 --- /dev/null +++ b/explorer/testdata/unformed/fail_base_access.carbon @@ -0,0 +1,19 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +base class B { + var n: i32; +} + +class D extends B {} + +fn Main() -> i32 { + var d: D; + // CHECK:STDERR: COMPILATION ERROR: fail_base_access.carbon:[[@LINE+1]]: use of uninitialized variable d + return d.n; +} diff --git a/explorer/testdata/unformed/fail_compound_member_access.carbon b/explorer/testdata/unformed/fail_compound_member_access.carbon new file mode 100644 index 000000000000..4e6cc123f2e5 --- /dev/null +++ b/explorer/testdata/unformed/fail_compound_member_access.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 +// +// AUTOUPDATE + +package ExplorerTest api; + +class C { + var n: i32; +} + +fn Main() -> i32 { + var c: C; + // CHECK:STDERR: COMPILATION ERROR: fail_compound_member_access.carbon:[[@LINE+1]]: use of uninitialized variable c + return c.(C.n); +} diff --git a/explorer/testdata/unformed/fail_control_flow_defer_to_dynamic.carbon b/explorer/testdata/unformed/fail_control_flow_defer_to_dynamic.carbon new file mode 100644 index 000000000000..7f0491e0e00f --- /dev/null +++ b/explorer/testdata/unformed/fail_control_flow_defer_to_dynamic.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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var x: i32; + if (0 == 1) { + x = 0; + } + // Static analysis thinks `x` may be formed, defer the check to run-time. + // CHECK:STDERR: RUNTIME ERROR: fail_control_flow_defer_to_dynamic.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + return x; +} diff --git a/explorer/testdata/unformed/fail_if_cond.carbon b/explorer/testdata/unformed/fail_if_cond.carbon new file mode 100644 index 000000000000..07d735cbeb78 --- /dev/null +++ b/explorer/testdata/unformed/fail_if_cond.carbon @@ -0,0 +1,13 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var v: bool; + // CHECK:STDERR: COMPILATION ERROR: fail_if_cond.carbon:[[@LINE+1]]: use of uninitialized variable v + return if v then 1 else 2; +} diff --git a/explorer/testdata/unformed/fail_if_else.carbon b/explorer/testdata/unformed/fail_if_else.carbon new file mode 100644 index 000000000000..b3f7e4639375 --- /dev/null +++ b/explorer/testdata/unformed/fail_if_else.carbon @@ -0,0 +1,13 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var v: i32; + // CHECK:STDERR: COMPILATION ERROR: fail_if_else.carbon:[[@LINE+1]]: use of uninitialized variable v + return if false then 1 else v; +} diff --git a/explorer/testdata/unformed/fail_if_then.carbon b/explorer/testdata/unformed/fail_if_then.carbon new file mode 100644 index 000000000000..539a2a74864f --- /dev/null +++ b/explorer/testdata/unformed/fail_if_then.carbon @@ -0,0 +1,13 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var v: i32; + // CHECK:STDERR: COMPILATION ERROR: fail_if_then.carbon:[[@LINE+1]]: use of uninitialized variable v + return if true then v else 2; +} diff --git a/explorer/testdata/unformed/fail_in_class.carbon b/explorer/testdata/unformed/fail_in_class.carbon new file mode 100644 index 000000000000..642c5f4f98b2 --- /dev/null +++ b/explorer/testdata/unformed/fail_in_class.carbon @@ -0,0 +1,19 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +class C { + fn F() -> i32 { + var n: i32; + // CHECK:STDERR: COMPILATION ERROR: fail_in_class.carbon:[[@LINE+1]]: use of uninitialized variable n + return n; + } +} + +fn Main() -> i32 { + return C.F(); +} diff --git a/explorer/testdata/unformed/fail_indirect_member_access.carbon b/explorer/testdata/unformed/fail_indirect_member_access.carbon new file mode 100644 index 000000000000..b7b57454bab6 --- /dev/null +++ b/explorer/testdata/unformed/fail_indirect_member_access.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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var pt: {.x: i32, .y: i32}; + // TODO: Without this, we don't allow taking the address of `pt.x`. + // That's probably too restrictive. + if (1 == 0) { pt = {.x = 1, .y = 2}; } + var p: i32* = &pt.x; + // CHECK:STDERR: RUNTIME ERROR: fail_indirect_member_access.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + return *p; +} diff --git a/explorer/testdata/unformed/fail_intrinsic.carbon b/explorer/testdata/unformed/fail_intrinsic.carbon new file mode 100644 index 000000000000..52ef6cfc6b7a --- /dev/null +++ b/explorer/testdata/unformed/fail_intrinsic.carbon @@ -0,0 +1,13 @@ +// 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 +// +// AUTOUPDATE + +package ExplorerTest api; + +fn Main() -> i32 { + var v: i32; + // CHECK:STDERR: COMPILATION ERROR: fail_intrinsic.carbon:[[@LINE+1]]: use of uninitialized variable v + return if __intrinsic_int_eq(v, v) then 1 else 2; +}