/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 #include #include #include #include #include #include #include #include #include #include #include namespace legacy = solidity::frontend; using namespace std; using namespace solidity::langutil; using namespace solidity::frontend::experimental; ASTTransform::ASTTransform(Analysis& _analysis): m_analysis(_analysis), m_errorReporter(_analysis.errorReporter()), m_ast(make_unique()) { m_ast->nodeById.resize(_analysis.maxAstId() + 1, nullptr); } bool ASTTransform::visit(legacy::TypeDefinition const& _typeDefinition) { SetNode setNode(*this, _typeDefinition); auto [it, newlyInserted] = m_ast->typeDefinitions.emplace(&_typeDefinition, term(_typeDefinition)); solAssert(newlyInserted); return false; } bool ASTTransform::visit(legacy::TypeClassDefinition const& _typeClassDefinition) { SetNode setNode(*this, _typeClassDefinition); auto [it, newlyInserted] = m_ast->typeClasses.emplace(&_typeClassDefinition, term(_typeClassDefinition)); solAssert(newlyInserted); return false; } bool ASTTransform::visit(legacy::TypeClassInstantiation const& _typeClassInstantiation) { SetNode setNode(*this, _typeClassInstantiation); auto [it, newlyInserted] = m_ast->typeClassInstantiations.emplace(&_typeClassInstantiation, term(_typeClassInstantiation)); solAssert(newlyInserted); return false; } bool ASTTransform::visit(legacy::ContractDefinition const& _contractDefinition) { SetNode setNode(*this, _contractDefinition); auto [it, newlyInserted] = m_ast->contracts.emplace(&_contractDefinition, term(_contractDefinition)); solAssert(newlyInserted); return false; } bool ASTTransform::visit(legacy::FunctionDefinition const& _functionDefinition) { SetNode setNode(*this, _functionDefinition); solAssert(m_ast->functions.emplace(&_functionDefinition, term(_functionDefinition)).second); return false; } bool ASTTransform::visitNode(ASTNode const& _node) { m_errorReporter.typeError(0000_error, _node.location(), "Unexpected AST node during AST transform."); return false; } unique_ptr ASTTransform::term(legacy::TypeDefinition const& _typeDefinition) { SetNode setNode(*this, _typeDefinition); unique_ptr name = reference(_typeDefinition); unique_ptr arguments = termOrConstant(_typeDefinition.arguments(), BuiltinConstant::Unit); if (_typeDefinition.typeExpression()) { unique_ptr definiens = term(*_typeDefinition.typeExpression()); return application(BuiltinConstant::TypeDefinition, std::move(name), std::move(arguments), std::move(definiens)); } else return application(BuiltinConstant::TypeDeclaration, std::move(name), std::move(arguments)); } unique_ptr ASTTransform::term(legacy::TypeClassDefinition const& _typeClassDefinition) { SetNode setNode(*this, _typeClassDefinition); unique_ptr typeVariable = term(_typeClassDefinition.typeVariable()); unique_ptr name = reference(_typeClassDefinition); unique_ptr functions = namedFunctionList(_typeClassDefinition.subNodes()); return application( BuiltinConstant::TypeClassDefinition, std::move(typeVariable), std::move(name), std::move(functions) ); } std::unique_ptr ASTTransform::term(legacy::TypeClassInstantiation const& _typeClassInstantiation) { SetNode setNode(*this, _typeClassInstantiation); unique_ptr typeConstructor = term(_typeClassInstantiation.typeConstructor()); unique_ptr argumentSorts = termOrConstant(_typeClassInstantiation.argumentSorts(), BuiltinConstant::Unit); unique_ptr typeClass = term(_typeClassInstantiation.typeClass()); unique_ptr functions = namedFunctionList(_typeClassInstantiation.subNodes()); return application( BuiltinConstant::TypeClassInstantiation, std::move(typeConstructor), std::move(argumentSorts), std::move(typeClass), std::move(functions) ); } std::unique_ptr ASTTransform::term(legacy::FunctionDefinition const& _functionDefinition) { SetNode setNode(*this, _functionDefinition); unique_ptr name = reference(_functionDefinition); unique_ptr arguments = term(_functionDefinition.parameterList()); unique_ptr returnType = termOrConstant(_functionDefinition.experimentalReturnExpression(), BuiltinConstant::Unit); if (_functionDefinition.isImplemented()) { unique_ptr body = term(_functionDefinition.body()); return application( BuiltinConstant::FunctionDefinition, std::move(name), std::move(arguments), std::move(returnType), std::move(body) ); } else return application( BuiltinConstant::FunctionDeclaration, std::move(name), std::move(arguments), std::move(returnType) ); } std::unique_ptr ASTTransform::term(legacy::ContractDefinition const& _contractDefinition) { SetNode setNode(*this, _contractDefinition); unique_ptr name = reference(_contractDefinition); return application( BuiltinConstant::ContractDefinition, std::move(name), namedFunctionList(_contractDefinition.subNodes()) ); } unique_ptr ASTTransform::term(legacy::VariableDeclarationStatement const& _declaration) { SetNode setNode(*this, _declaration); solAssert(_declaration.declarations().size() == 1); unique_ptr value; if (_declaration.initialValue()) value = term(*_declaration.initialValue()); return term(*_declaration.declarations().front(), std::move(value)); } unique_ptr ASTTransform::term(legacy::Assignment const& _assignment) { SetNode setNode(*this, _assignment); if (_assignment.assignmentOperator() == Token::Assign) return application(BuiltinConstant::Assign, _assignment.leftHandSide(), _assignment.rightHandSide()); else solAssert(false); } unique_ptr ASTTransform::term(legacy::Block const& _block) { SetNode setNode(*this, _block); if (auto statements = ranges::fold_right_last( _block.statements() | ranges::view::transform([&](auto stmt) { return term(*stmt); }) | ranges::view::move, [&](auto stmt, auto acc) { return application(BuiltinConstant::ChainStatements, std::move(stmt), std::move(acc)); } )) return application(BuiltinConstant::Block, std::move(*statements)); else return application(BuiltinConstant::Block, constant(BuiltinConstant::Unit)); } unique_ptr ASTTransform::term(legacy::Statement const& _statement) { SetNode setNode(*this, _statement); if (auto const* assembly = dynamic_cast(&_statement)) return application(BuiltinConstant::RegularStatement, *assembly); else if (auto const* declaration = dynamic_cast(&_statement)) return application(BuiltinConstant::RegularStatement, *declaration); else if (auto const* assign = dynamic_cast(&_statement)) return application(BuiltinConstant::RegularStatement, *assign); else if (auto const* expressionStatement = dynamic_cast(&_statement)) return application(BuiltinConstant::RegularStatement, expressionStatement->expression()); else if (auto const* returnStatement = dynamic_cast(&_statement)) return application(BuiltinConstant::ReturnStatement, termOrConstant(returnStatement->expression(), BuiltinConstant::Unit)); else { m_analysis.errorReporter().fatalTypeError(0000_error, _statement.location(), "Unsupported statement."); solAssert(false); } } unique_ptr ASTTransform::term(legacy::TypeName const& _name) { SetNode setNode(*this, _name); if (auto const* elementaryTypeName = dynamic_cast(&_name)) { switch (elementaryTypeName->typeName().token()) { case Token::Void: return constant(BuiltinConstant::Void); case Token::Fun: return constant(BuiltinConstant::Fun); case Token::Unit: return constant(BuiltinConstant::Unit); case Token::Pair: return constant(BuiltinConstant::Pair); case Token::Word: return constant(BuiltinConstant::Word); case Token::Integer: return constant(BuiltinConstant::Integer); case Token::Bool: return constant(BuiltinConstant::Bool); default: m_analysis.errorReporter().typeError(0000_error, m_currentLocation, "Unsupported type."); return constant(BuiltinConstant::Undefined); } } else if (auto const* userDefinedTypeName = dynamic_cast(&_name)) { auto const* declaration = userDefinedTypeName->pathNode().annotation().referencedDeclaration; solAssert(declaration); return reference(*declaration); } else solAssert(false); } unique_ptr ASTTransform::term(legacy::TypeClassName const& _typeClassName) { SetNode setNode(*this, _typeClassName); return std::visit(util::GenericVisitor{ [&](Token _token) -> unique_ptr { return builtinTypeClass(_token); }, [&](ASTPointer _identifierPath) -> unique_ptr { solAssert(_identifierPath->annotation().referencedDeclaration); return reference(*_identifierPath->annotation().referencedDeclaration); } }, _typeClassName.name()); } unique_ptr ASTTransform::term(legacy::ParameterList const& _parameterList) { SetNode setNode(*this, _parameterList); return tuple(_parameterList.parameters() | ranges::view::transform([&](auto parameter) { solAssert(!parameter->value()); return term(*parameter); }) | ranges::view::move | ranges::to>>); } unique_ptr ASTTransform::term(legacy::VariableDeclaration const& _variableDeclaration, std::unique_ptr _initialValue) { SetNode setNode(*this, _variableDeclaration); solAssert(!_variableDeclaration.value()); unique_ptr name = reference(_variableDeclaration); if (_variableDeclaration.typeExpression()) name = constrain(std::move(name), term(*_variableDeclaration.typeExpression())); if (_initialValue) return application(BuiltinConstant::VariableDefinition, std::move(name), std::move(_initialValue)); else return application(BuiltinConstant::VariableDeclaration, std::move(name)); } unique_ptr ASTTransform::term(legacy::InlineAssembly const& _inlineAssembly) { SetNode setNode(*this, _inlineAssembly); std::map> references; // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( yul::Identifier const& _identifier, yul::IdentifierContext _context, bool ) -> bool { if (_context == yul::IdentifierContext::NonExternal) { // TODO: do we need this? // Hack until we can disallow any shadowing: If we found an internal reference, // clear the external references, so that codegen does not use it. _inlineAssembly.annotation().externalReferences.erase(& _identifier); return false; } InlineAssemblyAnnotation::ExternalIdentifierInfo* identifierInfo = util::valueOrNullptr(_inlineAssembly.annotation().externalReferences, &_identifier); if (!identifierInfo) return false; Declaration const* declaration = identifierInfo->declaration; solAssert(!!declaration, ""); solAssert(identifierInfo->suffix == "", ""); SetNode setNode(*this, originLocationOf(_identifier)); references.emplace(&_identifier, reference(*declaration)); identifierInfo->valueSize = 1; return true; }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); _inlineAssembly.annotation().analysisInfo = make_shared(); yul::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_analysis.errorReporter(), _inlineAssembly.dialect(), identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) solAssert(m_analysis.errorReporter().hasErrors()); return makeTerm(_inlineAssembly.operations(), std::move(references)); } unique_ptr ASTTransform::term(legacy::Expression const& _expression) { SetNode setNode(*this, _expression); if (auto const* id = dynamic_cast(&_expression)) { solAssert(id->annotation().referencedDeclaration); return reference(*id->annotation().referencedDeclaration); } else if (auto const* call = dynamic_cast(&_expression)) { list> arguments; for (auto argument: call->arguments()) arguments.emplace_back(term(*argument)); return application(term(call->expression()), std::move(arguments)); } else if (auto const* assign = dynamic_cast(&_expression)) return term(*assign); else if (auto const* memberAccess = dynamic_cast(&_expression)) { unique_ptr memberNameConstant; { SetNode setMemberLocation(*this, memberAccess->memberLocation()); memberNameConstant = constant(memberAccess->memberName()); } return application(BuiltinConstant::MemberAccess, term(memberAccess->expression()), std::move(memberNameConstant)); } else if (auto const* operation = dynamic_cast(&_expression)) { unique_ptr left = term(operation->leftExpression()); unique_ptr right = term(operation->rightExpression()); return binaryOperation(operation->getOperator(), std::move(left), std::move(right)); } else if (auto const* typeNameExpression = dynamic_cast(&_expression)) return term(typeNameExpression->type()); else if (auto const* tupleExpression = dynamic_cast(&_expression)) return tuple(tupleExpression->components() | ranges::view::transform([&](auto component) { return term(*component); }) | ranges::view::move | ranges::to>>); else { m_analysis.errorReporter().fatalTypeError(0000_error, _expression.location(), "Unsupported expression."); return tuple({}); } } unique_ptr ASTTransform::reference(legacy::Declaration const& _declaration) { return makeTerm(static_cast(_declaration.id()), _declaration.name()); } unique_ptr ASTTransform::tuple(list> _components) { if (auto term = ranges::fold_right_last(_components | ranges::view::move, [&](auto a, auto b) { return pair(std::move(a), std::move(b)); })) return std::move(*term); else return constant(BuiltinConstant::Unit); } unique_ptr ASTTransform::constrain(unique_ptr _value, unique_ptr _constraint) { return application(BuiltinConstant::Constrain, std::move(_value), std::move(_constraint)); } std::unique_ptr ASTTransform::namedFunctionList(std::vector> _nodes) { list> functionList; for (auto subNode: _nodes) { auto const *function = dynamic_cast(subNode.get()); solAssert(function); unique_ptr functionName = constant(function->name()); unique_ptr functionDefinition = term(*function); functionList.emplace_back(application(BuiltinConstant::NamedTerm, std::move(functionName), std::move(functionDefinition))); } return tuple(std::move(functionList)); } unique_ptr ASTTransform::builtinBinaryOperator(Token _token) { switch (_token) { case Token::Colon: return constant(BuiltinConstant::Constrain); case Token::RightArrow: return constant(BuiltinConstant::Fun); case Token::Mul: return constant(BuiltinConstant::Mul); case Token::Add: return constant(BuiltinConstant::Add); case Token::Equal: return constant(BuiltinConstant::Equal); default: m_analysis.errorReporter().typeError(0000_error, m_currentLocation, "Unsupported operator."); return constant(BuiltinConstant::Undefined); } } unique_ptr ASTTransform::builtinTypeClass(langutil::Token _token) { switch (_token) { case Token::Mul: return constant(BuiltinConstant::Mul); case Token::Add: return constant(BuiltinConstant::Add); case Token::Integer: return constant(BuiltinConstant::Integer); case Token::Equal: return constant(BuiltinConstant::Equal); default: m_analysis.errorReporter().typeError(0000_error, m_currentLocation, "Invalid type class."); return constant(BuiltinConstant::Undefined); } } TermBase ASTTransform::makeTermBase() { return TermBase{ m_currentLocation, m_currentNode ? make_optional(m_currentNode->id()) : nullopt, std::monostate{} }; }