This commit is contained in:
Daniel Kirchner 2023-06-24 01:21:14 +02:00
parent 315270f3bb
commit 7d094b3be6
22 changed files with 933 additions and 174 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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;

View 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;
}

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

View File

@ -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;
};

View File

@ -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))));
}

View File

@ -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;

View File

@ -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;
}

View File

@ -33,6 +33,7 @@ public:
struct Annotation
{
Type type;
std::map<TypeExpression::Constructor, TypeClassInstantiation const*> instantiations;
};
TypeRegistration(Analysis& _analysis);

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
};
}

View 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());
}
}

View 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);
};
}

View File

@ -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;
};
}

View File

@ -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) << ", ";

View File

@ -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;
};

View File

@ -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))

View File

@ -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
);
}

View File

@ -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)