Separate experimental analysis pass for type class registration

This commit is contained in:
Kamil Śliwak 2023-09-07 12:14:28 +02:00
parent 086c912c65
commit da83b35a71
8 changed files with 174 additions and 29 deletions

View File

@ -188,6 +188,8 @@ set(sources
experimental/analysis/Analysis.h
experimental/analysis/DebugWarner.cpp
experimental/analysis/DebugWarner.h
experimental/analysis/TypeClassRegistration.cpp
experimental/analysis/TypeClassRegistration.h
experimental/analysis/TypeInference.cpp
experimental/analysis/TypeInference.h
experimental/analysis/TypeRegistration.cpp

View File

@ -18,6 +18,7 @@
#include <libsolidity/experimental/analysis/Analysis.h>
#include <libsolidity/experimental/analysis/DebugWarner.h>
#include <libsolidity/experimental/analysis/SyntaxRestrictor.h>
#include <libsolidity/experimental/analysis/TypeClassRegistration.h>
#include <libsolidity/experimental/analysis/TypeInference.h>
#include <libsolidity/experimental/analysis/TypeRegistration.h>
@ -27,16 +28,43 @@ using namespace solidity::frontend::experimental;
// TODO: creating all of them for all nodes up front may be wasteful, we should improve the mechanism.
struct Analysis::AnnotationContainer
{
TypeClassRegistration::Annotation typeClassRegistrationAnnotation;
TypeRegistration::Annotation typeRegistrationAnnotation;
TypeInference::Annotation typeInferenceAnnotation;
};
struct Analysis::GlobalAnnotationContainer
{
TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation;
TypeRegistration::GlobalAnnotation typeRegistrationAnnotation;
TypeInference::GlobalAnnotation typeInferenceAnnotation;
};
template<>
TypeClassRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeClassRegistration>::get(ASTNode const& _node)
{
return analysis.annotationContainer(_node).typeClassRegistrationAnnotation;
}
template<>
TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeClassRegistration>::get() const
{
return analysis.annotationContainer().typeClassRegistrationAnnotation;
}
template<>
TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeClassRegistration>::get()
{
return analysis.annotationContainer().typeClassRegistrationAnnotation;
}
template<>
TypeClassRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeClassRegistration>::get(ASTNode const& _node) const
{
return analysis.annotationContainer(_node).typeClassRegistrationAnnotation;
}
template<>
TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get(ASTNode const& _node)
{
@ -121,7 +149,7 @@ std::tuple<std::integral_constant<size_t, Is>...> makeIndexTuple(std::index_sequ
bool Analysis::check(std::vector<std::shared_ptr<SourceUnit const>> const& _sourceUnits)
{
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeRegistration, TypeInference, DebugWarner>;
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeClassRegistration, TypeRegistration, TypeInference, DebugWarner>;
return std::apply([&](auto... _indexTuple) {
return ([&](auto&& _step) {

View File

@ -0,0 +1,65 @@
/*
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/experimental/analysis/TypeClassRegistration.h>
#include <libsolidity/experimental/analysis/Analysis.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
using namespace solidity::frontend::experimental;
using namespace solidity::langutil;
TypeClassRegistration::TypeClassRegistration(Analysis& _analysis):
m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter()),
m_typeSystem(_analysis.typeSystem())
{
}
bool TypeClassRegistration::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
return !m_errorReporter.hasErrors();
}
bool TypeClassRegistration::visit(TypeClassDefinition const& _typeClassDefinition)
{
Type typeVar = m_typeSystem.freshTypeVariable({});
std::variant<TypeClass, std::string> typeClassOrError = m_typeSystem.declareTypeClass(
typeVar,
_typeClassDefinition.name(),
&_typeClassDefinition
);
m_analysis.annotation<TypeClassRegistration>(_typeClassDefinition).typeClass = std::visit(
util::GenericVisitor{
[](TypeClass _class) -> TypeClass { return _class; },
[&](std::string _error) -> TypeClass {
m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error);
util::unreachable();
}
},
typeClassOrError
);
return true;
}

View File

@ -0,0 +1,58 @@
/*
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 <libsolidity/experimental/ast/TypeSystem.h>
namespace solidity::langutil
{
class ErrorReporter;
}
namespace solidity::frontend::experimental
{
class Analysis;
class TypeSystem;
class TypeClassRegistration: public ASTConstVisitor
{
public:
struct Annotation
{
// Type classes.
std::optional<TypeClass> typeClass;
};
struct GlobalAnnotation
{
};
TypeClassRegistration(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);
private:
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
TypeSystem& m_typeSystem;
};
}

View File

@ -18,6 +18,8 @@
#include <libsolidity/experimental/analysis/TypeInference.h>
#include <libsolidity/experimental/analysis/TypeClassRegistration.h>
#include <libsolidity/experimental/analysis/TypeRegistration.h>
#include <libsolidity/experimental/analysis/Analysis.h>
#include <libsolidity/experimental/ast/TypeSystemHelper.h>
@ -205,7 +207,9 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
std::map<std::string, Type> functionTypes;
Type typeVar = m_typeSystem.freshTypeVariable({});
solAssert(m_analysis.annotation<TypeClassRegistration>(_typeClassDefinition).typeClass.has_value());
TypeClass typeClass = m_analysis.annotation<TypeClassRegistration>(_typeClassDefinition).typeClass.value();
Type typeVar = m_typeSystem.typeClassInfo(typeClass).typeVariable;
auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)];
for (auto subNode: _typeClassDefinition.subNodes())
@ -224,14 +228,6 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType};
}
TypeClass typeClass = std::visit(util::GenericVisitor{
[](TypeClass _class) -> TypeClass { return _class; },
[&](std::string _error) -> TypeClass {
m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error);
util::unreachable();
}
}, m_typeSystem.declareTypeClass(typeVar, _typeClassDefinition.name(), &_typeClassDefinition));
typeClassDefinitionAnnotation.typeClass = typeClass;
annotation().typeClassFunctions[typeClass] = std::move(functionTypes);
for (auto [functionName, functionType]: functionTypes)
@ -574,12 +570,10 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut
{
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term};
typeClassDefinition->accept(*this);
if (!annotation(*typeClassDefinition).typeClass)
{
m_errorReporter.typeError(2736_error, _location, "Unregistered type class.");
return m_typeSystem.freshTypeVariable({});
}
return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClassDefinition).typeClass}});
solAssert(m_analysis.annotation<TypeClassRegistration>(*typeClassDefinition).typeClass.has_value());
TypeClass typeClass = m_analysis.annotation<TypeClassRegistration>(*typeClassDefinition).typeClass.value();
return m_typeSystem.freshTypeVariable(Sort{{typeClass}});
}
else
{
@ -680,9 +674,8 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
// visiting the type class will re-visit this instantiation
typeClassDefinition->accept(*this);
// TODO: more error handling? Should be covered by the visit above.
if (!annotation(*typeClassDefinition).typeClass)
m_errorReporter.typeError(8503_error, _typeClassInstantiation.typeClass().location(), "Expected type class.");
return annotation(*typeClassDefinition).typeClass;
solAssert(m_analysis.annotation<TypeClassRegistration>(*typeClassDefinition).typeClass.has_value());
return m_analysis.annotation<TypeClassRegistration>(*typeClassDefinition).typeClass.value();
}
else
{

View File

@ -38,8 +38,6 @@ public:
{
/// Expressions, variable declarations, function declarations.
std::optional<Type> type;
// Type classes.
std::optional<TypeClass> typeClass;
};
struct TypeMember
{

View File

@ -19,6 +19,7 @@
#include <libsolidity/experimental/codegen/IRGeneratorForStatements.h>
#include <libsolidity/experimental/analysis/Analysis.h>
#include <libsolidity/experimental/analysis/TypeClassRegistration.h>
#include <libsolidity/experimental/analysis/TypeInference.h>
#include <libsolidity/experimental/analysis/TypeRegistration.h>
@ -271,11 +272,11 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(declaration);
if (auto const* typeClassDefinition = dynamic_cast<TypeClassDefinition const*>(declaration))
{
std::optional<TypeClass> typeClass = m_context.analysis.annotation<TypeInference>(*typeClassDefinition).typeClass;
solAssert(typeClass);
solAssert(m_context.analysis.annotation<TypeClassRegistration>(*typeClassDefinition).typeClass.has_value());
TypeClass typeClass = m_context.analysis.annotation<TypeClassRegistration>(*typeClassDefinition).typeClass.value();
solAssert(m_expressionDeclaration.emplace(
&_memberAccess,
&resolveTypeClassFunction(*typeClass, _memberAccess.memberName(), memberAccessType)
&resolveTypeClassFunction(typeClass, _memberAccess.memberName(), memberAccessType)
).second);
}
else if (dynamic_cast<TypeDefinition const*>(declaration))

View File

@ -3,9 +3,9 @@ pragma experimental solidity;
type Cat = word;
type Dog = word;
class A: Animal {
function new() -> A;
function alive(a: A) -> bool;
class Self: Animal {
function new() -> Self;
function alive(self: Self) -> bool;
}
instantiation Cat: Animal {
@ -14,7 +14,7 @@ instantiation Cat: Animal {
return c;
}
function alive(c: Cat) -> bool {
function alive(self: Cat) -> bool {
// TODO: Boolean literals or operators not implemented.
let w;
assembly {
@ -30,7 +30,7 @@ instantiation Dog: Animal {
return d;
}
function alive(d: Dog) -> bool {
function alive(self: Dog) -> bool {
let b: bool;
return b;
}