mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
tmp
This commit is contained in:
parent
315270f3bb
commit
7d094b3be6
@ -48,6 +48,8 @@ set(sources
|
||||
analysis/ViewPureChecker.h
|
||||
analysis/experimental/Analysis.cpp
|
||||
analysis/experimental/Analysis.h
|
||||
analysis/experimental/DebugWarner.cpp
|
||||
analysis/experimental/DebugWarner.h
|
||||
analysis/experimental/TypeInference.cpp
|
||||
analysis/experimental/TypeInference.h
|
||||
analysis/experimental/TypeRegistration.cpp
|
||||
@ -100,6 +102,8 @@ set(sources
|
||||
codegen/ReturnInfo.cpp
|
||||
codegen/YulUtilFunctions.h
|
||||
codegen/YulUtilFunctions.cpp
|
||||
codegen/experimental/Common.h
|
||||
codegen/experimental/Common.cpp
|
||||
codegen/experimental/IRGenerationContext.h
|
||||
codegen/experimental/IRGenerator.cpp
|
||||
codegen/experimental/IRGenerator.h
|
||||
|
@ -17,6 +17,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
|
||||
#include <libsolidity/analysis/experimental/DebugWarner.h>
|
||||
#include <libsolidity/analysis/experimental/SyntaxRestrictor.h>
|
||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||
#include <libsolidity/analysis/experimental/TypeRegistration.h>
|
||||
@ -38,24 +39,44 @@ TypeRegistration::Annotation& solidity::frontend::experimental::detail::Annotati
|
||||
return analysis.annotationContainer(_node).typeRegistrationAnnotation;
|
||||
}
|
||||
|
||||
template<>
|
||||
TypeRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeRegistration>::get(ASTNode const& _node) const
|
||||
{
|
||||
return analysis.annotationContainer(_node).typeRegistrationAnnotation;
|
||||
}
|
||||
|
||||
template<>
|
||||
TypeInference::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeInference>::get(ASTNode const& _node)
|
||||
{
|
||||
return analysis.annotationContainer(_node).typeInferenceAnnotation;
|
||||
}
|
||||
|
||||
template<>
|
||||
TypeInference::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeInference>::get(ASTNode const& _node) const
|
||||
{
|
||||
return analysis.annotationContainer(_node).typeInferenceAnnotation;
|
||||
}
|
||||
|
||||
Analysis::AnnotationContainer& Analysis::annotationContainer(ASTNode const& _node)
|
||||
{
|
||||
solAssert(_node.id() > 0);
|
||||
size_t id = static_cast<size_t>(_node.id());
|
||||
solAssert(id < m_maxAstId);
|
||||
solAssert(id <= m_maxAstId);
|
||||
return m_annotations[id];
|
||||
}
|
||||
|
||||
Analysis::AnnotationContainer const& Analysis::annotationContainer(ASTNode const& _node) const
|
||||
{
|
||||
solAssert(_node.id() > 0);
|
||||
size_t id = static_cast<size_t>(_node.id());
|
||||
solAssert(id <= m_maxAstId);
|
||||
return m_annotations[id];
|
||||
}
|
||||
|
||||
Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId):
|
||||
m_errorReporter(_errorReporter),
|
||||
m_maxAstId(_maxAstId),
|
||||
m_annotations(std::make_unique<AnnotationContainer[]>(static_cast<size_t>(_maxAstId)))
|
||||
m_annotations(std::make_unique<AnnotationContainer[]>(static_cast<size_t>(_maxAstId + 1)))
|
||||
{
|
||||
}
|
||||
|
||||
@ -69,7 +90,7 @@ std::tuple<std::integral_constant<size_t, Is>...> makeIndexTuple(std::index_sequ
|
||||
|
||||
bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits)
|
||||
{
|
||||
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeRegistration, TypeInference>;
|
||||
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeRegistration, TypeInference, DebugWarner>;
|
||||
|
||||
return std::apply([&](auto... _indexTuple) {
|
||||
return ([&](auto&& _step) {
|
||||
|
@ -48,6 +48,12 @@ struct AnnotationFetcher
|
||||
Analysis& analysis;
|
||||
typename Step::Annotation& get(ASTNode const& _node);
|
||||
};
|
||||
template<typename Step>
|
||||
struct ConstAnnotationFetcher
|
||||
{
|
||||
Analysis const& analysis;
|
||||
typename Step::Annotation const& get(ASTNode const& _node) const;
|
||||
};
|
||||
}
|
||||
|
||||
class Analysis
|
||||
@ -62,12 +68,19 @@ public:
|
||||
langutil::ErrorReporter& errorReporter() { return m_errorReporter; }
|
||||
uint64_t maxAstId() const { return m_maxAstId; }
|
||||
TypeSystem& typeSystem() { return m_typeSystem; }
|
||||
TypeSystem const& typeSystem() const { return m_typeSystem; }
|
||||
template<typename Step>
|
||||
typename Step::Annotation& annotation(ASTNode const& _node)
|
||||
{
|
||||
return detail::AnnotationFetcher<Step>{*this}.get(_node);
|
||||
}
|
||||
template<typename Step>
|
||||
typename Step::Annotation const& annotation(ASTNode const& _node) const
|
||||
{
|
||||
return detail::ConstAnnotationFetcher<Step>{*this}.get(_node);
|
||||
}
|
||||
AnnotationContainer& annotationContainer(ASTNode const& _node);
|
||||
AnnotationContainer const& annotationContainer(ASTNode const& _node) const;
|
||||
private:
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
TypeSystem m_typeSystem;
|
||||
|
47
libsolidity/analysis/experimental/DebugWarner.cpp
Normal file
47
libsolidity/analysis/experimental/DebugWarner.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
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/analysis/experimental/DebugWarner.h>
|
||||
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::experimental;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
DebugWarner::DebugWarner(Analysis& _analysis): m_analysis(_analysis), m_errorReporter(_analysis.errorReporter())
|
||||
{}
|
||||
|
||||
bool DebugWarner::analyze(ASTNode const& _astRoot)
|
||||
{
|
||||
_astRoot.accept(*this);
|
||||
return !Error::containsErrors(m_errorReporter.errors());
|
||||
}
|
||||
|
||||
bool DebugWarner::visitNode(ASTNode const& _node)
|
||||
{
|
||||
auto const& typeInferenceAnnotation = m_analysis.annotation<TypeInference>(_node);
|
||||
if (typeInferenceAnnotation.type)
|
||||
{
|
||||
m_errorReporter.info(0000_error, _node.location(), "Inferred type: " + m_analysis.typeSystem().env().typeToString(*typeInferenceAnnotation.type));
|
||||
}
|
||||
return true;
|
||||
}
|
42
libsolidity/analysis/experimental/DebugWarner.h
Normal file
42
libsolidity/analysis/experimental/DebugWarner.h
Normal 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/ASTVisitor.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
class Analysis;
|
||||
|
||||
class DebugWarner: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
DebugWarner(Analysis& _analysis);
|
||||
|
||||
bool analyze(ASTNode const& _astRoot);
|
||||
|
||||
private:
|
||||
bool visitNode(ASTNode const& _node) override;
|
||||
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
};
|
||||
|
||||
}
|
@ -48,11 +48,13 @@ private:
|
||||
bool visit(Block const&) override { return true; }
|
||||
bool visit(InlineAssembly const&) override { return true; }
|
||||
bool visit(Identifier const&) override { return true; }
|
||||
bool visit(IdentifierPath const&) override { return true; }
|
||||
bool visit(VariableDeclarationStatement const&) override;
|
||||
bool visit(VariableDeclaration const&) override;
|
||||
bool visit(ElementaryTypeName const&) override { return true; }
|
||||
bool visit(ParameterList const&) override { return true; }
|
||||
bool visit(Return const&) override { return true; }
|
||||
bool visit(MemberAccess const&) override { return true; }
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
};
|
||||
|
@ -40,6 +40,7 @@ m_typeSystem(_analysis.typeSystem())
|
||||
m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {});
|
||||
m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {});
|
||||
m_integerType = m_typeSystem.builtinType(BuiltinType::Integer, {});
|
||||
m_env = &m_typeSystem.env();
|
||||
}
|
||||
|
||||
bool TypeInference::analyze(SourceUnit const& _sourceUnit)
|
||||
@ -79,12 +80,9 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
|
||||
if (_functionDefinition.isImplemented())
|
||||
_functionDefinition.body().accept(*this);
|
||||
|
||||
functionAnnotation.type = m_typeSystem.fresh(
|
||||
functionType,
|
||||
true
|
||||
);
|
||||
functionAnnotation.type = functionType;
|
||||
|
||||
m_errorReporter.warning(0000_error, _functionDefinition.location(), m_typeSystem.typeToString(*functionAnnotation.type));
|
||||
m_errorReporter.info(0000_error, _functionDefinition.location(), m_env->typeToString(*functionAnnotation.type));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -122,7 +120,22 @@ void TypeInference::endVisit(ParameterList const& _parameterList)
|
||||
|
||||
bool TypeInference::visitNode(ASTNode const& _node)
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference.");
|
||||
m_errorReporter.fatalTypeError(0000_error, _node.location(), "Unsupported AST node during type inference.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
|
||||
{
|
||||
auto& typeVariableAnnotation = annotation(_typeClassDefinition.typeVariable());
|
||||
if (typeVariableAnnotation.type)
|
||||
return false;
|
||||
_typeClassDefinition.typeVariable().accept(*this);
|
||||
|
||||
for (auto const& subNode: _typeClassDefinition.subNodes())
|
||||
subNode->accept(*this);
|
||||
|
||||
solAssert(typeVariableAnnotation.type);
|
||||
unify(*typeVariableAnnotation.type, m_typeSystem.freshTypeVariable(false, Sort{{TypeClass{&_typeClassDefinition}}}));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -151,8 +164,10 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
||||
{
|
||||
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(variableDeclaration->scope()))
|
||||
{
|
||||
(void)typeClass;
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Using type class type variables not yet implemented.");
|
||||
typeClass->accept(*this);
|
||||
auto varType = annotation(typeClass->typeVariable()).type;
|
||||
solAssert(varType);
|
||||
return *varType;
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Type name referencing a variable declaration.");
|
||||
@ -162,12 +177,12 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
|
||||
return m_typeSystem.freshTypeVariable(false);
|
||||
return m_typeSystem.freshTypeVariable(false, {});
|
||||
}
|
||||
void TypeInference::unify(Type _a, Type _b)
|
||||
{
|
||||
for (auto failure: m_typeSystem.unify(_a, _b))
|
||||
m_errorReporter.typeError(0000_error, {}, fmt::format("Cannot unify {} and {}.", m_typeSystem.typeToString(_a), m_typeSystem.typeToString(_b)));
|
||||
for (auto failure: m_env->unify(_a, _b))
|
||||
m_errorReporter.typeError(0000_error, {}, fmt::format("Cannot unify {} and {}.", m_env->typeToString(_a), m_env->typeToString(_b)));
|
||||
|
||||
}
|
||||
bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
||||
@ -218,12 +233,25 @@ bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
|
||||
solAssert(!_variableDeclaration.value());
|
||||
auto& variableAnnotation = annotation(_variableDeclaration);
|
||||
solAssert(!variableAnnotation.type);
|
||||
|
||||
Sort sort;
|
||||
for (auto const& typeClass: _variableDeclaration.sort())
|
||||
{
|
||||
auto const* declaration = dynamic_cast<TypeClassDefinition const*>(typeClass->annotation().referencedDeclaration);
|
||||
if (!declaration)
|
||||
m_errorReporter.typeError(0000_error, typeClass->location(), "Expected type class.");
|
||||
sort.classes.emplace(TypeClass{declaration});
|
||||
}
|
||||
|
||||
variableAnnotation.type = [&] {
|
||||
if (_variableDeclaration.hasTypeName())
|
||||
return fromTypeName(_variableDeclaration.typeName());
|
||||
else
|
||||
return m_typeSystem.freshTypeVariable(false);
|
||||
return m_typeSystem.freshTypeVariable(false, sort);
|
||||
}();
|
||||
|
||||
// TODO: validate sort.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -242,7 +270,7 @@ void TypeInference::endVisit(Assignment const& _assignment)
|
||||
auto& rhsAnnotation = annotation(_assignment.rightHandSide());
|
||||
solAssert(rhsAnnotation.type);
|
||||
unify(*lhsAnnotation.type, *rhsAnnotation.type);
|
||||
assignmentAnnotation.type = m_typeSystem.resolve(*lhsAnnotation.type);
|
||||
assignmentAnnotation.type = m_env->resolve(*lhsAnnotation.type);
|
||||
}
|
||||
|
||||
TypeInference::Annotation& TypeInference::annotation(ASTNode const& _node)
|
||||
@ -269,7 +297,13 @@ bool TypeInference::visit(Identifier const& _identifier)
|
||||
referencedDeclaration->accept(*this);
|
||||
|
||||
solAssert(declarationAnnotation.type);
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
|
||||
if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false);
|
||||
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
else
|
||||
solAssert(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -282,6 +316,7 @@ bool TypeInference::visit(IdentifierPath const& _identifier)
|
||||
|
||||
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
|
||||
solAssert(referencedDeclaration);
|
||||
|
||||
if (
|
||||
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) &&
|
||||
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration)
|
||||
@ -293,15 +328,97 @@ bool TypeInference::visit(IdentifierPath const& _identifier)
|
||||
referencedDeclaration->accept(*this);
|
||||
|
||||
solAssert(declarationAnnotation.type);
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
|
||||
if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, false);
|
||||
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
|
||||
identifierAnnotation.type = declarationAnnotation.type;
|
||||
else
|
||||
solAssert(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
|
||||
{
|
||||
auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_typeClassInstantiation.typeClass().annotation().referencedDeclaration);
|
||||
if (!typeClass)
|
||||
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class.");
|
||||
typeClass->accept(*this);
|
||||
|
||||
map<string, Type> functionTypes;
|
||||
|
||||
for (auto subNode: typeClass->subNodes())
|
||||
{
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get());
|
||||
solAssert(functionDefinition);
|
||||
auto functionType = annotation(*functionDefinition).type;
|
||||
solAssert(functionType);
|
||||
functionTypes[functionDefinition->name()] = *functionType;
|
||||
}
|
||||
for (auto subNode: _typeClassInstantiation.subNodes())
|
||||
subNode->accept(*this);
|
||||
{
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get());
|
||||
solAssert(functionDefinition);
|
||||
Type* expectedFunctionType = util::valueOrNullptr(functionTypes, functionDefinition->name());
|
||||
if (!expectedFunctionType)
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, functionDefinition->location(), "Function definition during instantiation that does not belong to the class.");
|
||||
continue;
|
||||
}
|
||||
subNode->accept(*this);
|
||||
}
|
||||
for (auto subNode: _typeClassInstantiation.subNodes())
|
||||
{
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get());
|
||||
if (Type* expectedFunctionType = util::valueOrNullptr(functionTypes, functionDefinition->name()))
|
||||
{
|
||||
auto functionType = annotation(*functionDefinition).type;
|
||||
solAssert(functionType);
|
||||
// TODO: require exact match?
|
||||
unify(*functionType, m_env->fresh(*expectedFunctionType, true));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeInference::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()))
|
||||
{
|
||||
auto const* declaration = identifier->annotation().referencedDeclaration;
|
||||
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(declaration))
|
||||
{
|
||||
for (auto subNode: typeClass->subNodes())
|
||||
{
|
||||
if (auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get()))
|
||||
{
|
||||
if (functionDefinition->name() == _memberAccess.memberName())
|
||||
{
|
||||
auto& declarationAnnotation = annotation(*functionDefinition);
|
||||
if (!declarationAnnotation.type)
|
||||
functionDefinition->accept(*this);
|
||||
solAssert(declarationAnnotation.type);
|
||||
Type type = m_env->fresh(*declarationAnnotation.type, true);
|
||||
annotation(_memberAccess).type = type;
|
||||
auto typeVars = TypeSystemHelpers{m_typeSystem}.typeVars(type);
|
||||
if (typeVars.size() != 1)
|
||||
m_errorReporter.typeError(0000_error, _memberAccess.location(), "Type class reference does not uniquely depend on class type.");
|
||||
annotation(_memberAccess.expression()).type = typeVars.front();
|
||||
m_errorReporter.info(0000_error, _memberAccess.location(), m_env->typeToString(*declarationAnnotation.type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Unknown member of type-class.");
|
||||
}
|
||||
else
|
||||
m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Member access to non-type-class.");
|
||||
}
|
||||
else
|
||||
m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Member access to non-identifier.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -314,7 +431,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
|
||||
auto& expressionAnnotation = annotation(_functionCall.expression());
|
||||
solAssert(expressionAnnotation.type);
|
||||
|
||||
Type functionType = m_typeSystem.fresh(*expressionAnnotation.type, false);
|
||||
Type functionType = *expressionAnnotation.type;
|
||||
|
||||
std::vector<Type> argTypes;
|
||||
for(auto arg: _functionCall.arguments())
|
||||
@ -324,8 +441,8 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
|
||||
argTypes.emplace_back(*argAnnotation.type);
|
||||
}
|
||||
Type argTuple = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||
Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable(false));
|
||||
Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable(false, {}));
|
||||
unify(genericFunctionType, functionType);
|
||||
|
||||
functionCallAnnotation.type = m_typeSystem.resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_typeSystem.resolve(genericFunctionType))));
|
||||
functionCallAnnotation.type = m_env->resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_env->resolve(genericFunctionType))));
|
||||
}
|
||||
|
@ -34,12 +34,11 @@ public:
|
||||
|
||||
bool analyze(SourceUnit const& _sourceUnit);
|
||||
|
||||
|
||||
struct Annotation
|
||||
{
|
||||
/// Expressions, variable declarations, function declarations.
|
||||
std::optional<Type> type;
|
||||
};
|
||||
private:
|
||||
bool visit(Block const&) override { return true; }
|
||||
bool visit(VariableDeclarationStatement const&) override { return true; }
|
||||
bool visit(VariableDeclaration const& _variableDeclaration) override;
|
||||
@ -62,16 +61,19 @@ private:
|
||||
bool visit(Return const&) override { return true; }
|
||||
void endVisit(Return const& _return) override;
|
||||
|
||||
bool visit(MemberAccess const& _memberAccess) override;
|
||||
|
||||
// TODO: properly account for it
|
||||
bool visit(TypeClassDefinition const&) override { return true; }
|
||||
bool visit(TypeClassDefinition const&) override;
|
||||
bool visit(TypeClassInstantiation const&) override;
|
||||
|
||||
bool visitNode(ASTNode const& _node) override;
|
||||
|
||||
private:
|
||||
Type fromTypeName(TypeName const& _typeName);
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
TypeSystem& m_typeSystem;
|
||||
TypeEnvironment* m_env = nullptr;
|
||||
Type m_voidType;
|
||||
Type m_wordType;
|
||||
Type m_integerType;
|
||||
|
@ -61,37 +61,81 @@ bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition)
|
||||
}
|
||||
bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation)
|
||||
{
|
||||
auto const* classDefintion = dynamic_cast<TypeClassDefinition const*>(_typeClassInstantiation.sort().annotation().referencedDeclaration);
|
||||
auto const* classDefintion = dynamic_cast<TypeClassDefinition const*>(_typeClassInstantiation.typeClass().annotation().referencedDeclaration);
|
||||
if (!classDefintion)
|
||||
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.sort().location(), "Expected a type class.");
|
||||
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class.");
|
||||
classDefintion->accept(*this);
|
||||
|
||||
// TypeClass typeClass{classDefintion};
|
||||
TypeClass typeClass{classDefintion};
|
||||
|
||||
auto fromTypeName = [&](TypeName const& _typeName) -> Type {
|
||||
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
|
||||
TypeName const& typeName = _typeClassInstantiation.typeConstructor();
|
||||
|
||||
TypeExpression::Constructor typeConstructor = [&]() -> TypeExpression::Constructor {
|
||||
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&typeName))
|
||||
{
|
||||
switch(elementaryTypeName->typeName().token())
|
||||
{
|
||||
case Token::Word:
|
||||
return m_typeSystem.builtinType(BuiltinType::Word, {});
|
||||
return BuiltinType::Word;
|
||||
case Token::Void:
|
||||
return m_typeSystem.builtinType(BuiltinType::Void, {});
|
||||
return BuiltinType::Void;
|
||||
case Token::Integer:
|
||||
return m_typeSystem.builtinType(BuiltinType::Integer, {});
|
||||
return BuiltinType::Integer;
|
||||
default:
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
||||
break;
|
||||
m_errorReporter.typeError(0000_error, typeName.location(), "Only elementary types are supported.");
|
||||
return BuiltinType::Void;
|
||||
}
|
||||
}
|
||||
else if (auto const* userDefinedType = dynamic_cast<UserDefinedTypeName const*>(&typeName))
|
||||
{
|
||||
if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration)
|
||||
return referencedDeclaration;
|
||||
else
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, userDefinedType->pathNode().location(), "No declaration found for user-defined type name.");
|
||||
return BuiltinType::Void;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
|
||||
return m_typeSystem.freshTypeVariable(false);
|
||||
};
|
||||
auto type = fromTypeName(_typeClassInstantiation.typeConstructor());
|
||||
_typeClassInstantiation.argumentSorts();
|
||||
{
|
||||
m_errorReporter.typeError(0000_error, typeName.location(), "Only elementary types are supported.");
|
||||
return BuiltinType::Void;
|
||||
}
|
||||
}();
|
||||
|
||||
// m_typeSystem.instantiateClass();
|
||||
Arity arity{
|
||||
{},
|
||||
typeClass
|
||||
};
|
||||
if (_typeClassInstantiation.argumentSorts().size() != m_typeSystem.constructorArguments(typeConstructor))
|
||||
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.location(), "Invalid number of arguments.");
|
||||
|
||||
for (auto argumentSort : _typeClassInstantiation.argumentSorts())
|
||||
{
|
||||
if (auto const* referencedDeclaration = argumentSort->annotation().referencedDeclaration)
|
||||
{
|
||||
if (!dynamic_cast<TypeClassDefinition const*>(referencedDeclaration))
|
||||
m_errorReporter.fatalTypeError(0000_error, argumentSort->location(), "Argument sort has to be a type class.");
|
||||
// TODO: multi arities
|
||||
arity._argumentSorts.emplace_back(Sort{{TypeClass{referencedDeclaration}}});
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: error Handling
|
||||
m_errorReporter.fatalTypeError(0000_error, argumentSort->location(), "Invalid sort.");
|
||||
}
|
||||
}
|
||||
m_typeSystem.instantiateClass(typeConstructor, arity);
|
||||
|
||||
if (
|
||||
auto [instantiation, newlyInserted] = annotation(*classDefintion).instantiations.emplace(typeConstructor, &_typeClassInstantiation);
|
||||
!newlyInserted
|
||||
)
|
||||
{
|
||||
SecondarySourceLocation ssl;
|
||||
ssl.append("Previous instantiation.", instantiation->second->location());
|
||||
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public:
|
||||
struct Annotation
|
||||
{
|
||||
Type type;
|
||||
std::map<TypeExpression::Constructor, TypeClassInstantiation const*> instantiations;
|
||||
};
|
||||
TypeRegistration(Analysis& _analysis);
|
||||
|
||||
|
@ -1068,7 +1068,8 @@ public:
|
||||
bool _isIndexed = false,
|
||||
Mutability _mutability = Mutability::Mutable,
|
||||
ASTPointer<OverrideSpecifier> _overrides = nullptr,
|
||||
Location _referenceLocation = Location::Unspecified
|
||||
Location _referenceLocation = Location::Unspecified,
|
||||
std::vector<ASTPointer<IdentifierPath>> _sort = {}
|
||||
):
|
||||
Declaration(_id, _location, _name, std::move(_nameLocation), _visibility),
|
||||
StructurallyDocumented(std::move(_documentation)),
|
||||
@ -1077,10 +1078,11 @@ public:
|
||||
m_isIndexed(_isIndexed),
|
||||
m_mutability(_mutability),
|
||||
m_overrides(std::move(_overrides)),
|
||||
m_location(_referenceLocation)
|
||||
m_location(_referenceLocation),
|
||||
m_sort(std::move(_sort))
|
||||
{
|
||||
// TODO: consider still asserting unless we are in experimental solidity.
|
||||
// solAssert(m_typeName, "");
|
||||
// solAssert(m_typeName, ""); solAssert(m_sorts.empy(), "");
|
||||
}
|
||||
|
||||
|
||||
@ -1142,6 +1144,7 @@ public:
|
||||
/// @returns null when it is not accessible as a function.
|
||||
FunctionTypePointer functionType(bool /*_internal*/) const override;
|
||||
|
||||
std::vector<ASTPointer<IdentifierPath>> const& sort() const { return m_sort; }
|
||||
VariableDeclarationAnnotation& annotation() const override;
|
||||
|
||||
protected:
|
||||
@ -1157,6 +1160,7 @@ private:
|
||||
Mutability m_mutability = Mutability::Mutable;
|
||||
ASTPointer<OverrideSpecifier> m_overrides; ///< Contains the override specifier node
|
||||
Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type.
|
||||
std::vector<ASTPointer<IdentifierPath>> m_sort;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2493,13 +2497,13 @@ public:
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<TypeName> _typeConstructor,
|
||||
std::vector<ASTPointer<IdentifierPath>> const& _argumentSorts,
|
||||
ASTPointer<IdentifierPath> _sort,
|
||||
ASTPointer<IdentifierPath> _class,
|
||||
std::vector<ASTPointer<ASTNode>> _subNodes
|
||||
):
|
||||
ASTNode(_id, _location),
|
||||
m_typeConstructor(std::move(_typeConstructor)),
|
||||
m_argumentSorts(std::move(_argumentSorts)),
|
||||
m_sort(std::move(_sort)),
|
||||
m_class(std::move(_class)),
|
||||
m_subNodes(std::move(_subNodes))
|
||||
{}
|
||||
|
||||
@ -2508,7 +2512,7 @@ public:
|
||||
|
||||
TypeName const& typeConstructor() const { return *m_typeConstructor; }
|
||||
std::vector<ASTPointer<IdentifierPath>> const& argumentSorts() const { return m_argumentSorts; }
|
||||
IdentifierPath const& sort() const { return *m_sort; }
|
||||
IdentifierPath const& typeClass() const { return *m_class; }
|
||||
std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
|
||||
|
||||
bool experimentalSolidityOnly() const override { return true; }
|
||||
@ -2516,7 +2520,7 @@ public:
|
||||
private:
|
||||
ASTPointer<TypeName> m_typeConstructor;
|
||||
std::vector<ASTPointer<IdentifierPath>> m_argumentSorts;
|
||||
ASTPointer<IdentifierPath> m_sort;
|
||||
ASTPointer<IdentifierPath> m_class;
|
||||
std::vector<ASTPointer<ASTNode>> m_subNodes;
|
||||
};
|
||||
|
||||
|
@ -296,6 +296,7 @@ void VariableDeclaration::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
listAccept(m_sort, _visitor);
|
||||
if (m_overrides)
|
||||
m_overrides->accept(_visitor);
|
||||
if (m_value)
|
||||
@ -310,6 +311,7 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
listAccept(m_sort, _visitor);
|
||||
if (m_overrides)
|
||||
m_overrides->accept(_visitor);
|
||||
if (m_value)
|
||||
@ -1056,7 +1058,7 @@ void TypeClassInstantiation::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
m_typeConstructor->accept(_visitor);
|
||||
listAccept(m_argumentSorts, _visitor);
|
||||
m_sort->accept(_visitor);
|
||||
m_class->accept(_visitor);
|
||||
listAccept(m_subNodes, _visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
@ -1068,7 +1070,7 @@ void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
m_typeConstructor->accept(_visitor);
|
||||
listAccept(m_argumentSorts, _visitor);
|
||||
m_sort->accept(_visitor);
|
||||
m_class->accept(_visitor);
|
||||
listAccept(m_subNodes, _visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
|
@ -35,7 +35,108 @@ using namespace std;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::frontend::experimental;
|
||||
|
||||
std::string TypeSystem::typeToString(Type const& _type) const
|
||||
bool TypeClass::operator<(TypeClass const& _rhs) const
|
||||
{
|
||||
return declaration->id() < _rhs.declaration->id();
|
||||
}
|
||||
|
||||
bool Sort::operator==(Sort const& _rhs) const
|
||||
{
|
||||
if (classes.size() != _rhs.classes.size())
|
||||
return false;
|
||||
for (auto [lhs, rhs]: ranges::zip_view(classes, _rhs.classes))
|
||||
if (lhs != rhs)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sort::operator<(Sort const& _rhs) const
|
||||
{
|
||||
for (auto c: classes)
|
||||
if (!_rhs.classes.count(c))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Sort Sort::operator+(Sort const& _rhs) const
|
||||
{
|
||||
Sort result { classes };
|
||||
result.classes += _rhs.classes;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TypeExpression::operator<(TypeExpression const& _rhs) const
|
||||
{
|
||||
if (constructor < _rhs.constructor)
|
||||
return true;
|
||||
if (_rhs.constructor < constructor)
|
||||
return false;
|
||||
solAssert(arguments.size() == _rhs.arguments.size());
|
||||
for(auto [lhs, rhs]: ranges::zip_view(arguments, _rhs.arguments))
|
||||
{
|
||||
if (lhs < rhs)
|
||||
return true;
|
||||
if (rhs < lhs)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string experimental::canonicalTypeName(Type _type)
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](TypeExpression 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();
|
||||
// TODO: canonical name
|
||||
stream << _declaration->name();
|
||||
},
|
||||
[&](BuiltinType _builtinType) {
|
||||
printTypeArguments();
|
||||
switch(_builtinType)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
std::string TypeEnvironment::typeToString(Type const& _type) const
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](TypeExpression const& _type) {
|
||||
@ -53,7 +154,7 @@ std::string TypeSystem::typeToString(Type const& _type) const
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](Declaration const* _declaration) {
|
||||
printTypeArguments();
|
||||
stream << _declaration->name();
|
||||
stream << m_typeSystem.typeName(_declaration);
|
||||
},
|
||||
[&](BuiltinType _builtinType) {
|
||||
switch (_builtinType)
|
||||
@ -68,7 +169,7 @@ std::string TypeSystem::typeToString(Type const& _type) const
|
||||
break;
|
||||
case BuiltinType::Pair:
|
||||
{
|
||||
auto tupleTypes = TypeSystemHelpers{*this}.destTupleType(_type);
|
||||
auto tupleTypes = TypeSystemHelpers{m_typeSystem}.destTupleType(_type);
|
||||
stream << "(";
|
||||
for (auto type: tupleTypes | ranges::views::drop_last(1))
|
||||
stream << typeToString(type) << ", ";
|
||||
@ -77,7 +178,7 @@ std::string TypeSystem::typeToString(Type const& _type) const
|
||||
}
|
||||
default:
|
||||
printTypeArguments();
|
||||
stream << builtinTypeName(_builtinType);
|
||||
stream << m_typeSystem.typeName(_builtinType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -85,12 +186,29 @@ std::string TypeSystem::typeToString(Type const& _type) const
|
||||
return stream.str();
|
||||
},
|
||||
[](TypeVariable const& _type) {
|
||||
return fmt::format("{}var{}", _type.generic() ? '?' : '\'', _type.index());
|
||||
std::stringstream stream;
|
||||
stream << (_type.generic() ? '?' : '\'') << "var" << _type.index();
|
||||
switch (_type.sort().classes.size())
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
stream << ":" << _type.sort().classes.begin()->declaration->name();
|
||||
break;
|
||||
default:
|
||||
stream << ":{";
|
||||
for (auto typeClass: _type.sort().classes | ranges::views::drop_last(1))
|
||||
stream << typeClass.declaration->name() << ", ";
|
||||
stream << _type.sort().classes.rbegin()->declaration->name();
|
||||
stream << "}";
|
||||
break;
|
||||
}
|
||||
return stream.str();
|
||||
},
|
||||
}, resolve(_type));
|
||||
}
|
||||
|
||||
vector<TypeSystem::UnificationFailure> TypeSystem::unify(Type _a, Type _b)
|
||||
vector<TypeEnvironment::UnificationFailure> TypeEnvironment::unify(Type _a, Type _b)
|
||||
{
|
||||
vector<UnificationFailure> failures;
|
||||
auto unificationFailure = [&]() {
|
||||
@ -100,10 +218,24 @@ vector<TypeSystem::UnificationFailure> TypeSystem::unify(Type _a, Type _b)
|
||||
_b = resolve(_b);
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](TypeVariable _left, TypeVariable _right) {
|
||||
validate(_left);
|
||||
validate(_right);
|
||||
if (_left.index() != _right.index())
|
||||
instantiate(_left, _right);
|
||||
if (_left.index() == _right.index())
|
||||
{
|
||||
if (_left.sort() != _right.sort())
|
||||
unificationFailure();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_left.sort() < _right.sort())
|
||||
instantiate(_left, _right);
|
||||
else if (_right.sort() < _left.sort())
|
||||
instantiate(_right, _left);
|
||||
else
|
||||
{
|
||||
Type newVar = m_typeSystem.freshTypeVariable(_left.generic() && _right.generic(), _left.sort() + _right.sort());
|
||||
instantiate(_left, newVar);
|
||||
instantiate(_right, newVar);
|
||||
}
|
||||
}
|
||||
},
|
||||
[&](TypeVariable _var, auto) {
|
||||
instantiate(_var, _b);
|
||||
@ -126,55 +258,60 @@ vector<TypeSystem::UnificationFailure> TypeSystem::unify(Type _a, Type _b)
|
||||
return failures;
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::freshTypeVariable(bool _generic)
|
||||
TypeEnvironment TypeEnvironment::clone() const
|
||||
{
|
||||
uint64_t index = m_typeVariables.size();
|
||||
m_typeVariables.emplace_back(std::nullopt);
|
||||
return TypeVariable(*this, index, _generic);
|
||||
TypeEnvironment result{m_typeSystem};
|
||||
result.m_typeVariables = m_typeVariables;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TypeSystem::instantiate(TypeVariable _variable, Type _type)
|
||||
experimental::Type TypeSystem::freshTypeVariable(bool _generic, Sort const& _sort)
|
||||
{
|
||||
validate(_variable);
|
||||
solAssert(!m_typeVariables.at(static_cast<size_t>(_variable.index())).has_value());
|
||||
solAssert(_variable.m_parent == this);
|
||||
m_typeVariables[static_cast<size_t>(_variable.index())] = _type;
|
||||
uint64_t index = m_numTypeVariables++;
|
||||
return TypeVariable(index, _sort, _generic);
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::resolve(Type _type) const
|
||||
void TypeEnvironment::instantiate(TypeVariable _variable, Type _type)
|
||||
{
|
||||
solAssert(m_typeVariables.emplace(_variable.index(), _type).second);
|
||||
}
|
||||
|
||||
experimental::Type TypeEnvironment::resolve(Type _type) const
|
||||
{
|
||||
Type result = _type;
|
||||
while(auto const* var = std::get_if<TypeVariable>(&result))
|
||||
if (auto value = m_typeVariables.at(static_cast<size_t>(var->index())))
|
||||
result = *value;
|
||||
if (Type const* resolvedType = util::valueOrNullptr(m_typeVariables, var->index()))
|
||||
result = *resolvedType;
|
||||
else
|
||||
break;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity)
|
||||
void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arguments)
|
||||
{
|
||||
solAssert(m_builtinTypes.count(_builtinType) == 0, "Builtin type already declared.");
|
||||
m_builtinTypes[_builtinType] = TypeConstructorInfo{
|
||||
declareTypeConstructor(_builtinType, _name, _arguments);
|
||||
}
|
||||
|
||||
void TypeSystem::declareTypeConstructor(TypeExpression::Constructor _typeConstructor, std::string _name, size_t _arguments)
|
||||
{
|
||||
bool newlyInserted = m_typeConstructors.emplace(std::make_pair(_typeConstructor, TypeConstructorInfo{
|
||||
_name,
|
||||
_arity
|
||||
};
|
||||
_arguments,
|
||||
{}
|
||||
})).second;
|
||||
// TODO: proper error handling.
|
||||
solAssert(newlyInserted, "Type constructor already declared.");
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const
|
||||
{
|
||||
auto const& info = m_builtinTypes.at(_builtinType);
|
||||
solAssert(info.arity == _arguments.size(), "Invalid arity.");
|
||||
// TODO: proper error handling
|
||||
auto const& info = m_typeConstructors.at(_builtinType);
|
||||
solAssert(info.arguments == _arguments.size(), "Invalid arity.");
|
||||
return TypeExpression{_builtinType, _arguments};
|
||||
}
|
||||
|
||||
void TypeSystem::validate(TypeVariable _variable) const
|
||||
{
|
||||
solAssert(_variable.m_parent == this);
|
||||
solAssert(_variable.index() < m_typeVariables.size());
|
||||
}
|
||||
|
||||
experimental::Type TypeSystem::fresh(Type _type, bool _generalize)
|
||||
experimental::Type TypeEnvironment::fresh(Type _type, bool _generalize)
|
||||
{
|
||||
std::unordered_map<uint64_t, Type> mapping;
|
||||
auto freshImpl = [&](Type _type, bool _generalize, auto _recurse) -> Type {
|
||||
@ -188,12 +325,17 @@ experimental::Type TypeSystem::fresh(Type _type, bool _generalize)
|
||||
};
|
||||
},
|
||||
[&](TypeVariable const& _var) -> Type {
|
||||
validate(_var);
|
||||
if (_generalize || _var.generic())
|
||||
{
|
||||
if (mapping.count(_var.index()))
|
||||
return mapping[_var.index()];
|
||||
return mapping[_var.index()] = freshTypeVariable(true);
|
||||
if (auto* mapped = util::valueOrNullptr(mapping, _var.index()))
|
||||
{
|
||||
auto* typeVariable = get_if<TypeVariable>(mapped);
|
||||
solAssert(typeVariable);
|
||||
// TODO: can there be a mismatch?
|
||||
solAssert(typeVariable->sort() == _var.sort());
|
||||
return *mapped;
|
||||
}
|
||||
return mapping[_var.index()] = m_typeSystem.freshTypeVariable(true, _var.sort());
|
||||
}
|
||||
else
|
||||
return _type;
|
||||
@ -203,11 +345,12 @@ experimental::Type TypeSystem::fresh(Type _type, bool _generalize)
|
||||
return freshImpl(_type, _generalize, freshImpl);
|
||||
}
|
||||
|
||||
void TypeSystem::instantiateClass(TypeExpression::Constructor _typeConstructor, vector<TypeClass> _argumentSorts, TypeClass _class)
|
||||
void TypeSystem::instantiateClass(TypeExpression::Constructor _typeConstructor, Arity _arity)
|
||||
{
|
||||
(void)_typeConstructor;
|
||||
(void)_argumentSorts;
|
||||
(void)_class;
|
||||
// TODO: proper error handling
|
||||
auto& typeConstructorInfo = m_typeConstructors.at(_typeConstructor);
|
||||
solAssert(_arity._argumentSorts.size() == typeConstructorInfo.arguments, "Invalid arity.");
|
||||
typeConstructorInfo.arities.emplace_back(_arity);
|
||||
}
|
||||
|
||||
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
|
||||
@ -262,7 +405,7 @@ experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType,
|
||||
return typeSystem.builtinType(BuiltinType::Function, {_argType, _resultType});
|
||||
}
|
||||
|
||||
tuple<TypeExpression::Constructor, vector<experimental::Type>> TypeSystemHelpers::destTypeExpression(Type _functionType) const
|
||||
tuple<TypeExpression::Constructor, vector<experimental::Type>> TypeSystemHelpers::destTypeExpression(Type _type) const
|
||||
{
|
||||
using ResultType = tuple<TypeExpression::Constructor, vector<Type>>;
|
||||
return std::visit(util::GenericVisitor{
|
||||
@ -272,7 +415,7 @@ tuple<TypeExpression::Constructor, vector<experimental::Type>> TypeSystemHelpers
|
||||
[](auto) -> ResultType {
|
||||
solAssert(false);
|
||||
}
|
||||
}, _functionType);
|
||||
}, _type);
|
||||
}
|
||||
|
||||
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
|
||||
@ -283,3 +426,23 @@ tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionTyp
|
||||
solAssert(arguments.size() == 2);
|
||||
return make_tuple(arguments.front(), arguments.back());
|
||||
}
|
||||
|
||||
vector<experimental::Type> TypeSystemHelpers::typeVars(Type _type) const
|
||||
{
|
||||
vector<Type> typeVars;
|
||||
auto typeVarsImpl = [&](Type _type, auto _recurse) -> void {
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](TypeExpression 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;
|
||||
|
||||
}
|
@ -40,6 +40,8 @@ struct TypeVariable;
|
||||
|
||||
using Type = std::variant<TypeExpression, TypeVariable>;
|
||||
|
||||
std::string canonicalTypeName(Type _type);
|
||||
|
||||
enum class BuiltinType
|
||||
{
|
||||
Void,
|
||||
@ -55,68 +57,123 @@ struct TypeExpression
|
||||
using Constructor = std::variant<BuiltinType, Declaration const*>;
|
||||
Constructor constructor;
|
||||
std::vector<Type> arguments;
|
||||
};
|
||||
|
||||
struct TypeVariable
|
||||
{
|
||||
TypeSystem const& parent() const { return *m_parent; }
|
||||
uint64_t index() const { return m_index; }
|
||||
bool generic() const { return m_generic; }
|
||||
private:
|
||||
friend class TypeSystem;
|
||||
TypeSystem const* m_parent = nullptr;
|
||||
uint64_t m_index = 0;
|
||||
bool m_generic = false;
|
||||
TypeVariable(TypeSystem const& _parent, uint64_t _index, bool _generic): m_parent(&_parent), m_index(_index), m_generic(_generic) {}
|
||||
bool operator<(TypeExpression const& _rhs) const;
|
||||
bool operator==(TypeExpression const& _rhs) const
|
||||
{
|
||||
// TODO
|
||||
return !(*this < _rhs) && !(_rhs < *this);
|
||||
}
|
||||
bool operator!=(TypeExpression const& _rhs) const
|
||||
{
|
||||
return !operator==(_rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct TypeClass
|
||||
{
|
||||
Declaration const* declaration = nullptr;
|
||||
bool operator<(TypeClass const& _rhs) const;
|
||||
bool operator==(TypeClass const& _rhs) const { return declaration == _rhs.declaration; }
|
||||
bool operator!=(TypeClass const& _rhs) const { return declaration != _rhs.declaration; }
|
||||
};
|
||||
|
||||
struct Sort
|
||||
{
|
||||
|
||||
std::set<TypeClass> classes;
|
||||
bool operator==(Sort const& _rhs) const;
|
||||
bool operator!=(Sort const& _rhs) const { return !operator==(_rhs); }
|
||||
bool operator<(Sort const& _rhs) const;
|
||||
Sort operator+(Sort const& _rhs) const;
|
||||
};
|
||||
|
||||
struct Arity
|
||||
{
|
||||
std::vector<Arity> _argumentSorts;
|
||||
std::vector<Sort> _argumentSorts;
|
||||
TypeClass _class;
|
||||
};
|
||||
|
||||
struct TypeVariable
|
||||
{
|
||||
size_t index() const { return m_index; }
|
||||
bool generic() const { return m_generic; }
|
||||
Sort const& sort() const { return m_sort; }
|
||||
bool operator<(TypeVariable const& _rhs) const
|
||||
{
|
||||
// TODO: more robust comparison?
|
||||
return m_index < _rhs.m_index;
|
||||
}
|
||||
bool operator==(TypeVariable const& _rhs) const
|
||||
{
|
||||
// TODO
|
||||
return !(*this < _rhs) && !(_rhs < *this);
|
||||
}
|
||||
bool operator!=(TypeVariable const& _rhs) const
|
||||
{
|
||||
return !operator==(_rhs);
|
||||
}
|
||||
private:
|
||||
friend class TypeSystem;
|
||||
size_t m_index = 0;
|
||||
Sort m_sort;
|
||||
bool m_generic = false;
|
||||
TypeVariable(size_t _index, Sort _sort, bool _generic):
|
||||
m_index(_index), m_sort(std::move(_sort)), m_generic(_generic) {}
|
||||
};
|
||||
|
||||
class TypeEnvironment
|
||||
{
|
||||
public:
|
||||
TypeEnvironment(TypeSystem& _typeSystem): m_typeSystem(_typeSystem) {}
|
||||
TypeEnvironment(TypeEnvironment const&) = delete;
|
||||
TypeEnvironment& operator=(TypeEnvironment const&) = delete;
|
||||
TypeEnvironment clone() const;
|
||||
Type resolve(Type _type) const;
|
||||
Type fresh(Type _type, bool _generalize);
|
||||
struct UnificationFailure { Type a; Type b; };
|
||||
[[nodiscard]] std::vector<UnificationFailure> unify(Type _a, Type _b);
|
||||
std::string typeToString(Type const& _type) const;
|
||||
private:
|
||||
TypeEnvironment(TypeEnvironment&& _env): m_typeSystem(_env.m_typeSystem), m_typeVariables(std::move(_env.m_typeVariables)) {}
|
||||
void instantiate(TypeVariable _variable, Type _type);
|
||||
TypeSystem& m_typeSystem;
|
||||
std::map<size_t, Type> m_typeVariables;
|
||||
};
|
||||
|
||||
class TypeSystem
|
||||
{
|
||||
public:
|
||||
TypeSystem() {}
|
||||
TypeSystem(TypeSystem const&) = delete;
|
||||
TypeSystem const& operator=(TypeSystem const&) = delete;
|
||||
void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity);
|
||||
void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arguments);
|
||||
Type builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const;
|
||||
std::string builtinTypeName(BuiltinType _builtinType) const
|
||||
std::string typeName(TypeExpression::Constructor _typeConstructor) const
|
||||
{
|
||||
return m_builtinTypes.at(_builtinType).name;
|
||||
// TODO: proper error handling
|
||||
return m_typeConstructors.at(_typeConstructor).name;
|
||||
}
|
||||
Type freshFreeType();
|
||||
Type resolve(Type _type) const;
|
||||
std::string typeToString(Type const& _type) const;
|
||||
Type freshTypeVariable(bool _generic);
|
||||
Type fresh(Type _type, bool _generalize);
|
||||
struct UnificationFailure { Type a; Type b; };
|
||||
[[nodiscard]] std::vector<UnificationFailure> unify(Type _a, Type _b);
|
||||
void instantiateClass(TypeExpression::Constructor _typeConstructor, std::vector<TypeClass> _argumentSorts, TypeClass _class);
|
||||
void declareTypeConstructor(TypeExpression::Constructor _typeConstructor, std::string _name, size_t _arguments);
|
||||
size_t constructorArguments(TypeExpression::Constructor _typeConstructor) const
|
||||
{
|
||||
// TODO: error handling
|
||||
return m_typeConstructors.at(_typeConstructor).arguments;
|
||||
}
|
||||
void instantiateClass(TypeExpression::Constructor _typeConstructor, Arity _arity);
|
||||
|
||||
Type freshTypeVariable(bool _generic, Sort const& _sort);
|
||||
|
||||
TypeEnvironment const& env() const { return m_globalTypeEnvironment; }
|
||||
TypeEnvironment& env() { return m_globalTypeEnvironment; }
|
||||
private:
|
||||
void instantiate(TypeVariable _variable, Type _type);
|
||||
void validate(TypeVariable _variable) const;
|
||||
std::vector<std::optional<Type>> m_freeTypes;
|
||||
size_t m_numTypeVariables = 0;
|
||||
struct TypeConstructorInfo
|
||||
{
|
||||
std::string name;
|
||||
uint64_t arity = 0;
|
||||
size_t arguments;
|
||||
std::vector<Arity> arities;
|
||||
};
|
||||
std::map<BuiltinType, TypeConstructorInfo> m_builtinTypes;
|
||||
std::vector<std::optional<Type>> m_typeVariables;
|
||||
std::map<TypeExpression::Constructor, Sort> m_sorts;
|
||||
std::map<TypeExpression::Constructor, TypeConstructorInfo> m_typeConstructors;
|
||||
TypeEnvironment m_globalTypeEnvironment{*this};
|
||||
};
|
||||
|
||||
struct TypeSystemHelpers
|
||||
@ -127,6 +184,7 @@ struct TypeSystemHelpers
|
||||
std::vector<Type> destTupleType(Type _tupleType) const;
|
||||
Type functionType(Type _argType, Type _resultType) const;
|
||||
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
|
||||
std::vector<Type> typeVars(Type _type) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
73
libsolidity/codegen/experimental/Common.cpp
Normal file
73
libsolidity/codegen/experimental/Common.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
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/codegen/experimental/Common.h>
|
||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
#include <libyul/AsmPrinter.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::yul;
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
string IRNames::function(FunctionDefinition const& _function, Type _type)
|
||||
{
|
||||
if (_function.isConstructor())
|
||||
return constructor(*_function.annotation().contract);
|
||||
|
||||
return "fun_" + _function.name() + "_" + to_string(_function.id()) + "$" + canonicalTypeName(_type) + "$";
|
||||
}
|
||||
|
||||
string IRNames::function(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||
}
|
||||
|
||||
string IRNames::creationObject(ContractDefinition const& _contract)
|
||||
{
|
||||
return _contract.name() + "_" + toString(_contract.id());
|
||||
}
|
||||
|
||||
string IRNames::deployedObject(ContractDefinition const& _contract)
|
||||
{
|
||||
return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
|
||||
}
|
||||
|
||||
string IRNames::constructor(ContractDefinition const& _contract)
|
||||
{
|
||||
return "constructor_" + _contract.name() + "_" + to_string(_contract.id());
|
||||
}
|
||||
|
||||
string IRNames::localVariable(VariableDeclaration const& _declaration)
|
||||
{
|
||||
return "var_" + _declaration.name() + '_' + std::to_string(_declaration.id());
|
||||
}
|
||||
|
||||
string IRNames::localVariable(Expression const& _expression)
|
||||
{
|
||||
return "expr_" + to_string(_expression.id());
|
||||
}
|
||||
|
||||
}
|
41
libsolidity/codegen/experimental/Common.h
Normal file
41
libsolidity/codegen/experimental/Common.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
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/AST.h>
|
||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
struct IRNames
|
||||
{
|
||||
static std::string function(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);
|
||||
static std::string constructor(ContractDefinition const& _contract);
|
||||
static std::string localVariable(VariableDeclaration const& _declaration);
|
||||
static std::string localVariable(Expression const& _expression);
|
||||
};
|
||||
|
||||
}
|
@ -20,6 +20,9 @@
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
@ -31,13 +34,23 @@ class Analysis;
|
||||
struct IRGenerationContext
|
||||
{
|
||||
Analysis const& analysis;
|
||||
std::list<FunctionDefinition const*> functionQueue;
|
||||
std::set<FunctionDefinition const*> generatedFunctions;
|
||||
void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition)
|
||||
TypeEnvironment const* env = nullptr;
|
||||
void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition, Type _type)
|
||||
{
|
||||
if (!generatedFunctions.count(_functionDefinition))
|
||||
functionQueue.emplace_back(_functionDefinition);
|
||||
QueuedFunction queue{_functionDefinition, env->resolve(_type)};
|
||||
if (!generatedFunctions.count(queue))
|
||||
functionQueue.emplace_back(queue);
|
||||
}
|
||||
struct QueuedFunction
|
||||
{
|
||||
FunctionDefinition const* function;
|
||||
Type type;
|
||||
bool operator<(QueuedFunction const& _rhs) const {
|
||||
return std::make_tuple(function, type) < std::make_tuple(_rhs.function, _rhs.type);
|
||||
}
|
||||
};
|
||||
std::list<QueuedFunction> functionQueue;
|
||||
std::set<QueuedFunction> generatedFunctions;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -17,11 +17,14 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGenerator.h>
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/experimental/IRGeneratorForStatements.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/Common.h>
|
||||
#include <libsolidity/codegen/experimental/Common.h>
|
||||
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||
|
||||
|
||||
#include <libyul/YulStack.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
@ -42,6 +45,24 @@ using namespace solidity::frontend::experimental;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::util;
|
||||
|
||||
IRGenerator::IRGenerator(
|
||||
EVMVersion _evmVersion,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
frontend::RevertStrings, std::map<std::string, unsigned int>,
|
||||
DebugInfoSelection const&,
|
||||
CharStreamProvider const*,
|
||||
Analysis const& _analysis
|
||||
)
|
||||
:
|
||||
m_evmVersion(_evmVersion),
|
||||
m_eofVersion(_eofVersion),
|
||||
// m_debugInfoSelection(_debugInfoSelection),
|
||||
// m_soliditySourceProvider(_soliditySourceProvider),
|
||||
m_env(_analysis.typeSystem().env().clone()),
|
||||
m_context{_analysis, &m_env, {}, {}}
|
||||
{
|
||||
}
|
||||
|
||||
string IRGenerator::run(
|
||||
ContractDefinition const& _contract,
|
||||
bytes const& /*_cborMetadata*/,
|
||||
@ -75,30 +96,33 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
code << "{\n";
|
||||
if (_contract.fallbackFunction())
|
||||
{
|
||||
code << IRNames::function(*_contract.fallbackFunction()) << "()\n";
|
||||
m_context.functionQueue.emplace_front(_contract.fallbackFunction());
|
||||
auto type = m_context.analysis.annotation<TypeInference>(*_contract.fallbackFunction()).type;
|
||||
solAssert(type);
|
||||
type = m_context.analysis.typeSystem().env().resolve(*type);
|
||||
code << IRNames::function(*_contract.fallbackFunction(), *type) << "()\n";
|
||||
m_context.enqueueFunctionDefinition(_contract.fallbackFunction(), *type);
|
||||
}
|
||||
code << "revert(0,0)\n";
|
||||
code << "}\n";
|
||||
|
||||
while (!m_context.functionQueue.empty())
|
||||
{
|
||||
FunctionDefinition const* function = m_context.functionQueue.front();
|
||||
auto function = m_context.functionQueue.front();
|
||||
m_context.functionQueue.pop_front();
|
||||
if (!m_context.generatedFunctions.count(function))
|
||||
{
|
||||
m_context.generatedFunctions.insert(function);
|
||||
code << generate(*function);
|
||||
code << generate(*function.function, function.type);
|
||||
}
|
||||
}
|
||||
|
||||
return code.str();
|
||||
}
|
||||
|
||||
string IRGenerator::generate(FunctionDefinition const& _function)
|
||||
string IRGenerator::generate(FunctionDefinition const& _function, Type _type)
|
||||
{
|
||||
std::stringstream code;
|
||||
code << "function " << IRNames::function(_function) << "(";
|
||||
code << "function " << IRNames::function(_function, _type) << "(";
|
||||
if (_function.parameters().size() > 1)
|
||||
for (auto const& arg: _function.parameters() | ranges::views::drop_last(1))
|
||||
code << IRNames::localVariable(*arg) << ", ";
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/ast/CallGraph.h>
|
||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||
|
||||
#include <liblangutil/CharStreamProvider.h>
|
||||
#include <liblangutil/DebugInfoSelection.h>
|
||||
@ -35,7 +36,6 @@
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
class SourceUnit;
|
||||
class Analysis;
|
||||
|
||||
class IRGenerator
|
||||
@ -49,13 +49,7 @@ public:
|
||||
langutil::DebugInfoSelection const& /*_debugInfoSelection*/,
|
||||
langutil::CharStreamProvider const* /*_soliditySourceProvider*/,
|
||||
Analysis const& _analysis
|
||||
):
|
||||
m_evmVersion(_evmVersion),
|
||||
m_eofVersion(_eofVersion),
|
||||
// m_debugInfoSelection(_debugInfoSelection),
|
||||
// m_soliditySourceProvider(_soliditySourceProvider),
|
||||
m_context{_analysis, {}, {}}
|
||||
{}
|
||||
);
|
||||
|
||||
std::string run(
|
||||
ContractDefinition const& _contract,
|
||||
@ -64,13 +58,14 @@ public:
|
||||
);
|
||||
|
||||
std::string generate(ContractDefinition const& _contract);
|
||||
std::string generate(FunctionDefinition const& _function);
|
||||
std::string generate(FunctionDefinition const& _function, Type _type);
|
||||
private:
|
||||
langutil::EVMVersion const m_evmVersion;
|
||||
std::optional<uint8_t> const m_eofVersion;
|
||||
OptimiserSettings const m_optimiserSettings;
|
||||
// langutil::DebugInfoSelection m_debugInfoSelection = {};
|
||||
// langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
||||
TypeEnvironment m_env;
|
||||
IRGenerationContext m_context;
|
||||
};
|
||||
|
||||
|
@ -18,12 +18,16 @@
|
||||
|
||||
#include <libsolidity/codegen/experimental/IRGeneratorForStatements.h>
|
||||
|
||||
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||
#include <libsolidity/analysis/experimental/TypeRegistration.h>
|
||||
|
||||
#include <libyul/YulStack.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/optimiser/ASTCopier.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/Common.h>
|
||||
#include <libsolidity/codegen/experimental/Common.h>
|
||||
|
||||
#include <range/v3/view/drop_last.hpp>
|
||||
|
||||
@ -46,9 +50,10 @@ namespace {
|
||||
struct CopyTranslate: public yul::ASTCopier
|
||||
{
|
||||
CopyTranslate(
|
||||
IRGenerationContext const& _context,
|
||||
yul::Dialect const& _dialect,
|
||||
map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> _references
|
||||
): m_dialect(_dialect), m_references(std::move(_references)) {}
|
||||
): m_context(_context), m_dialect(_dialect), m_references(std::move(_references)) {}
|
||||
|
||||
using ASTCopier::operator();
|
||||
|
||||
@ -90,11 +95,15 @@ private:
|
||||
auto const& reference = m_references.at(&_identifier);
|
||||
auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
|
||||
solAssert(varDecl, "External reference in inline assembly to something that is not a variable declaration.");
|
||||
// TODO: validate that variable is known and has word type.
|
||||
auto type = m_context.analysis.annotation<TypeInference>(*varDecl).type;
|
||||
solAssert(type);
|
||||
type = m_context.env->resolve(*type);
|
||||
solAssert(*type == m_context.analysis.typeSystem().builtinType(BuiltinType::Word, {}));
|
||||
string value = IRNames::localVariable(*varDecl);
|
||||
return yul::Identifier{_identifier.debugData, yul::YulString{value}};
|
||||
}
|
||||
|
||||
IRGenerationContext const& m_context;
|
||||
yul::Dialect const& m_dialect;
|
||||
map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> m_references;
|
||||
};
|
||||
@ -103,7 +112,7 @@ private:
|
||||
|
||||
bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly)
|
||||
{
|
||||
CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences};
|
||||
CopyTranslate bodyCopier{m_context, _assembly.dialect(), _assembly.annotation().externalReferences};
|
||||
yul::Statement modified = bodyCopier(_assembly.operations());
|
||||
solAssert(holds_alternative<yul::Block>(modified));
|
||||
m_code << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
|
||||
@ -153,13 +162,45 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||
for(auto arg: _functionCall.arguments())
|
||||
arg->accept(*this);
|
||||
|
||||
auto const* identifier = dynamic_cast<Identifier const*>(&_functionCall.expression());
|
||||
solAssert(identifier, "Complex function call expressions not supported.");
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration);
|
||||
solAssert(functionDefinition, "Function call expression must refer to a function definition.");
|
||||
m_context.enqueueFunctionDefinition(functionDefinition);
|
||||
FunctionDefinition const* functionDefinition = nullptr;
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
||||
{
|
||||
functionDefinition = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration);
|
||||
}
|
||||
else if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||
{
|
||||
auto const& expressionAnnotation = m_context.analysis.annotation<TypeInference>(memberAccess->expression());
|
||||
solAssert(expressionAnnotation.type);
|
||||
|
||||
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition) << "(";
|
||||
auto typeConstructor = std::get<0>(TypeSystemHelpers{m_context.analysis.typeSystem()}.destTypeExpression(
|
||||
m_context.analysis.typeSystem().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.");
|
||||
auto const* typeClassDefinition = dynamic_cast<TypeClassDefinition const*>(typeClass->annotation().referencedDeclaration);
|
||||
solAssert(typeClassDefinition, "Function call to member access only supported for type classes.");
|
||||
auto const& classAnnotation = m_context.analysis.annotation<TypeRegistration>(*typeClassDefinition);
|
||||
TypeClassInstantiation const* instantiation = classAnnotation.instantiations.at(typeConstructor);
|
||||
for (auto const& node: instantiation->subNodes())
|
||||
{
|
||||
auto const* def = dynamic_cast<FunctionDefinition const*>(node.get());
|
||||
solAssert(def);
|
||||
if (def->name() == memberAccess->memberName())
|
||||
{
|
||||
functionDefinition = def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
solAssert(false, "Complex function call expressions not supported.");
|
||||
|
||||
solAssert(functionDefinition);
|
||||
auto functionType = m_context.analysis.annotation<TypeInference>(_functionCall).type;
|
||||
solAssert(functionType);
|
||||
functionType = m_context.analysis.typeSystem().env().resolve(*functionType);
|
||||
m_context.enqueueFunctionDefinition(functionDefinition, *functionType);
|
||||
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition, *functionType) << "(";
|
||||
auto const& arguments = _functionCall.arguments();
|
||||
if (arguments.size() > 1)
|
||||
for (auto arg: arguments | ranges::views::drop_last(1))
|
||||
|
@ -1717,11 +1717,42 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
|
||||
auto [identifier, nameLocation] = expectIdentifierWithLocation();
|
||||
|
||||
ASTPointer<TypeName> type;
|
||||
vector<ASTPointer<IdentifierPath>> sorts;
|
||||
if (m_scanner->currentToken() == Token::Colon)
|
||||
{
|
||||
advance();
|
||||
type = parseTypeName();
|
||||
nodeFactory.setEndPositionFromNode(type);
|
||||
// TODO: handle this in parseTypeName()?
|
||||
if (m_scanner->currentLiteral() == "_")
|
||||
{
|
||||
nodeFactory.markEndPosition();
|
||||
advance();
|
||||
}
|
||||
else
|
||||
{
|
||||
type = parseTypeName();
|
||||
nodeFactory.setEndPositionFromNode(type);
|
||||
}
|
||||
if (m_scanner->currentToken() == Token::Colon)
|
||||
{
|
||||
advance();
|
||||
if (m_scanner->currentToken() == Token::LBrace)
|
||||
{
|
||||
// TODO
|
||||
sorts.emplace_back(parseIdentifierPath());
|
||||
while (m_scanner->currentToken() == Token::Comma)
|
||||
{
|
||||
advance();
|
||||
sorts.emplace_back(parseIdentifierPath());
|
||||
}
|
||||
expectToken(Token::RBrace);
|
||||
}
|
||||
else
|
||||
{
|
||||
sorts.emplace_back(parseIdentifierPath());
|
||||
}
|
||||
if (!sorts.empty())
|
||||
nodeFactory.setEndPositionFromNode(sorts.back());
|
||||
}
|
||||
}
|
||||
|
||||
return nodeFactory.createNode<VariableDeclaration>(
|
||||
@ -1730,7 +1761,12 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
|
||||
nameLocation,
|
||||
nullptr,
|
||||
Visibility::Default,
|
||||
documentation
|
||||
documentation,
|
||||
false,
|
||||
VariableDeclaration::Mutability::Mutable,
|
||||
nullptr,
|
||||
VariableDeclaration::Location::Unspecified,
|
||||
sorts
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,44 @@
|
||||
pragma experimental solidity;
|
||||
|
||||
|
||||
class a:StackType {
|
||||
function stackSize() -> x:integer;
|
||||
class a:A {
|
||||
function testValue(x:a) -> y:word;
|
||||
}
|
||||
|
||||
instantiation word : StackType {
|
||||
function stackSize() -> x:integer {
|
||||
return x;
|
||||
}
|
||||
|
||||
class a:B {
|
||||
function testValue(x:a) -> y:word;
|
||||
}
|
||||
|
||||
function f(a) -> b {
|
||||
instantiation word : A {
|
||||
function testValue(x:word) -> y:word {
|
||||
assembly {
|
||||
y := 7
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instantiation word : B {
|
||||
function testValue(x:word) -> y:word {
|
||||
assembly {
|
||||
y := 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f(a:_:A) -> b:_:B {
|
||||
return a;
|
||||
}
|
||||
|
||||
contract C {
|
||||
fallback() external {
|
||||
let x : word;
|
||||
let y : word;
|
||||
let x : word : A;
|
||||
let y;
|
||||
assembly {
|
||||
x := 0x42
|
||||
}
|
||||
y = f(x);
|
||||
y = B.testValue(x);
|
||||
assembly {
|
||||
mstore(0, y)
|
||||
return(0, 32)
|
||||
|
Loading…
Reference in New Issue
Block a user