This commit is contained in:
Daniel Kirchner 2023-06-24 19:12:14 +02:00
parent 955ada39e5
commit 423972883c
13 changed files with 309 additions and 252 deletions

View File

@ -82,6 +82,8 @@ set(sources
ast/experimental/Type.h
ast/experimental/TypeSystem.cpp
ast/experimental/TypeSystem.h
ast/experimental/TypeSystemHelper.cpp
ast/experimental/TypeSystemHelper.h
codegen/ABIFunctions.cpp
codegen/ABIFunctions.h
codegen/ArrayUtils.cpp

View File

@ -20,6 +20,7 @@
#include <libsolidity/analysis/experimental/Analysis.h>
#include <libsolidity/analysis/experimental/TypeInference.h>
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <liblangutil/Exceptions.h>
@ -41,7 +42,7 @@ bool DebugWarner::visitNode(ASTNode const& _node)
auto const& typeInferenceAnnotation = m_analysis.annotation<TypeInference>(_node);
if (typeInferenceAnnotation.type)
{
Type type = m_analysis.typeSystem().env().resolveRecursive(*typeInferenceAnnotation.type);
Type type = *typeInferenceAnnotation.type;
m_errorReporter.info(
0000_error,
_node.location(),

View File

@ -19,6 +19,7 @@
#include <libsolidity/analysis/experimental/TypeInference.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <liblangutil/Exceptions.h>
#include <libyul/AsmAnalysis.h>

View File

@ -29,69 +29,6 @@ using namespace std;
using namespace solidity;
using namespace solidity::frontend::experimental;
std::string frontend::experimental::canonicalTypeName(Type _type)
{
return std::visit(util::GenericVisitor{
[&](TypeConstant const& _type) {
std::stringstream stream;
auto printTypeArguments = [&]() {
if (!_type.arguments.empty())
{
stream << "$";
for (auto type: _type.arguments | ranges::views::drop_last(1))
stream << canonicalTypeName(type) << "$";
stream << canonicalTypeName(_type.arguments.back());
stream << "$";
}
};
std::visit(util::GenericVisitor{
[&](Declaration const* _declaration) {
printTypeArguments();
if (auto const* typeDeclarationAnnotation = dynamic_cast<TypeDeclarationAnnotation const*>(&_declaration->annotation()))
stream << *typeDeclarationAnnotation->canonicalName;
else
// TODO: canonical name
stream << _declaration->name();
},
[&](BuiltinType _builtinType) {
printTypeArguments();
switch(_builtinType)
{
case BuiltinType::Type:
stream << "type";
break;
case BuiltinType::Sort:
stream << "sort";
break;
case BuiltinType::Void:
stream << "void";
break;
case BuiltinType::Function:
stream << "fun";
break;
case BuiltinType::Unit:
stream << "unit";
break;
case BuiltinType::Pair:
stream << "pair";
break;
case BuiltinType::Word:
stream << "word";
break;
case BuiltinType::Integer:
stream << "integer";
break;
}
}
}, _type.constructor);
return stream.str();
},
[](TypeVariable const&)-> string {
solAssert(false);
},
}, _type);
}
bool TypeClass::operator<(TypeClass const& _rhs) const
{
return std::visit(util::GenericVisitor{

View File

@ -38,8 +38,6 @@ struct TypeVariable;
using Type = std::variant<TypeConstant, TypeVariable>;
std::string canonicalTypeName(Type _type);
enum class BuiltinType
{
Type,

View File

@ -18,6 +18,7 @@
#include <libsolidity/ast/experimental/TypeSystem.h>
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <libsolidity/ast/AST.h>
#include <liblangutil/Exceptions.h>
@ -35,6 +36,70 @@ using namespace std;
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
std::string TypeEnvironment::canonicalTypeName(Type _type) const
{
return visit(util::GenericVisitor{
[&](TypeConstant const& _type) {
std::stringstream stream;
auto printTypeArguments = [&]() {
if (!_type.arguments.empty())
{
stream << "$";
for (auto type: _type.arguments | ranges::views::drop_last(1))
stream << canonicalTypeName(type) << "$";
stream << canonicalTypeName(_type.arguments.back());
stream << "$";
}
};
std::visit(util::GenericVisitor{
[&](Declaration const* _declaration) {
printTypeArguments();
if (auto const* typeDeclarationAnnotation = dynamic_cast<TypeDeclarationAnnotation const*>(&_declaration->annotation()))
stream << *typeDeclarationAnnotation->canonicalName;
else
// TODO: canonical name
stream << _declaration->name();
},
[&](BuiltinType _builtinType) {
printTypeArguments();
switch(_builtinType)
{
case BuiltinType::Type:
stream << "type";
break;
case BuiltinType::Sort:
stream << "sort";
break;
case BuiltinType::Void:
stream << "void";
break;
case BuiltinType::Function:
stream << "fun";
break;
case BuiltinType::Unit:
stream << "unit";
break;
case BuiltinType::Pair:
stream << "pair";
break;
case BuiltinType::Word:
stream << "word";
break;
case BuiltinType::Integer:
stream << "integer";
break;
}
}
}, _type.constructor);
return stream.str();
},
[](TypeVariable const&)-> string {
solAssert(false);
},
}, resolve(_type));
}
std::string TypeEnvironment::typeToString(Type const& _type) const
{
return std::visit(util::GenericVisitor{
@ -350,159 +415,3 @@ void TypeSystem::instantiateClass(TypeConstructor _typeConstructor, Arity _arity
solAssert(_arity.argumentSorts.size() == typeConstructorInfo.arguments(), "Invalid arity.");
typeConstructorInfo.arities.emplace_back(_arity);
}
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
{
if (_elements.empty())
return typeSystem.type(BuiltinType::Unit, {});
if (_elements.size() == 1)
return _elements.front();
Type result = _elements.back();
for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1))
result = typeSystem.type(BuiltinType::Pair, {type, result});
return result;
}
vector<experimental::Type> TypeSystemHelpers::destTupleType(Type _tupleType) const
{
if (!isTypeConstant(_tupleType))
return {_tupleType};
auto [constructor, arguments] = destTypeConstant(_tupleType);
if (auto const* builtinType = get_if<BuiltinType>(&constructor))
{
if (*builtinType == BuiltinType::Unit)
return {};
else if (*builtinType != BuiltinType::Pair)
return {_tupleType};
}
else
return {_tupleType};
solAssert(arguments.size() == 2);
vector<Type> result;
result.emplace_back(arguments.front());
Type tail = arguments.back();
while(true)
{
if (!isTypeConstant(tail))
break;
auto [tailConstructor, tailArguments] = destTypeConstant(tail);
auto const* builtinType = get_if<BuiltinType>(&tailConstructor);
if(!builtinType || *builtinType != BuiltinType::Pair)
break;
solAssert(tailArguments.size() == 2);
result.emplace_back(tailArguments.front());
tail = tailArguments.back();
}
result.emplace_back(tail);
return result;
}
experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const
{
return typeSystem.type(BuiltinType::Function, {_argType, _resultType});
}
tuple<TypeConstructor, vector<experimental::Type>> TypeSystemHelpers::destTypeConstant(Type _type) const
{
using ResultType = tuple<TypeConstructor, vector<Type>>;
return std::visit(util::GenericVisitor{
[&](TypeConstant const& _type) -> ResultType {
return std::make_tuple(_type.constructor, _type.arguments);
},
[](auto const&) -> ResultType {
solAssert(false);
}
}, _type);
}
bool TypeSystemHelpers::isTypeConstant(Type _type) const
{
return std::visit(util::GenericVisitor{
[&](TypeConstant const&) -> bool {
return true;
},
[](auto const&) -> bool {
return false;
}
}, _type);
}
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
{
auto [constructor, arguments] = destTypeConstant(_functionType);
auto const* builtinType = get_if<BuiltinType>(&constructor);
solAssert(builtinType && *builtinType == BuiltinType::Function);
solAssert(arguments.size() == 2);
return make_tuple(arguments.front(), arguments.back());
}
bool TypeSystemHelpers::isFunctionType(Type _type) const
{
if (!isTypeConstant(_type))
return false;
auto constructor = get<0>(destTypeConstant(_type));
auto const* builtinType = get_if<BuiltinType>(&constructor);
return builtinType && *builtinType == BuiltinType::Function;
}
vector<experimental::Type> TypeSystemHelpers::typeVars(Type _type) const
{
vector<Type> typeVars;
auto typeVarsImpl = [&](Type _type, auto _recurse) -> void {
std::visit(util::GenericVisitor{
[&](TypeConstant const& _type) {
for (auto arg: _type.arguments)
_recurse(arg, _recurse);
},
[&](TypeVariable const& _var) {
typeVars.emplace_back(_var);
},
// TODO: move to env helpers?
}, typeSystem.env().resolve(_type));
};
typeVarsImpl(_type, typeVarsImpl);
return typeVars;
}
experimental::Type TypeSystemHelpers::kindType(Type _type) const
{
return typeSystem.type(BuiltinType::Type, {_type});
}
experimental::Type TypeSystemHelpers::destKindType(Type _type) const
{
auto [constructor, arguments] = destTypeConstant(_type);
solAssert(constructor == TypeConstructor{BuiltinType::Type});
solAssert(arguments.size() == 1);
return arguments.front();
}
bool TypeSystemHelpers::isKindType(Type _type) const
{
if (!isTypeConstant(_type))
return false;
auto constructor = get<0>(destTypeConstant(_type));
return constructor == TypeConstructor{BuiltinType::Type};
}
std::string TypeSystemHelpers::sortToString(Sort _sort) const
{
switch (_sort.classes.size())
{
case 0:
return "()";
case 1:
return _sort.classes.begin()->toString();
default:
{
std::stringstream stream;
stream << "(";
for (auto typeClass: _sort.classes | ranges::views::drop_last(1))
stream << typeClass.toString() << ", ";
stream << _sort.classes.rbegin()->toString() << ")";
return stream.str();
}
}
}

View File

@ -25,12 +25,6 @@
#include <variant>
#include <vector>
namespace solidity::frontend
{
class Declaration;
class TypeClassDefinition;
}
namespace solidity::frontend::experimental
{
@ -48,6 +42,7 @@ public:
struct SortMismatch { Type type; Sort sort; };
using UnificationFailure = std::variant<TypeMismatch, SortMismatch>;
[[nodiscard]] std::vector<UnificationFailure> unify(Type _a, Type _b);
std::string canonicalTypeName(Type _type) const;
std::string typeToString(Type const& _type) const;
Sort sort(Type _type) const;
private:
@ -107,21 +102,4 @@ private:
TypeEnvironment m_globalTypeEnvironment{*this};
};
struct TypeSystemHelpers
{
TypeSystem const& typeSystem;
std::tuple<TypeConstructor, std::vector<Type>> destTypeConstant(Type _type) const;
bool isTypeConstant(Type _type) const;
Type tupleType(std::vector<Type> _elements) const;
std::vector<Type> destTupleType(Type _tupleType) const;
Type functionType(Type _argType, Type _resultType) const;
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
bool isFunctionType(Type _type) const;
std::vector<Type> typeVars(Type _type) const;
std::string sortToString(Sort _sort) const;
Type kindType(Type _type) const;
bool isKindType(Type _type) const;
Type destKindType(Type _type) const;
};
}

View File

@ -0,0 +1,187 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <libsolutil/Visitor.h>
#include <range/v3/to_container.hpp>
#include <range/v3/view/drop_exactly.hpp>
#include <range/v3/view/drop_last.hpp>
#include <range/v3/view/reverse.hpp>
using namespace std;
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
{
if (_elements.empty())
return typeSystem.type(BuiltinType::Unit, {});
if (_elements.size() == 1)
return _elements.front();
Type result = _elements.back();
for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1))
result = typeSystem.type(BuiltinType::Pair, {type, result});
return result;
}
vector<experimental::Type> TypeSystemHelpers::destTupleType(Type _tupleType) const
{
if (!isTypeConstant(_tupleType))
return {_tupleType};
auto [constructor, arguments] = destTypeConstant(_tupleType);
if (auto const* builtinType = get_if<BuiltinType>(&constructor))
{
if (*builtinType == BuiltinType::Unit)
return {};
else if (*builtinType != BuiltinType::Pair)
return {_tupleType};
}
else
return {_tupleType};
solAssert(arguments.size() == 2);
vector<Type> result;
result.emplace_back(arguments.front());
Type tail = arguments.back();
while(true)
{
if (!isTypeConstant(tail))
break;
auto [tailConstructor, tailArguments] = destTypeConstant(tail);
auto const* builtinType = get_if<BuiltinType>(&tailConstructor);
if(!builtinType || *builtinType != BuiltinType::Pair)
break;
solAssert(tailArguments.size() == 2);
result.emplace_back(tailArguments.front());
tail = tailArguments.back();
}
result.emplace_back(tail);
return result;
}
experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const
{
return typeSystem.type(BuiltinType::Function, {_argType, _resultType});
}
tuple<TypeConstructor, vector<experimental::Type>> TypeSystemHelpers::destTypeConstant(Type _type) const
{
using ResultType = tuple<TypeConstructor, vector<Type>>;
return std::visit(util::GenericVisitor{
[&](TypeConstant const& _type) -> ResultType {
return std::make_tuple(_type.constructor, _type.arguments);
},
[](auto const&) -> ResultType {
solAssert(false);
}
}, _type);
}
bool TypeSystemHelpers::isTypeConstant(Type _type) const
{
return std::visit(util::GenericVisitor{
[&](TypeConstant const&) -> bool {
return true;
},
[](auto const&) -> bool {
return false;
}
}, _type);
}
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
{
auto [constructor, arguments] = destTypeConstant(_functionType);
auto const* builtinType = get_if<BuiltinType>(&constructor);
solAssert(builtinType && *builtinType == BuiltinType::Function);
solAssert(arguments.size() == 2);
return make_tuple(arguments.front(), arguments.back());
}
bool TypeSystemHelpers::isFunctionType(Type _type) const
{
if (!isTypeConstant(_type))
return false;
auto constructor = get<0>(destTypeConstant(_type));
auto const* builtinType = get_if<BuiltinType>(&constructor);
return builtinType && *builtinType == BuiltinType::Function;
}
vector<experimental::Type> TypeSystemHelpers::typeVars(Type _type) const
{
vector<Type> typeVars;
auto typeVarsImpl = [&](Type _type, auto _recurse) -> void {
std::visit(util::GenericVisitor{
[&](TypeConstant const& _type) {
for (auto arg: _type.arguments)
_recurse(arg, _recurse);
},
[&](TypeVariable const& _var) {
typeVars.emplace_back(_var);
},
// TODO: move to env helpers?
}, typeSystem.env().resolve(_type));
};
typeVarsImpl(_type, typeVarsImpl);
return typeVars;
}
experimental::Type TypeSystemHelpers::kindType(Type _type) const
{
return typeSystem.type(BuiltinType::Type, {_type});
}
experimental::Type TypeSystemHelpers::destKindType(Type _type) const
{
auto [constructor, arguments] = destTypeConstant(_type);
solAssert(constructor == TypeConstructor{BuiltinType::Type});
solAssert(arguments.size() == 1);
return arguments.front();
}
bool TypeSystemHelpers::isKindType(Type _type) const
{
if (!isTypeConstant(_type))
return false;
auto constructor = get<0>(destTypeConstant(_type));
return constructor == TypeConstructor{BuiltinType::Type};
}
std::string TypeSystemHelpers::sortToString(Sort _sort) const
{
switch (_sort.classes.size())
{
case 0:
return "()";
case 1:
return _sort.classes.begin()->toString();
default:
{
std::stringstream stream;
stream << "(";
for (auto typeClass: _sort.classes | ranges::views::drop_last(1))
stream << typeClass.toString() << ", ";
stream << _sort.classes.rbegin()->toString() << ")";
return stream.str();
}
}
}

View File

@ -0,0 +1,42 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libsolidity/ast/experimental/TypeSystem.h>
namespace solidity::frontend::experimental
{
struct TypeSystemHelpers
{
TypeSystem const& typeSystem;
std::tuple<TypeConstructor, std::vector<Type>> destTypeConstant(Type _type) const;
bool isTypeConstant(Type _type) const;
Type tupleType(std::vector<Type> _elements) const;
std::vector<Type> destTupleType(Type _tupleType) const;
Type functionType(Type _argType, Type _resultType) const;
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
bool isFunctionType(Type _type) const;
std::vector<Type> typeVars(Type _type) const;
std::string sortToString(Sort _sort) const;
Type kindType(Type _type) const;
bool isKindType(Type _type) const;
Type destKindType(Type _type) const;
};
}

View File

@ -32,12 +32,12 @@ using namespace solidity::yul;
namespace solidity::frontend::experimental
{
string IRNames::function(FunctionDefinition const& _function, Type _type)
string IRNames::function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type)
{
if (_function.isConstructor())
return constructor(*_function.annotation().contract);
return "fun_" + _function.name() + "_" + to_string(_function.id()) + "$" + canonicalTypeName(_type) + "$";
return "fun_" + _function.name() + "_" + to_string(_function.id()) + "$" + _env.canonicalTypeName(_type) + "$";
}
string IRNames::function(VariableDeclaration const& _varDecl)

View File

@ -29,7 +29,7 @@ namespace solidity::frontend::experimental
struct IRNames
{
static std::string function(FunctionDefinition const& _function, Type _type);
static std::string function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type);
static std::string function(VariableDeclaration const& _varDecl);
static std::string creationObject(ContractDefinition const& _contract);
static std::string deployedObject(ContractDefinition const& _contract);

View File

@ -99,7 +99,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
auto type = m_context.analysis.annotation<TypeInference>(*_contract.fallbackFunction()).type;
solAssert(type);
type = m_context.env->resolve(*type);
code << IRNames::function(*_contract.fallbackFunction(), *type) << "()\n";
code << IRNames::function(*m_context.env, *_contract.fallbackFunction(), *type) << "()\n";
m_context.enqueueFunctionDefinition(_contract.fallbackFunction(), *type);
}
code << "revert(0,0)\n";
@ -130,7 +130,7 @@ string IRGenerator::generate(FunctionDefinition const& _function, Type _type)
solAssert(false, newEnv.typeToString(*type) + " <-> " + newEnv.typeToString(_type));
}
std::stringstream code;
code << "function " << IRNames::function(_function, _type) << "(";
code << "function " << IRNames::function(newEnv, _function, _type) << "(";
if (_function.parameters().size() > 1)
for (auto const& arg: _function.parameters() | ranges::views::drop_last(1))
code << IRNames::localVariable(*arg) << ", ";

View File

@ -22,6 +22,8 @@
#include <libsolidity/analysis/experimental/TypeInference.h>
#include <libsolidity/analysis/experimental/TypeRegistration.h>
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <libyul/YulStack.h>
#include <libyul/AsmPrinter.h>
#include <libyul/AST.h>
@ -173,7 +175,7 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
solAssert(expressionAnnotation.type);
auto typeConstructor = std::get<0>(TypeSystemHelpers{m_context.analysis.typeSystem()}.destTypeConstant(
m_context.analysis.typeSystem().env().resolve(*expressionAnnotation.type)
m_context.env->resolve(*expressionAnnotation.type)
));
auto const* typeClass = dynamic_cast<Identifier const*>(&memberAccess->expression());
solAssert(typeClass, "Function call to member access only supported for type classes.");
@ -201,7 +203,7 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
// TODO: get around resolveRecursive by passing the environment further down?
functionType = m_context.env->resolveRecursive(*functionType);
m_context.enqueueFunctionDefinition(functionDefinition, *functionType);
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition, *functionType) << "(";
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*m_context.env, *functionDefinition, *functionType) << "(";
auto const& arguments = _functionCall.arguments();
if (arguments.size() > 1)
for (auto arg: arguments | ranges::views::drop_last(1))