Skip to content

Commit

Permalink
Separate experimental analysis pass for type class registration
Browse files Browse the repository at this point in the history
  • Loading branch information
cameel committed Sep 11, 2023
1 parent cf267e4 commit 2746135
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 29 deletions.
2 changes: 2 additions & 0 deletions libsolidity/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,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
Expand Down
30 changes: 29 additions & 1 deletion libsolidity/experimental/analysis/Analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

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

Expand All @@ -28,16 +29,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)
{
Expand Down Expand Up @@ -122,7 +150,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) {
Expand Down
65 changes: 65 additions & 0 deletions libsolidity/experimental/analysis/TypeClassRegistration.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
58 changes: 58 additions & 0 deletions libsolidity/experimental/analysis/TypeClassRegistration.h
Original file line number Diff line number Diff line change
@@ -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;
};

}
29 changes: 11 additions & 18 deletions libsolidity/experimental/analysis/TypeInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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())
Expand All @@ -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)
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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
{
Expand Down
2 changes: 0 additions & 2 deletions libsolidity/experimental/analysis/TypeInference.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ class TypeInference: public ASTConstVisitor
{
/// Expressions, variable declarations, function declarations.
std::optional<Type> type;
// Type classes.
std::optional<TypeClass> typeClass;
};
struct TypeMember
{
Expand Down
7 changes: 4 additions & 3 deletions libsolidity/experimental/codegen/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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))
Expand Down
10 changes: 5 additions & 5 deletions test/libsolidity/semanticTests/experimental/type_class.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -30,7 +30,7 @@ instantiation Dog: Animal {
return d;
}

function alive(d: Dog) -> bool {
function alive(self: Dog) -> bool {
let b: bool;
return b;
}
Expand Down

0 comments on commit 2746135

Please sign in to comment.