// 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); block.static_scope().AddParent(&static_scope); 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()) { clause.static_scope().AddParent(&static_scope); 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); func.static_scope().AddParent(&static_scope); 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(), func.static_scope()); break; } case DeclarationKind::ClassDeclaration: { auto& class_decl = cast(declaration); class_decl.static_scope().AddParent(&static_scope); 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); choice.static_scope().AddParent(&static_scope); 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; } } // Populates the named_entity member of all IdentifierExpressions in the // subtree rooted at `expression`, whose nearest enclosing scope is // `enclosing_scope`. static void ResolveNamesInExpression(Expression& expression, const StaticScope& enclosing_scope) { switch (expression.kind()) { case ExpressionKind::CallExpression: { auto& call = cast(expression); ResolveNamesInExpression(call.function(), enclosing_scope); ResolveNamesInExpression(call.argument(), enclosing_scope); break; } case ExpressionKind::FunctionTypeLiteral: { auto& fun_type = cast(expression); ResolveNamesInExpression(fun_type.parameter(), enclosing_scope); ResolveNamesInExpression(fun_type.return_type(), enclosing_scope); break; } case ExpressionKind::FieldAccessExpression: ResolveNamesInExpression( cast(expression).aggregate(), enclosing_scope); break; case ExpressionKind::IndexExpression: { auto& index = cast(expression); ResolveNamesInExpression(index.aggregate(), enclosing_scope); ResolveNamesInExpression(index.offset(), enclosing_scope); break; } case ExpressionKind::PrimitiveOperatorExpression: for (Nonnull operand : cast(expression).arguments()) { ResolveNamesInExpression(*operand, enclosing_scope); } break; case ExpressionKind::TupleLiteral: for (Nonnull field : cast(expression).fields()) { ResolveNamesInExpression(*field, enclosing_scope); } break; case ExpressionKind::StructLiteral: for (FieldInitializer& init : cast(expression).fields()) { ResolveNamesInExpression(init.expression(), enclosing_scope); } break; case ExpressionKind::StructTypeLiteral: for (FieldInitializer& init : cast(expression).fields()) { ResolveNamesInExpression(init.expression(), enclosing_scope); } break; case ExpressionKind::IdentifierExpression: { auto& identifier = cast(expression); identifier.set_named_entity( enclosing_scope.Resolve(identifier.name(), identifier.source_loc())); break; } case ExpressionKind::IntrinsicExpression: ResolveNamesInExpression(cast(expression).args(), enclosing_scope); break; case ExpressionKind::BoolTypeLiteral: case ExpressionKind::BoolLiteral: case ExpressionKind::IntTypeLiteral: case ExpressionKind::ContinuationTypeLiteral: case ExpressionKind::IntLiteral: case ExpressionKind::StringLiteral: case ExpressionKind::StringTypeLiteral: case ExpressionKind::TypeTypeLiteral: break; case ExpressionKind::UnimplementedExpression: FATAL() << "Unimplemented"; } } // Equivalent to ResolveNamesInExpression, but operates on patterns. static void ResolveNamesInPattern(Pattern& pattern, const StaticScope& enclosing_scope) { switch (pattern.kind()) { case PatternKind::BindingPattern: ResolveNamesInPattern(cast(pattern).type(), enclosing_scope); break; case PatternKind::TuplePattern: for (Nonnull field : cast(pattern).fields()) { ResolveNamesInPattern(*field, enclosing_scope); } break; case PatternKind::AlternativePattern: { auto& alternative = cast(pattern); ResolveNamesInExpression(alternative.choice_type(), enclosing_scope); ResolveNamesInPattern(alternative.arguments(), enclosing_scope); break; } case PatternKind::ExpressionPattern: ResolveNamesInExpression(cast(pattern).expression(), enclosing_scope); break; case PatternKind::AutoPattern: break; } } // Equivalent to ResolveNamesInExpression, but operates on statements. static void ResolveNamesInStatement(Statement& statement, const StaticScope& enclosing_scope) { switch (statement.kind()) { case StatementKind::ExpressionStatement: ResolveNamesInExpression( cast(statement).expression(), enclosing_scope); break; case StatementKind::Assign: { auto& assign = cast(statement); ResolveNamesInExpression(assign.lhs(), enclosing_scope); ResolveNamesInExpression(assign.rhs(), enclosing_scope); break; } case StatementKind::VariableDefinition: { auto& def = cast(statement); ResolveNamesInPattern(def.pattern(), enclosing_scope); ResolveNamesInExpression(def.init(), enclosing_scope); break; } case StatementKind::If: { auto& if_stmt = cast(statement); ResolveNamesInExpression(if_stmt.condition(), enclosing_scope); ResolveNamesInStatement(if_stmt.then_block(), enclosing_scope); if (if_stmt.else_block().has_value()) { ResolveNamesInStatement(**if_stmt.else_block(), enclosing_scope); } break; } case StatementKind::Return: ResolveNamesInExpression(cast(statement).expression(), enclosing_scope); break; case StatementKind::Block: { // TODO: this will resolve usages to declarations that occur later in // the block. Figure out how to avoid that. auto& block = cast(statement); for (Nonnull sub_statement : block.statements()) { ResolveNamesInStatement(*sub_statement, block.static_scope()); } break; } case StatementKind::While: { auto& while_stmt = cast(statement); ResolveNamesInExpression(while_stmt.condition(), enclosing_scope); ResolveNamesInStatement(while_stmt.body(), enclosing_scope); break; } case StatementKind::Match: { auto& match = cast(statement); ResolveNamesInExpression(match.expression(), enclosing_scope); for (Match::Clause& clause : match.clauses()) { ResolveNamesInPattern(clause.pattern(), clause.static_scope()); ResolveNamesInStatement(clause.statement(), clause.static_scope()); } break; } case StatementKind::Continuation: ResolveNamesInStatement(cast(statement).body(), enclosing_scope); break; case StatementKind::Run: ResolveNamesInExpression(cast(statement).argument(), enclosing_scope); break; case StatementKind::Await: case StatementKind::Break: case StatementKind::Continue: break; } } // Recurses through a declaration to find and resolve IdentifierExpressions // using declared_names. void ResolveNamesInDeclaration(Declaration& declaration, const StaticScope& enclosing_scope) { switch (declaration.kind()) { case DeclarationKind::FunctionDeclaration: { auto& function = cast(declaration); for (Nonnull binding : function.deduced_parameters()) { ResolveNamesInExpression(binding->type(), function.static_scope()); } ResolveNamesInPattern(function.param_pattern(), function.static_scope()); if (function.return_term().type_expression().has_value()) { ResolveNamesInExpression(**function.return_term().type_expression(), function.static_scope()); } if (function.body().has_value()) { ResolveNamesInStatement(**function.body(), function.static_scope()); } break; } case DeclarationKind::ClassDeclaration: { auto& class_decl = cast(declaration); for (Nonnull member : class_decl.members()) { switch (member->kind()) { case MemberKind::FieldMember: ResolveNamesInPattern(cast(member)->binding(), class_decl.static_scope()); } } break; } case DeclarationKind::ChoiceDeclaration: { auto& choice = cast(declaration); for (Nonnull alternative : choice.alternatives()) { ResolveNamesInExpression(alternative->signature(), choice.static_scope()); } break; } case DeclarationKind::VariableDeclaration: { auto& var = cast(declaration); ResolveNamesInPattern(var.binding(), enclosing_scope); ResolveNamesInExpression(var.initializer(), enclosing_scope); 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); } if (ast.main_call.has_value()) { ResolveNamesInExpression(**ast.main_call, ast.static_scope); } } } // namespace Carbon