| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- // 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
- #include "explorer/interpreter/resolve_unformed.h"
- #include <unordered_map>
- #include "common/check.h"
- #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;
- };
- // 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)
- -> 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> {
- 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();
- }
- break;
- }
- case ExpressionKind::CallExpression: {
- auto& call = cast<CallExpression>(*expression);
- CARBON_RETURN_IF_ERROR(
- ResolveUnformed(&call.argument(), flow_facts, /*set_formed=*/false));
- break;
- }
- case ExpressionKind::TupleLiteral:
- for (Nonnull<const Expression*> field :
- cast<TupleLiteral>(*expression).fields()) {
- CARBON_RETURN_IF_ERROR(
- ResolveUnformed(field, flow_facts, /*set_formed=*/false));
- }
- break;
- case ExpressionKind::OperatorExpression: {
- auto& opt_exp = cast<OperatorExpression>(*expression);
- if (opt_exp.op() == Operator::AddressOf) {
- CARBON_CHECK(opt_exp.arguments().size() == 1)
- << "OperatorExpression with op & can only have 1 argument";
- CARBON_RETURN_IF_ERROR(
- // When a variable is taken address of, defer the unformed check to
- // runtime. A more sound analysis can be implemented when a
- // points-to analysis is available.
- ResolveUnformed(opt_exp.arguments().front(), flow_facts,
- /*set_formed=*/true));
- } else {
- for (Nonnull<const Expression*> operand : opt_exp.arguments()) {
- CARBON_RETURN_IF_ERROR(
- ResolveUnformed(operand, flow_facts, /*set_formed=*/false));
- }
- }
- break;
- }
- case ExpressionKind::DotSelfExpression:
- case ExpressionKind::IntLiteral:
- case ExpressionKind::BoolLiteral:
- case ExpressionKind::BoolTypeLiteral:
- case ExpressionKind::IntTypeLiteral:
- case ExpressionKind::StringLiteral:
- case ExpressionKind::StringTypeLiteral:
- case ExpressionKind::TypeTypeLiteral:
- 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:
- case ExpressionKind::FunctionTypeLiteral:
- case ExpressionKind::ArrayTypeLiteral:
- case ExpressionKind::InstantiateImpl:
- break;
- }
- return Success();
- }
- static auto ResolveUnformed(
- Nonnull<const Pattern*> pattern,
- std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts,
- const bool has_init) -> ErrorOr<Success> {
- switch (pattern->kind()) {
- case PatternKind::BindingPattern:
- flow_facts.insert(
- {Nonnull<const AstNode*>(&cast<BindingPattern>(*pattern)),
- {has_init}});
- break;
- case PatternKind::TuplePattern:
- for (Nonnull<const Pattern*> field :
- cast<TuplePattern>(*pattern).fields()) {
- CARBON_RETURN_IF_ERROR(ResolveUnformed(field, flow_facts, has_init));
- }
- break;
- case PatternKind::GenericBinding:
- case PatternKind::AlternativePattern:
- case PatternKind::ExpressionPattern:
- case PatternKind::AutoPattern:
- case PatternKind::VarPattern:
- case PatternKind::AddrPattern:
- // do nothing
- break;
- }
- return Success();
- }
- static auto ResolveUnformed(
- Nonnull<const Statement*> statement,
- std::unordered_map<Nonnull<const AstNode*>, FlowFact>& flow_facts)
- -> 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));
- }
- break;
- }
- case StatementKind::VariableDefinition: {
- auto& def = cast<VariableDefinition>(*statement);
- CARBON_RETURN_IF_ERROR(ResolveUnformed(&def.pattern(), flow_facts,
- /*has_init=*/def.has_init()));
- break;
- }
- case StatementKind::ReturnVar:
- // TODO: @slaterlatiao: Implement this flow.
- 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));
- 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));
- break;
- }
- case StatementKind::ExpressionStatement: {
- auto& exp_stmt = cast<ExpressionStatement>(*statement);
- CARBON_RETURN_IF_ERROR(ResolveUnformed(&exp_stmt.expression(), flow_facts,
- /*set_formed=*/false));
- 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:
- // do nothing
- break;
- }
- return Success();
- }
- static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
- -> ErrorOr<Success> {
- switch (declaration->kind()) {
- // Checks formed/unformed state intraprocedurally.
- // Can be extended to an interprocedural analysis when a call graph is
- // available.
- 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));
- }
- break;
- }
- case DeclarationKind::ClassDeclaration:
- case DeclarationKind::InterfaceDeclaration:
- case DeclarationKind::ImplDeclaration:
- case DeclarationKind::ChoiceDeclaration:
- case DeclarationKind::VariableDeclaration:
- case DeclarationKind::AssociatedConstantDeclaration:
- case DeclarationKind::SelfDeclaration:
- case DeclarationKind::AliasDeclaration:
- // do nothing
- break;
- }
- return Success();
- }
- auto ResolveUnformed(const AST& ast) -> ErrorOr<Success> {
- for (auto declaration : ast.declarations) {
- CARBON_RETURN_IF_ERROR(ResolveUnformed(declaration));
- }
- return Success();
- }
- } // namespace Carbon
|