/*
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)));
}