Implement more flows on unformed static analysis (#1923)

- Added detection of unformed usage with `return var`, control-flow statements and member access.
- Refactored the work flow: made flow facts a class and moved operations of flow facts into the class.
- Added, cleaned and renamed test cases. Some test cases for the dynamic unformed check were suppressed by the static check. They are now added back.

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Zenong Zhang
2022-08-15 10:53:42 -07:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent d7957cc4e1
commit b28fd7ebc9
32 changed files with 525 additions and 77 deletions
+146 -70
View File
@@ -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<const AstNode*> node, ActionType action,
SourceLocation source_loc, const std::string& name)
-> ErrorOr<Success> {
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<const Expression*> expression,
std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts,
bool set_formed) -> ErrorOr<Success>;
static auto ResolveUnformed(
Nonnull<const Pattern*> pattern,
std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts,
bool has_init) -> ErrorOr<Success>;
static auto ResolveUnformed(
Nonnull<const Statement*> statement,
std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts)
static auto ResolveUnformed(Nonnull<const Expression*> expression,
FlowFacts& flow_facts, FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Pattern*> pattern,
FlowFacts& flow_facts, FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Statement*> statement,
FlowFacts& flow_facts, FlowFacts::ActionType action)
-> ErrorOr<Success>;
static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
-> ErrorOr<Success>;
static auto ResolveUnformed(
Nonnull<const Expression*> expression,
std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts,
const bool set_formed) -> ErrorOr<Success> {
static auto ResolveUnformed(Nonnull<const Expression*> expression,
FlowFacts& flow_facts, FlowFacts::ActionType action)
-> ErrorOr<Success> {
switch (expression->kind()) {
case ExpressionKind::IdentifierExpression: {
auto& identifier = cast<IdentifierExpression>(*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<CallExpression>(*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<const Expression*> field :
cast<TupleLiteral>(*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<const Expression*> 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<StructLiteral>(*expression).fields()) {
CARBON_RETURN_IF_ERROR(ResolveUnformed(&init.expression(), flow_facts,
FlowFacts::ActionType::Check));
}
break;
case ExpressionKind::SimpleMemberAccessExpression:
CARBON_RETURN_IF_ERROR(ResolveUnformed(
&cast<SimpleMemberAccessExpression>(*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<const Pattern*> pattern,
std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts,
const bool has_init) -> ErrorOr<Success> {
static auto ResolveUnformed(Nonnull<const Pattern*> pattern,
FlowFacts& flow_facts, FlowFacts::ActionType action)
-> ErrorOr<Success> {
switch (pattern->kind()) {
case PatternKind::BindingPattern:
flow_facts.insert(
{Nonnull<const AstNode*>(&cast<BindingPattern>(*pattern)),
{has_init}});
break;
case PatternKind::BindingPattern: {
auto& binding_pattern = cast<BindingPattern>(*pattern);
CARBON_RETURN_IF_ERROR(flow_facts.TakeAction(&binding_pattern, action,
binding_pattern.source_loc(),
binding_pattern.name()));
} break;
case PatternKind::TuplePattern:
for (Nonnull<const Pattern*> field :
cast<TuplePattern>(*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<const Statement*> statement,
std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts)
static auto ResolveUnformed(Nonnull<const Statement*> statement,
FlowFacts& flow_facts, FlowFacts::ActionType action)
-> ErrorOr<Success> {
switch (statement->kind()) {
case StatementKind::Block: {
auto& block = cast<Block>(*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<VariableDefinition>(*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<ReturnVar>(*statement);
auto& binding_pattern = cast<BindingPattern>(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<ReturnExpression>(*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<Assign>(*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<ExpressionStatement>(*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<If>(*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<While>(*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<Match>(*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<const Declaration*> declaration)
case DeclarationKind::FunctionDeclaration: {
auto& function = cast<FunctionDeclaration>(*declaration);
if (function.body().has_value()) {
std::unordered_map<Nonnull<const AstNode*>, 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;
}
+43
View File
@@ -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<const AstNode*> node, ActionType action,
SourceLocation source_loc, const std::string& name)
-> ErrorOr<Success>;
private:
enum class FormedState {
MustBeFormed,
MayBeFormed,
Unformed,
};
// Aggregate information about a AstNode being analyzed.
struct Fact {
FormedState formed_state;
};
void AddFact(Nonnull<const AstNode*> node, const FormedState state) {
CARBON_CHECK(facts_.find(node) == facts_.end());
facts_.insert({node, {state}});
}
std::unordered_map<Nonnull<const AstNode*>, 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.
@@ -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;
}
@@ -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<Placeholder<x>>
// CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/unformed/dynamic/fail_global.carbon:[[@LINE+1]]: undefined behavior: access to uninitialized value Uninit<Placeholder<x>>
return x;
}
+25
View File
@@ -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<Placeholder<x>>
return AddInt(x, 2);
}
@@ -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<Placeholder<y>>
return y;
}
+21
View File
@@ -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<Placeholder<x>>
return x;
}
@@ -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<Placeholder<x>>
returned var x: i32;
if (0 == 1) {
x = 0;
}
// Static analysis thinks `x` may be formed, defer the check to run-time.
return var;
}
@@ -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<Placeholder<x>>
y = x;
return y;
}
@@ -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;
}
+20
View File
@@ -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;
}
+23
View File
@@ -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;
}
}
+22
View File
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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);
}
@@ -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;
}
@@ -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<Placeholder<x>>
returned var x: i32;
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/unformed/static/fail_returned_var.carbon:[[@LINE+1]]: use of uninitialized variable x
return var;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}