/* 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()) { } bool ASTTransform::visit(legacy::ContractDefinition const& _contractDefinition) { SetNode setNode(*this, _contractDefinition); auto [it, newlyInserted] = m_ast->contracts.emplace(&_contractDefinition, AST::ContractInfo{}); solAssert(newlyInserted); AST::ContractInfo& contractInfo = it->second; for (auto const& node: _contractDefinition.subNodes()) if (auto const* function = dynamic_cast(node.get())) solAssert(contractInfo.functions.emplace(string{}, functionDefinition(*function)).second); else m_errorReporter.typeError(0000_error, node->location(), "Unsupported contract element."); return false; } bool ASTTransform::visit(legacy::FunctionDefinition const& _functionDefinition) { SetNode setNode(*this, _functionDefinition); solAssert(m_ast->functions.emplace(&_functionDefinition, functionDefinition(_functionDefinition)).second); return false; } bool ASTTransform::visit(legacy::TypeClassDefinition const& _typeClassDefinition) { SetNode setNode(*this, _typeClassDefinition); auto [it, newlyInserted] = m_ast->typeClasses.emplace(&_typeClassDefinition, AST::TypeClassInformation{}); solAssert(newlyInserted); auto& info = it->second; info.typeVariable = term(_typeClassDefinition.typeVariable()); info.declaration = reference(_typeClassDefinition); declare(_typeClassDefinition, *info.declaration); map& functions = info.functions; for (auto subNode: _typeClassDefinition.subNodes()) { auto const *function = dynamic_cast(subNode.get()); solAssert(function); solAssert(functions.emplace(function->name(), functionDefinition(*function)).second); } return false; } bool ASTTransform::visit(legacy::TypeClassInstantiation const& _typeClassInstantiation) { SetNode setNode(*this, _typeClassInstantiation); auto [it, newlyInserted] = m_ast->typeClassInstantiations.emplace(&_typeClassInstantiation, AST::TypeClassInstantiationInformation{}); solAssert(newlyInserted); auto& info = it->second; info.typeClass = std::visit(util::GenericVisitor{ [&](Token _token) -> unique_ptr { return builtinTypeClass(_token); }, [&](ASTPointer _identifierPath) -> unique_ptr { solAssert(_identifierPath->annotation().referencedDeclaration); return reference(*_identifierPath->annotation().referencedDeclaration); } }, _typeClassInstantiation.typeClass().name()); info.typeConstructor = term(_typeClassInstantiation.typeConstructor()); info.argumentSorts = termOrConstant(_typeClassInstantiation.argumentSorts(), BuiltinConstant::Unit); map& functions = info.functions; for (auto subNode: _typeClassInstantiation.subNodes()) { auto const *function = dynamic_cast(subNode.get()); solAssert(function); solAssert(functions.emplace(function->name(), functionDefinition(*function)).second); } return false; } bool ASTTransform::visit(legacy::TypeDefinition const& _typeDefinition) { SetNode setNode(*this, _typeDefinition); auto [it, newlyInserted] = m_ast->typeDefinitions.emplace(&_typeDefinition, AST::TypeInformation{}); solAssert(newlyInserted); auto& info = it->second; info.declaration = makeTerm(reference(_typeDefinition), nullptr); declare(_typeDefinition, *info.declaration); if (_typeDefinition.arguments()) info.arguments = tuple(_typeDefinition.arguments()->parameters() | ranges::view::transform([&](auto argument){ solAssert(!argument->typeExpression()); // TODO: error handling return term(*argument); }) | ranges::view::move | ranges::to>>); if (_typeDefinition.typeExpression()) info.value = term(*_typeDefinition.typeExpression()); return false; } 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(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::Statement const& _statement) { SetNode setNode(*this, _statement); if (auto const* assembly = dynamic_cast(&_statement)) return term(*assembly); else if (auto const* declaration = dynamic_cast(&_statement)) return term(*declaration); else if (auto const* assign = dynamic_cast(&_statement)) return term(*assign); else if (auto const* expressionStatement = dynamic_cast(&_statement)) return term(expressionStatement->expression()); else if (auto const* returnStatement = dynamic_cast(&_statement)) return application(BuiltinConstant::Return, termOrConstant(returnStatement->expression(), BuiltinConstant::Unit)); else { m_analysis.errorReporter().fatalTypeError(0000_error, _statement.location(), "Unsupported statement."); solAssert(false); } } unique_ptr ASTTransform::term(legacy::Block const& _block) { SetNode setNode(*this, _block); if (_block.statements().empty()) return application(BuiltinConstant::Block, constant(BuiltinConstant::Unit)); auto makeStatement = [&](auto _stmt) { return application(BuiltinConstant::Statement, *_stmt); }; return application( BuiltinConstant::Block, ranges::fold_right( _block.statements() | ranges::view::drop(1), makeStatement(_block.statements().front()), [&](auto stmt, auto acc) { return application(BuiltinConstant::ChainStatements, std::move(acc), makeStatement(stmt)); } ) ); } AST::FunctionInfo ASTTransform::functionDefinition(legacy::FunctionDefinition const& _functionDefinition) { SetNode setNode(*this, _functionDefinition); std::unique_ptr body = nullptr; unique_ptr argumentExpression = term(_functionDefinition.parameterList()); if (_functionDefinition.isImplemented()) body = term(_functionDefinition.body()); unique_ptr returnType = termOrConstant(_functionDefinition.experimentalReturnExpression(), BuiltinConstant::Unit); unique_ptr name = reference(_functionDefinition); unique_ptr function = makeTerm(std::move(name), std::move(body)); declare(_functionDefinition, *function); return AST::FunctionInfo{ std::move(function), std::move(argumentExpression), std::move(returnType) }; } unique_ptr ASTTransform::term(legacy::ParameterList const& _parameterList) { SetNode setNode(*this, _parameterList); return tuple(_parameterList.parameters() | ranges::view::transform([&](auto parameter) { 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())); unique_ptr declaration = makeTerm(std::move(name), std::move(_initialValue)); declare(_variableDeclaration, *declaration); return declaration; } 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::binaryOperation( Token _operator, unique_ptr _leftHandSide, unique_ptr _rightHandSide ) { return application(builtinBinaryOperator(_operator), std::move(_leftHandSide), std::move(_rightHandSide)); } unique_ptr ASTTransform::reference(legacy::Declaration const& _declaration) { auto [it, newlyInserted] = m_declarationIndices.emplace(&_declaration, m_ast->declarations.size()); if (newlyInserted) m_ast->declarations.emplace_back(AST::DeclarationInfo{nullptr, {}}); return makeTerm(it->second); } size_t ASTTransform::declare(legacy::Declaration const& _declaration, Term& _term) { auto [it, newlyInserted] = m_declarationIndices.emplace(&_declaration, m_ast->declarations.size()); if (newlyInserted) m_ast->declarations.emplace_back(AST::DeclarationInfo{&_term, _declaration.name()}); else { auto& info = m_ast->declarations.at(it->second); solAssert(!info.target); info.target = &_term; info.name = _declaration.name(); } termBase(_term).declaration = it->second; return it->second; } TermBase ASTTransform::makeTermBase() { return TermBase{ m_currentLocation, m_currentNode ? make_optional(m_currentNode->id()) : nullopt, std::monostate{}, nullopt }; } unique_ptr ASTTransform::constrain(unique_ptr _value, unique_ptr _constraint) { return application(BuiltinConstant::Constrain, std::move(_value), std::move(_constraint)); } 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); } } 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::pair(unique_ptr _first, unique_ptr _second) { return application( application( BuiltinConstant::Pair, std::move(_first) ), std::move(_second) ); } 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::application(unique_ptr _function, std::list> _arguments) { return makeTerm(std::move(_function), tuple(std::move(_arguments))); }