|
|
@@ -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;
|
|
|
}
|