// 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 "executable_semantics/interpreter/resolve_names.h" #include "executable_semantics/ast/declaration.h" #include "llvm/Support/Casting.h" using llvm::cast; namespace Carbon { namespace { // Populates names for a pattern. See PopulateNamesInDeclaration for overall // flow. void PopulateNamesInPattern(const Pattern& pattern, StaticScope& static_scope) { switch (pattern.kind()) { case PatternKind::AlternativePattern: { const auto& alt = cast(pattern); PopulateNamesInPattern(alt.arguments(), static_scope); break; } case PatternKind::BindingPattern: { const auto& binding = cast(pattern); if (binding.name().has_value()) { static_scope.Add(*binding.name(), &binding); } break; } case PatternKind::TuplePattern: { const auto& tuple = cast(pattern); for (auto* field : tuple.fields()) { PopulateNamesInPattern(*field, static_scope); } break; } case PatternKind::AutoPattern: case PatternKind::ExpressionPattern: // These don't add names. break; } } // Populates names for a statement. See PopulateNamesInDeclaration for overall // flow. void PopulateNamesInStatement(Arena* arena, std::optional> opt_statement, StaticScope& static_scope) { if (!opt_statement.has_value()) { return; } Statement& statement = **opt_statement; switch (statement.kind()) { case StatementKind::Block: { // Defines a new scope for names. auto& block = cast(statement); for (const auto& statement : block.statements()) { PopulateNamesInStatement(arena, statement, block.static_scope()); } break; } case StatementKind::Continuation: { // Defines a new name and contains a block. auto& cont = cast(statement); static_scope.Add(cont.continuation_variable(), &cont); PopulateNamesInStatement(arena, &cont.body(), static_scope); break; } case StatementKind::VariableDefinition: { // Defines a new name. const auto& var = cast(statement); PopulateNamesInPattern(var.pattern(), static_scope); break; } case StatementKind::If: { // Contains blocks. auto& if_stmt = cast(statement); PopulateNamesInStatement(arena, &if_stmt.then_block(), static_scope); PopulateNamesInStatement(arena, if_stmt.else_block(), static_scope); break; } case StatementKind::While: { // Contains a block. auto& while_stmt = cast(statement); PopulateNamesInStatement(arena, &while_stmt.body(), static_scope); break; } case StatementKind::Match: { // Contains blocks. auto& match = cast(statement); for (auto& clause : match.clauses()) { PopulateNamesInPattern(clause.pattern(), clause.static_scope()); PopulateNamesInStatement(arena, &clause.statement(), clause.static_scope()); } break; } case StatementKind::Assign: case StatementKind::Await: case StatementKind::Break: case StatementKind::Continue: case StatementKind::ExpressionStatement: case StatementKind::Return: case StatementKind::Run: // Neither contains names nor a scope. break; } } // Populates names for a member. See PopulateNamesInDeclaration for overall // flow. void PopulateNamesInMember(Arena* arena, const Member& member, StaticScope& static_scope) { switch (member.kind()) { case MemberKind::FieldMember: { const auto& field = cast(member); if (field.binding().name().has_value()) { static_scope.Add(*field.binding().name(), &member); } break; } } } // Populates declared names at scoped boundaries, such as file-level or // function bodies. This doesn't currently recurse into expressions, but // likely will in the future in order to resolve names in lambdas. void PopulateNamesInDeclaration(Arena* arena, Declaration& declaration, StaticScope& static_scope) { switch (declaration.kind()) { case DeclarationKind::FunctionDeclaration: { auto& func = cast(declaration); static_scope.Add(func.name(), &declaration); for (Nonnull param : func.deduced_parameters()) { func.static_scope().Add(param->name(), param); } PopulateNamesInPattern(func.param_pattern(), func.static_scope()); PopulateNamesInStatement(arena, func.body(), static_scope); break; } case DeclarationKind::ClassDeclaration: { auto& class_decl = cast(declaration); static_scope.Add(class_decl.name(), &declaration); for (auto* member : class_decl.members()) { PopulateNamesInMember(arena, *member, class_decl.static_scope()); } break; } case DeclarationKind::ChoiceDeclaration: { auto& choice = cast(declaration); static_scope.Add(choice.name(), &declaration); for (Nonnull alt : choice.alternatives()) { choice.static_scope().Add(alt->name(), alt); } // Populate name into declared_names. // Init the choice's declared_names, and populate it with the // alternatives. break; } case DeclarationKind::VariableDeclaration: auto& var = cast(declaration); if (var.binding().name().has_value()) { static_scope.Add(*(var.binding().name()), &var.binding()); } return; } } // TODO: ResolveNames for Expression, Member, Pattern, and Statement will be // needed for recursion. // Recurses through a declaration to find and resolve IdentifierExpressions // using declared_names. void ResolveNamesInDeclaration(Declaration& declaration, const StaticScope& static_scope) { switch (declaration.kind()) { case DeclarationKind::FunctionDeclaration: case DeclarationKind::ClassDeclaration: case DeclarationKind::ChoiceDeclaration: case DeclarationKind::VariableDeclaration: break; } } } // namespace void ResolveNames(Arena* arena, AST& ast) { for (auto declaration : ast.declarations) { PopulateNamesInDeclaration(arena, *declaration, ast.static_scope); } for (auto declaration : ast.declarations) { ResolveNamesInDeclaration(*declaration, ast.static_scope); } } } // namespace Carbon