mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:20:13 +01:00
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:
co-authored by
Jon Ross-Perkins
parent
d7957cc4e1
commit
b28fd7ebc9
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+1
-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;
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user