diff --git a/explorer/interpreter/resolve_unformed.cpp b/explorer/interpreter/resolve_unformed.cpp index dc1110be02f6..83e23184986e 100644 --- a/explorer/interpreter/resolve_unformed.cpp +++ b/explorer/interpreter/resolve_unformed.cpp @@ -10,67 +10,85 @@ #include "explorer/ast/ast.h" #include "explorer/ast/expression.h" #include "explorer/ast/pattern.h" -#include "explorer/common/error_builders.h" #include "explorer/common/nonnull.h" using llvm::cast; namespace Carbon { -// Aggregate information about a AstNode being analyzed. -struct FlowFact { - bool may_be_formed; -}; +auto FlowFacts::TakeAction(Nonnull node, ActionType action, + SourceLocation source_loc, const std::string& name) + -> ErrorOr { + switch (action) { + case ActionType::AddInit: { + AddFact(node, FormedState::MustBeFormed); + break; + } + case ActionType::AddUninit: { + AddFact(node, FormedState::Unformed); + break; + } + case ActionType::Form: { + // TODO: Use CARBON_CHECK when we are able to handle global variables. + auto entry = facts_.find(node); + if (entry != facts_.end() && + entry->second.formed_state == FormedState::Unformed) { + entry->second.formed_state = FormedState::MayBeFormed; + } + break; + } + case ActionType::Check: { + // TODO: @slaterlatiao add all available value nodes to flow facts and use + // CARBON_CHECK on the following line. + auto entry = facts_.find(node); + if (entry != facts_.end() && + entry->second.formed_state == FormedState::Unformed) { + return CompilationError(source_loc) + << "use of uninitialized variable " << name; + } + break; + } + case ActionType::None: + break; + } + 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, - std::unordered_map, FlowFact>& flow_facts, - bool set_formed) -> ErrorOr; -static auto ResolveUnformed( - Nonnull pattern, - std::unordered_map, FlowFact>& flow_facts, - bool has_init) -> ErrorOr; -static auto ResolveUnformed( - Nonnull statement, - std::unordered_map, FlowFact>& flow_facts) +static auto ResolveUnformed(Nonnull expression, + FlowFacts& flow_facts, FlowFacts::ActionType action) + -> ErrorOr; +static auto ResolveUnformed(Nonnull pattern, + FlowFacts& flow_facts, FlowFacts::ActionType action) + -> ErrorOr; +static auto ResolveUnformed(Nonnull statement, + FlowFacts& flow_facts, FlowFacts::ActionType action) -> ErrorOr; static auto ResolveUnformed(Nonnull declaration) -> ErrorOr; -static auto ResolveUnformed( - Nonnull expression, - std::unordered_map, FlowFact>& flow_facts, - const bool set_formed) -> ErrorOr { +static auto ResolveUnformed(Nonnull expression, + FlowFacts& flow_facts, FlowFacts::ActionType action) + -> ErrorOr { switch (expression->kind()) { case ExpressionKind::IdentifierExpression: { auto& identifier = cast(*expression); - auto fact = flow_facts.find(&identifier.value_node().base()); - // TODO: @slaterlatiao add all available value nodes to flow facts and use - // CARBON_CHECK on the following line. - if (fact == flow_facts.end()) { - break; - } - if (set_formed) { - fact->second.may_be_formed = true; - } else if (!fact->second.may_be_formed) { - return CompilationError(identifier.source_loc()) - << "use of uninitialized variable " << identifier.name(); - } + CARBON_RETURN_IF_ERROR( + flow_facts.TakeAction(&identifier.value_node().base(), action, + identifier.source_loc(), identifier.name())); break; } case ExpressionKind::CallExpression: { auto& call = cast(*expression); CARBON_RETURN_IF_ERROR( - ResolveUnformed(&call.argument(), flow_facts, /*set_formed=*/false)); + ResolveUnformed(&call.argument(), flow_facts, action)); break; } case ExpressionKind::TupleLiteral: for (Nonnull field : cast(*expression).fields()) { - CARBON_RETURN_IF_ERROR( - ResolveUnformed(field, flow_facts, /*set_formed=*/false)); + CARBON_RETURN_IF_ERROR(ResolveUnformed(field, flow_facts, action)); } break; case ExpressionKind::OperatorExpression: { @@ -83,15 +101,26 @@ static auto ResolveUnformed( // runtime. A more sound analysis can be implemented when a // points-to analysis is available. ResolveUnformed(opt_exp.arguments().front(), flow_facts, - /*set_formed=*/true)); + FlowFacts::ActionType::Form)); } else { for (Nonnull operand : opt_exp.arguments()) { - CARBON_RETURN_IF_ERROR( - ResolveUnformed(operand, flow_facts, /*set_formed=*/false)); + CARBON_RETURN_IF_ERROR(ResolveUnformed(operand, flow_facts, action)); } } break; } + case ExpressionKind::StructLiteral: + for (const FieldInitializer& init : + cast(*expression).fields()) { + CARBON_RETURN_IF_ERROR(ResolveUnformed(&init.expression(), flow_facts, + FlowFacts::ActionType::Check)); + } + break; + case ExpressionKind::SimpleMemberAccessExpression: + CARBON_RETURN_IF_ERROR(ResolveUnformed( + &cast(*expression).object(), flow_facts, + FlowFacts::ActionType::Check)); + break; case ExpressionKind::DotSelfExpression: case ExpressionKind::IntLiteral: case ExpressionKind::BoolLiteral: @@ -103,11 +132,9 @@ static auto ResolveUnformed( case ExpressionKind::ContinuationTypeLiteral: case ExpressionKind::ValueLiteral: case ExpressionKind::IndexExpression: - case ExpressionKind::SimpleMemberAccessExpression: case ExpressionKind::CompoundMemberAccessExpression: case ExpressionKind::IfExpression: case ExpressionKind::WhereExpression: - case ExpressionKind::StructLiteral: case ExpressionKind::StructTypeLiteral: case ExpressionKind::IntrinsicExpression: case ExpressionKind::UnimplementedExpression: @@ -119,20 +146,20 @@ static auto ResolveUnformed( return Success(); } -static auto ResolveUnformed( - Nonnull pattern, - std::unordered_map, FlowFact>& flow_facts, - const bool has_init) -> ErrorOr { +static auto ResolveUnformed(Nonnull pattern, + FlowFacts& flow_facts, FlowFacts::ActionType action) + -> ErrorOr { switch (pattern->kind()) { - case PatternKind::BindingPattern: - flow_facts.insert( - {Nonnull(&cast(*pattern)), - {has_init}}); - break; + case PatternKind::BindingPattern: { + auto& binding_pattern = cast(*pattern); + CARBON_RETURN_IF_ERROR(flow_facts.TakeAction(&binding_pattern, action, + binding_pattern.source_loc(), + binding_pattern.name())); + } break; case PatternKind::TuplePattern: for (Nonnull field : cast(*pattern).fields()) { - CARBON_RETURN_IF_ERROR(ResolveUnformed(field, flow_facts, has_init)); + CARBON_RETURN_IF_ERROR(ResolveUnformed(field, flow_facts, action)); } break; case PatternKind::GenericBinding: @@ -147,52 +174,100 @@ static auto ResolveUnformed( return Success(); } -static auto ResolveUnformed( - Nonnull statement, - std::unordered_map, FlowFact>& flow_facts) +static auto ResolveUnformed(Nonnull statement, + FlowFacts& flow_facts, FlowFacts::ActionType action) -> ErrorOr { switch (statement->kind()) { case StatementKind::Block: { auto& block = cast(*statement); for (auto* block_statement : block.statements()) { - CARBON_RETURN_IF_ERROR(ResolveUnformed(block_statement, flow_facts)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(block_statement, flow_facts, action)); } break; } case StatementKind::VariableDefinition: { auto& def = cast(*statement); - CARBON_RETURN_IF_ERROR(ResolveUnformed(&def.pattern(), flow_facts, - /*has_init=*/def.has_init())); + if (def.has_init()) { + CARBON_RETURN_IF_ERROR(ResolveUnformed(&def.pattern(), flow_facts, + FlowFacts::ActionType::AddInit)); + CARBON_RETURN_IF_ERROR(ResolveUnformed(&def.init(), flow_facts, + FlowFacts::ActionType::Check)); + } else { + CARBON_RETURN_IF_ERROR(ResolveUnformed( + &def.pattern(), flow_facts, FlowFacts::ActionType::AddUninit)); + } break; } - case StatementKind::ReturnVar: - // TODO: @slaterlatiao: Implement this flow. + case StatementKind::ReturnVar: { + auto& ret_var = cast(*statement); + auto& binding_pattern = cast(ret_var.value_node().base()); + CARBON_RETURN_IF_ERROR( + flow_facts.TakeAction(&binding_pattern, FlowFacts::ActionType::Check, + ret_var.source_loc(), binding_pattern.name())); break; + } case StatementKind::ReturnExpression: { auto& ret_exp_stmt = cast(*statement); CARBON_RETURN_IF_ERROR(ResolveUnformed(&ret_exp_stmt.expression(), - flow_facts, /*set_formed=*/false)); + flow_facts, + FlowFacts::ActionType::Check)); break; } case StatementKind::Assign: { auto& assign = cast(*statement); - CARBON_RETURN_IF_ERROR( - ResolveUnformed(&assign.lhs(), flow_facts, /*set_formed=*/true)); - CARBON_RETURN_IF_ERROR( - ResolveUnformed(&assign.rhs(), flow_facts, /*set_formed=*/false)); + if (assign.lhs().kind() == ExpressionKind::IdentifierExpression) { + CARBON_RETURN_IF_ERROR(ResolveUnformed(&assign.lhs(), flow_facts, + FlowFacts::ActionType::Form)); + } else { + // TODO: Support checking non-identifier lhs expression. + CARBON_RETURN_IF_ERROR(ResolveUnformed(&assign.lhs(), flow_facts, + FlowFacts::ActionType::None)); + } + CARBON_RETURN_IF_ERROR(ResolveUnformed(&assign.rhs(), flow_facts, + FlowFacts::ActionType::Check)); break; } case StatementKind::ExpressionStatement: { auto& exp_stmt = cast(*statement); - CARBON_RETURN_IF_ERROR(ResolveUnformed(&exp_stmt.expression(), flow_facts, - /*set_formed=*/false)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&exp_stmt.expression(), flow_facts, action)); + break; + } + case StatementKind::If: { + auto& if_stmt = cast(*statement); + CARBON_RETURN_IF_ERROR(ResolveUnformed(&if_stmt.condition(), flow_facts, + FlowFacts::ActionType::Check)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&if_stmt.then_block(), flow_facts, action)); + if (if_stmt.else_block().has_value()) { + CARBON_RETURN_IF_ERROR( + ResolveUnformed(*if_stmt.else_block(), flow_facts, action)); + } + break; + } + case StatementKind::While: { + auto& while_stmt = cast(*statement); + CARBON_RETURN_IF_ERROR(ResolveUnformed( + &while_stmt.condition(), flow_facts, FlowFacts::ActionType::Check)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&while_stmt.body(), flow_facts, action)); + break; + } + case StatementKind::Match: { + auto& match = cast(*statement); + CARBON_RETURN_IF_ERROR(ResolveUnformed(&match.expression(), flow_facts, + FlowFacts::ActionType::Check)); + for (auto& clause : match.clauses()) { + CARBON_RETURN_IF_ERROR(ResolveUnformed(&clause.pattern(), flow_facts, + FlowFacts::ActionType::Check)); + CARBON_RETURN_IF_ERROR( + ResolveUnformed(&clause.statement(), flow_facts, action)); + } break; } case StatementKind::Break: case StatementKind::Continue: - case StatementKind::If: - case StatementKind::While: - case StatementKind::Match: case StatementKind::Continuation: case StatementKind::Run: case StatementKind::Await: @@ -212,8 +287,9 @@ static auto ResolveUnformed(Nonnull declaration) case DeclarationKind::FunctionDeclaration: { auto& function = cast(*declaration); if (function.body().has_value()) { - std::unordered_map, FlowFact> flow_facts; - CARBON_RETURN_IF_ERROR(ResolveUnformed(*function.body(), flow_facts)); + FlowFacts flow_facts; + CARBON_RETURN_IF_ERROR(ResolveUnformed(*function.body(), flow_facts, + FlowFacts::ActionType::None)); } break; } diff --git a/explorer/interpreter/resolve_unformed.h b/explorer/interpreter/resolve_unformed.h index 3e65b2be62ce..de48bff7a856 100644 --- a/explorer/interpreter/resolve_unformed.h +++ b/explorer/interpreter/resolve_unformed.h @@ -10,6 +10,49 @@ namespace Carbon { +// Maps AST nodes to flow facts within a function. +class FlowFacts { + public: + enum class ActionType { + // Adds a must-be-formed flow fact. + // Used at `VariableDefinition` with initialization. + AddInit, + // Adds an unformed flow fact. + // Used at `VariableDefinition` without initialization. + AddUninit, + // Marks an unformed flow fact as may-be-formed. + // Used at AST nodes that potentially initializes a variable. + Form, + // Returns compilation error if the AST node is impossible to be formed. + // Used at AST nodes that uses a variable. + Check, + // Used in traversing children nodes without an acion to take. + None, + }; + // Take action on flow facts based on `ActionType`. + auto TakeAction(Nonnull node, ActionType action, + SourceLocation source_loc, const std::string& name) + -> ErrorOr; + + private: + enum class FormedState { + MustBeFormed, + MayBeFormed, + Unformed, + }; + // Aggregate information about a AstNode being analyzed. + struct Fact { + FormedState formed_state; + }; + + void AddFact(Nonnull node, const FormedState state) { + CARBON_CHECK(facts_.find(node) == facts_.end()); + facts_.insert({node, {state}}); + } + + std::unordered_map, Fact> facts_; +}; + // An intraprocedural forward analysis that checks the may-be-formed states on // local variables. Returns compilation error on usage of must-be-unformed // variables. diff --git a/explorer/testdata/unformed/control_flow_defer_to_dynamic.carbon b/explorer/testdata/unformed/control_flow_defer_to_dynamic.carbon new file mode 100644 index 000000000000..8c27b6f79969 --- /dev/null +++ b/explorer/testdata/unformed/control_flow_defer_to_dynamic.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 0 + +package ExplorerTest api; + +fn Main() -> i32 { + var x: i32; + if (0 == 0) { + x = 0; + } + // Static analysis thinks `x` may be formed, defer the check to run-time. + return x; +} diff --git a/explorer/testdata/uninitialized/fail_global_uninitialized.carbon b/explorer/testdata/unformed/dynamic/fail_global.carbon similarity index 74% rename from explorer/testdata/uninitialized/fail_global_uninitialized.carbon rename to explorer/testdata/unformed/dynamic/fail_global.carbon index 3d2eda222809..49e6eed20958 100644 --- a/explorer/testdata/uninitialized/fail_global_uninitialized.carbon +++ b/explorer/testdata/unformed/dynamic/fail_global.carbon @@ -13,6 +13,6 @@ package ExplorerTest api; var x: i32; fn Main() -> i32 { - // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/uninitialized/fail_global_uninitialized.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_global.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> return x; } diff --git a/explorer/testdata/unformed/dynamic/fail_param.carbon b/explorer/testdata/unformed/dynamic/fail_param.carbon new file mode 100644 index 000000000000..865e921dcb93 --- /dev/null +++ b/explorer/testdata/unformed/dynamic/fail_param.carbon @@ -0,0 +1,25 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn AddInt(a: i32, b: i32) -> auto { + return a + b; +} + +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: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_param.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + return AddInt(x, 2); +} diff --git a/explorer/testdata/unformed/dynamic/fail_pattern_declare.carbon b/explorer/testdata/unformed/dynamic/fail_pattern_declare.carbon new file mode 100644 index 000000000000..f72cc8605199 --- /dev/null +++ b/explorer/testdata/unformed/dynamic/fail_pattern_declare.carbon @@ -0,0 +1,22 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var (x: i32, y: i32); + x = 1; + if (0 == 1) { + y = 0; + } + // Static analysis thinks `y` may be formed, defer the check to run-time. + // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_pattern_declare.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + return y; +} diff --git a/explorer/testdata/unformed/dynamic/fail_return.carbon b/explorer/testdata/unformed/dynamic/fail_return.carbon new file mode 100644 index 000000000000..499d47c6eeaa --- /dev/null +++ b/explorer/testdata/unformed/dynamic/fail_return.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +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: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_return.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + return x; +} diff --git a/explorer/testdata/unformed/dynamic/fail_returned_var.carbon b/explorer/testdata/unformed/dynamic/fail_returned_var.carbon new file mode 100644 index 000000000000..6efabc7953bd --- /dev/null +++ b/explorer/testdata/unformed/dynamic/fail_returned_var.carbon @@ -0,0 +1,21 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_returned_var.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + returned var x: i32; + if (0 == 1) { + x = 0; + } + // Static analysis thinks `x` may be formed, defer the check to run-time. + return var; +} diff --git a/explorer/testdata/unformed/dynamic/fail_rhs_assign.carbon b/explorer/testdata/unformed/dynamic/fail_rhs_assign.carbon new file mode 100644 index 000000000000..39761c33c4e3 --- /dev/null +++ b/explorer/testdata/unformed/dynamic/fail_rhs_assign.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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var x: i32; + var y: i32; + if (0 == 1) { + x = 0; + } + // Static analysis thinks `x` may be formed, defer the check to run-time. + // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_rhs_assign.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> + y = x; + return y; +} diff --git a/explorer/testdata/uninitialized/global_declare.carbon b/explorer/testdata/unformed/global_assign_before_use.carbon similarity index 100% rename from explorer/testdata/uninitialized/global_declare.carbon rename to explorer/testdata/unformed/global_assign_before_use.carbon diff --git a/explorer/testdata/uninitialized/global_uninitialized_escape.carbon b/explorer/testdata/unformed/global_escape.carbon similarity index 100% rename from explorer/testdata/uninitialized/global_uninitialized_escape.carbon rename to explorer/testdata/unformed/global_escape.carbon diff --git a/explorer/testdata/uninitialized/global_uninit_without_use.carbon b/explorer/testdata/unformed/global_unformed_without_use.carbon similarity index 100% rename from explorer/testdata/uninitialized/global_uninit_without_use.carbon rename to explorer/testdata/unformed/global_unformed_without_use.carbon diff --git a/explorer/testdata/uninitialized/local_declare.carbon b/explorer/testdata/unformed/local_assign_before_use.carbon similarity index 100% rename from explorer/testdata/uninitialized/local_declare.carbon rename to explorer/testdata/unformed/local_assign_before_use.carbon diff --git a/explorer/testdata/uninitialized/local_uninitialized_escape.carbon b/explorer/testdata/unformed/local_escape.carbon similarity index 100% rename from explorer/testdata/uninitialized/local_uninitialized_escape.carbon rename to explorer/testdata/unformed/local_escape.carbon diff --git a/explorer/testdata/uninitialized/local_uninit_without_use.carbon b/explorer/testdata/unformed/local_unformed_without_use.carbon similarity index 100% rename from explorer/testdata/uninitialized/local_uninit_without_use.carbon rename to explorer/testdata/unformed/local_unformed_without_use.carbon diff --git a/explorer/testdata/uninitialized/local_declare_pattern.carbon b/explorer/testdata/unformed/pattern_declare.carbon similarity index 100% rename from explorer/testdata/uninitialized/local_declare_pattern.carbon rename to explorer/testdata/unformed/pattern_declare.carbon diff --git a/explorer/testdata/uninitialized/declare_returned_var.carbon b/explorer/testdata/unformed/returned_var_assign_before_use.carbon similarity index 100% rename from explorer/testdata/uninitialized/declare_returned_var.carbon rename to explorer/testdata/unformed/returned_var_assign_before_use.carbon diff --git a/explorer/testdata/unformed/static/fail_field_value.carbon b/explorer/testdata/unformed/static/fail_field_value.carbon new file mode 100644 index 000000000000..e30c47ca17f0 --- /dev/null +++ b/explorer/testdata/unformed/static/fail_field_value.carbon @@ -0,0 +1,18 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var x: i32; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_field_value.carbon:[[@LINE+1]]: use of uninitialized variable x + var p: auto = {.x = x,}; + return 0; +} diff --git a/explorer/testdata/unformed/static/fail_if_cond.carbon b/explorer/testdata/unformed/static/fail_if_cond.carbon new file mode 100644 index 000000000000..41a87960a56e --- /dev/null +++ b/explorer/testdata/unformed/static/fail_if_cond.carbon @@ -0,0 +1,20 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var x: i32; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_if_cond.carbon:[[@LINE+1]]: use of uninitialized variable x + if (x == 0) { + return 0; + } + return 1; +} diff --git a/explorer/testdata/unformed/static/fail_if_else.carbon b/explorer/testdata/unformed/static/fail_if_else.carbon new file mode 100644 index 000000000000..5374c92ff372 --- /dev/null +++ b/explorer/testdata/unformed/static/fail_if_else.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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Foo() -> i32; + +fn Main() -> i32 { + var x: i32; + if (Foo() == 0) { + return 0; + } else { + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_if_else.carbon:[[@LINE+1]]: use of uninitialized variable x + return x; + } +} diff --git a/explorer/testdata/unformed/static/fail_if_then.carbon b/explorer/testdata/unformed/static/fail_if_then.carbon new file mode 100644 index 000000000000..063494008c1b --- /dev/null +++ b/explorer/testdata/unformed/static/fail_if_then.carbon @@ -0,0 +1,22 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Foo() -> i32; + +fn Main() -> i32 { + var x: i32; + if (Foo() == 0) { + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_if_then.carbon:[[@LINE+1]]: use of uninitialized variable x + return x; + } + return 1; +} diff --git a/explorer/testdata/uninitialized/fail_local_uninitialized_pattern.carbon b/explorer/testdata/unformed/static/fail_local_pattern_declare.carbon similarity index 78% rename from explorer/testdata/uninitialized/fail_local_uninitialized_pattern.carbon rename to explorer/testdata/unformed/static/fail_local_pattern_declare.carbon index c9ecc3e5d72b..214919bdf134 100644 --- a/explorer/testdata/uninitialized/fail_local_uninitialized_pattern.carbon +++ b/explorer/testdata/unformed/static/fail_local_pattern_declare.carbon @@ -13,6 +13,6 @@ package ExplorerTest api; fn Main() -> i32 { var (x: i32, y: i32); x = 1; - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/uninitialized/fail_local_uninitialized_pattern.carbon:[[@LINE+1]]: use of uninitialized variable y + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_local_pattern_declare.carbon:[[@LINE+1]]: use of uninitialized variable y return y; } diff --git a/explorer/testdata/uninitialized/fail_local_uninitialized_assign.carbon b/explorer/testdata/unformed/static/fail_local_rhs_assign.carbon similarity index 78% rename from explorer/testdata/uninitialized/fail_local_uninitialized_assign.carbon rename to explorer/testdata/unformed/static/fail_local_rhs_assign.carbon index 1109176e9aea..099d581f1474 100644 --- a/explorer/testdata/uninitialized/fail_local_uninitialized_assign.carbon +++ b/explorer/testdata/unformed/static/fail_local_rhs_assign.carbon @@ -13,7 +13,7 @@ package ExplorerTest api; fn Main() -> i32 { var x: i32; var y: i32; - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/uninitialized/fail_local_uninitialized_assign.carbon:[[@LINE+1]]: use of uninitialized variable x + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_local_rhs_assign.carbon:[[@LINE+1]]: use of uninitialized variable x y = x; return y; } diff --git a/explorer/testdata/unformed/static/fail_match_clause.carbon b/explorer/testdata/unformed/static/fail_match_clause.carbon new file mode 100644 index 000000000000..5813f605f111 --- /dev/null +++ b/explorer/testdata/unformed/static/fail_match_clause.carbon @@ -0,0 +1,29 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Foo() -> i32; + +fn Main() -> i32 { + var x : i32; + match (Foo()) { + case 0 => { + return 2; + } + case 1 => { + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_match_clause.carbon:[[@LINE+1]]: use of uninitialized variable x + return x; + } + default => { + return 0; + } + } +} diff --git a/explorer/testdata/unformed/static/fail_match_expression.carbon b/explorer/testdata/unformed/static/fail_match_expression.carbon new file mode 100644 index 000000000000..dd449ad548b5 --- /dev/null +++ b/explorer/testdata/unformed/static/fail_match_expression.carbon @@ -0,0 +1,24 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var x : i32; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_match_expression.carbon:[[@LINE+1]]: use of uninitialized variable x + match (x) { + case 0 => { + return 1; + } + default => { + return 0; + } + } +} diff --git a/explorer/testdata/uninitialized/fail_local_uninitialized_param.carbon b/explorer/testdata/unformed/static/fail_param.carbon similarity index 80% rename from explorer/testdata/uninitialized/fail_local_uninitialized_param.carbon rename to explorer/testdata/unformed/static/fail_param.carbon index 2e2530ed15cc..eac6e7dfac96 100644 --- a/explorer/testdata/uninitialized/fail_local_uninitialized_param.carbon +++ b/explorer/testdata/unformed/static/fail_param.carbon @@ -16,6 +16,6 @@ fn AddInt(a: i32, b: i32) -> auto { fn Main() -> i32 { var x: i32; - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/uninitialized/fail_local_uninitialized_param.carbon:[[@LINE+1]]: use of uninitialized variable x + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_param.carbon:[[@LINE+1]]: use of uninitialized variable x return AddInt(x, 2); } diff --git a/explorer/testdata/uninitialized/fail_local_uninitialized_return.carbon b/explorer/testdata/unformed/static/fail_return.carbon similarity index 78% rename from explorer/testdata/uninitialized/fail_local_uninitialized_return.carbon rename to explorer/testdata/unformed/static/fail_return.carbon index 59670821eab5..3a2f5a0b03ef 100644 --- a/explorer/testdata/uninitialized/fail_local_uninitialized_return.carbon +++ b/explorer/testdata/unformed/static/fail_return.carbon @@ -12,6 +12,6 @@ package ExplorerTest api; fn Main() -> i32 { var x: i32; - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/uninitialized/fail_local_uninitialized_return.carbon:[[@LINE+1]]: use of uninitialized variable x + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_return.carbon:[[@LINE+1]]: use of uninitialized variable x return x; } diff --git a/explorer/testdata/uninitialized/fail_uninitialized_returned_var.carbon b/explorer/testdata/unformed/static/fail_returned_var.carbon similarity index 74% rename from explorer/testdata/uninitialized/fail_uninitialized_returned_var.carbon rename to explorer/testdata/unformed/static/fail_returned_var.carbon index ac7d3ac11ff3..6d42d83e8831 100644 --- a/explorer/testdata/uninitialized/fail_uninitialized_returned_var.carbon +++ b/explorer/testdata/unformed/static/fail_returned_var.carbon @@ -11,7 +11,7 @@ package ExplorerTest api; fn Main() -> i32 { - // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/uninitialized/fail_uninitialized_returned_var.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit> returned var x: i32; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_returned_var.carbon:[[@LINE+1]]: use of uninitialized variable x return var; } diff --git a/explorer/testdata/uninitialized/fail_local_uninitialized_init.carbon b/explorer/testdata/unformed/static/fail_rhs_def.carbon similarity index 78% rename from explorer/testdata/uninitialized/fail_local_uninitialized_init.carbon rename to explorer/testdata/unformed/static/fail_rhs_def.carbon index 50e5b31c2319..274c2943249e 100644 --- a/explorer/testdata/uninitialized/fail_local_uninitialized_init.carbon +++ b/explorer/testdata/unformed/static/fail_rhs_def.carbon @@ -12,7 +12,7 @@ package ExplorerTest api; fn Main() -> i32 { var x: i32; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_rhs_def.carbon:[[@LINE+1]]: use of uninitialized variable x var y: i32 = x; - // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/uninitialized/fail_local_uninitialized_init.carbon:[[@LINE+1]]: use of uninitialized variable x return x; } diff --git a/explorer/testdata/unformed/static/fail_struct_member_access.carbon b/explorer/testdata/unformed/static/fail_struct_member_access.carbon new file mode 100644 index 000000000000..0d8b42f1be1e --- /dev/null +++ b/explorer/testdata/unformed/static/fail_struct_member_access.carbon @@ -0,0 +1,18 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var p: {.x: i32, .y: i32}; + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_struct_member_access.carbon:[[@LINE+1]]: use of uninitialized variable p + p.x = p.y; + return 0; +} diff --git a/explorer/testdata/unformed/static/fail_while_body.carbon b/explorer/testdata/unformed/static/fail_while_body.carbon new file mode 100644 index 000000000000..40025f99e85c --- /dev/null +++ b/explorer/testdata/unformed/static/fail_while_body.carbon @@ -0,0 +1,22 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Foo() -> i32; + +fn Main() -> i32 { + var (x: i32, y: i32); + while (Foo() == 0) { + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_while_body.carbon:[[@LINE+1]]: use of uninitialized variable x + y = x; + } + return 1; +} diff --git a/explorer/testdata/unformed/static/fail_while_cond.carbon b/explorer/testdata/unformed/static/fail_while_cond.carbon new file mode 100644 index 000000000000..f34c3f0dd1fd --- /dev/null +++ b/explorer/testdata/unformed/static/fail_while_cond.carbon @@ -0,0 +1,20 @@ +// 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 +// +// RUN: %{not} %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s + +package ExplorerTest api; + +fn Main() -> i32 { + var (x: i32, y: i32); + // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_while_cond.carbon:[[@LINE+1]]: use of uninitialized variable x + while (x == 0) { + y = 1; + } + return 1; +}