This commit is contained in:
Daniel Kirchner 2023-06-25 03:44:37 +02:00
parent 1bc8caf54a
commit d882a08582
24 changed files with 1099 additions and 321 deletions

View File

@ -340,6 +340,11 @@ namespace TokenTraits
tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave ||
tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex;
}
constexpr bool isBuiltinTypeClassName(Token tok)
{
return tok == Token::Integer || (isBinaryOp(tok) && tok != Token::Comma) ||
isCompareOp(tok) || isUnaryOp(tok) || (isAssignmentOp(tok) && tok != Token::Assign);
}
constexpr bool isExperimentalSolidityKeyword(Token tok)
{
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback ||

View File

@ -33,12 +33,31 @@ struct Analysis::AnnotationContainer
TypeInference::Annotation typeInferenceAnnotation;
};
struct Analysis::GlobalAnnotationContainer
{
TypeRegistration::GlobalAnnotation typeRegistrationAnnotation;
TypeInference::GlobalAnnotation typeInferenceAnnotation;
};
template<>
TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get(ASTNode const& _node)
{
return analysis.annotationContainer(_node).typeRegistrationAnnotation;
}
template<>
TypeRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeRegistration>::get() const
{
return analysis.annotationContainer().typeRegistrationAnnotation;
}
template<>
TypeRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get()
{
return analysis.annotationContainer().typeRegistrationAnnotation;
}
template<>
TypeRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeRegistration>::get(ASTNode const& _node) const
{
@ -57,6 +76,19 @@ TypeInference::Annotation const& solidity::frontend::experimental::detail::Const
return analysis.annotationContainer(_node).typeInferenceAnnotation;
}
template<>
TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<TypeInference>::get() const
{
return analysis.annotationContainer().typeInferenceAnnotation;
}
template<>
TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeInference>::get()
{
return analysis.annotationContainer().typeInferenceAnnotation;
}
Analysis::AnnotationContainer& Analysis::annotationContainer(ASTNode const& _node)
{
solAssert(_node.id() > 0);
@ -76,7 +108,8 @@ Analysis::AnnotationContainer const& Analysis::annotationContainer(ASTNode const
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 + 1)))
m_annotations(std::make_unique<AnnotationContainer[]>(static_cast<size_t>(_maxAstId + 1))),
m_globalAnnotation(std::make_unique<GlobalAnnotationContainer>())
{
}

View File

@ -47,18 +47,21 @@ struct AnnotationFetcher
{
Analysis& analysis;
typename Step::Annotation& get(ASTNode const& _node);
typename Step::GlobalAnnotation& get();
};
template<typename Step>
struct ConstAnnotationFetcher
{
Analysis const& analysis;
typename Step::Annotation const& get(ASTNode const& _node) const;
typename Step::GlobalAnnotation const& get() const;
};
}
class Analysis
{
struct AnnotationContainer;
struct GlobalAnnotationContainer;
public:
Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId);
Analysis(Analysis const&) = delete;
@ -79,13 +82,26 @@ public:
{
return detail::ConstAnnotationFetcher<Step>{*this}.get(_node);
}
template<typename Step>
typename Step::GlobalAnnotation& annotation()
{
return detail::AnnotationFetcher<Step>{*this}.get();
}
template<typename Step>
typename Step::GlobalAnnotation const& annotation() const
{
return detail::ConstAnnotationFetcher<Step>{*this}.get();
}
AnnotationContainer& annotationContainer(ASTNode const& _node);
AnnotationContainer const& annotationContainer(ASTNode const& _node) const;
GlobalAnnotationContainer& annotationContainer() { return *m_globalAnnotation; }
GlobalAnnotationContainer const& annotationContainer() const { return *m_globalAnnotation; }
private:
langutil::ErrorReporter& m_errorReporter;
TypeSystem m_typeSystem;
uint64_t m_maxAstId = 0;
std::unique_ptr<AnnotationContainer[]> m_annotations;
std::unique_ptr<GlobalAnnotationContainer> m_globalAnnotation;
};
}

View File

@ -80,8 +80,6 @@ bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition)
bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
{
if (_variableDeclarationStatement.initialValue())
m_errorReporter.syntaxError(0000_error, _variableDeclarationStatement.initialValue()->location(), "Variable declarations with initial value not supported.");
if (_variableDeclarationStatement.declarations().size() == 1)
{
if (!_variableDeclarationStatement.declarations().front())

View File

@ -58,6 +58,7 @@ private:
bool visit(BinaryOperation const&) override { return true; }
bool visit(ElementaryTypeNameExpression const&) override { return true; }
bool visit(TupleExpression const&) override { return true; }
bool visit(Literal const&) override { return true; }
langutil::ErrorReporter& m_errorReporter;
};

View File

@ -18,17 +18,23 @@
#include <libsolidity/analysis/experimental/TypeInference.h>
#include <libsolidity/analysis/experimental/TypeRegistration.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/StringUtils.h>
#include <liblangutil/Exceptions.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AST.h>
#include <boost/algorithm/string.hpp>
#include <range/v3/view/transform.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
using namespace solidity::langutil;
@ -128,36 +134,75 @@ bool TypeInference::visitNode(ASTNode const& _node)
bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
{
solAssert(m_expressionContext == ExpressionContext::Term);
auto& typeVariableAnnotation = annotation(_typeClassDefinition.typeVariable());
if (typeVariableAnnotation.type)
auto& typeClassAnnotation = annotation(_typeClassDefinition);
if (typeClassAnnotation.type)
return false;
m_typeSystem.declareTypeConstructor(&_typeClassDefinition, _typeClassDefinition.name(), 0);
typeClassAnnotation.type = TypeConstant{&_typeClassDefinition, {}};
auto& typeVariableAnnotation = annotation(_typeClassDefinition.typeVariable());
{
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
_typeClassDefinition.typeVariable().accept(*this);
}
solAssert(typeVariableAnnotation.type);
for (auto const& subNode: _typeClassDefinition.subNodes())
map<string, Type> functionTypes;
Type typeVar = m_typeSystem.freshTypeVariable(false, {});
auto& typeMembers = annotation().members[&_typeClassDefinition];
for (auto subNode: _typeClassDefinition.subNodes())
{
subNode->accept(*this);
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get());
solAssert(functionDefinition);
auto functionDefinitionType = annotation(*functionDefinition).type;
solAssert(functionDefinitionType);
auto functionType = m_env->fresh(*functionDefinitionType, true);
functionTypes[functionDefinition->name()] = functionType;
auto typeVars = TypeSystemHelpers{m_typeSystem}.typeVars(functionType);
if(typeVars.size() != 1)
m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class may only depend on the type class variable.");
unify(typeVars.front(), typeVar);
if (!typeMembers.emplace(functionDefinition->name(), TypeMember{functionType}).second)
m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class declared multiple times.");
}
if (auto error = m_typeSystem.declareTypeClass(TypeClass{&_typeClassDefinition}, typeVar, std::move(functionTypes)))
m_errorReporter.fatalTypeError(0000_error, _typeClassDefinition.location(), *error);
solAssert(typeVariableAnnotation.type);
TypeSystemHelpers helper{m_typeSystem};
unify(*typeVariableAnnotation.type, helper.kindType(m_typeSystem.freshTypeVariable(false, Sort{{TypeClass{&_typeClassDefinition}}})), _typeClassDefinition.location());
for (auto instantiation: m_analysis.annotation<TypeRegistration>(_typeClassDefinition).instantiations | ranges::views::values)
// TODO: recursion-safety?
instantiation->accept(*this);
return false;
}
/*
experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
optional<TypeConstructor> TypeInference::fromTypeName(TypeName const& _typeName)
{
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
{
switch(elementaryTypeName->typeName().token())
{
case Token::Word:
return m_wordType;
case Token::Void:
return m_voidType;
return BuiltinType::Void;
case Token::Fun:
return BuiltinType::Function;
case Token::Unit:
return BuiltinType::Unit;
case Token::Pair:
return BuiltinType::Pair;
case Token::Word:
return BuiltinType::Word;
case Token::Integer:
return m_integerType;
return BuiltinType::Integer;
default:
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
break;
@ -188,17 +233,19 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
}
*/
void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location)
void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location, TypeEnvironment* _env)
{
for (auto failure: m_env->unify(_a, _b))
if (!_env)
_env = m_env;
for (auto failure: _env->unify(_a, _b))
std::visit(util::GenericVisitor{
[&](TypeEnvironment::TypeMismatch _typeMismatch) {
m_errorReporter.typeError(0000_error, _location, fmt::format("Cannot unify {} and {}.", m_env->typeToString(_typeMismatch.a), m_env->typeToString(_typeMismatch.b)));
m_errorReporter.typeError(0000_error, _location, fmt::format("Cannot unify {} and {}.", _env->typeToString(_typeMismatch.a), _env->typeToString(_typeMismatch.b)));
},
[&](TypeEnvironment::SortMismatch _sortMismatch) {
m_errorReporter.typeError(0000_error, _location, fmt::format(
"{} does not have sort {}",
m_env->typeToString(_sortMismatch.type),
_env->typeToString(_sortMismatch.type),
TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort)
));
}
@ -276,8 +323,8 @@ bool TypeInference::visit(ElementaryTypeNameExpression const& _expression)
break;
case Token::Pair:
{
auto leftType = m_typeSystem.freshTypeVariable(true, {});
auto rightType = m_typeSystem.freshTypeVariable(true, {});
auto leftType = m_typeSystem.freshTypeVariable(false, {});
auto rightType = m_typeSystem.freshTypeVariable(false, {});
expressionAnnotation.type =
helper.functionType(
helper.kindType(helper.tupleType({leftType, rightType})),
@ -287,8 +334,8 @@ bool TypeInference::visit(ElementaryTypeNameExpression const& _expression)
}
case Token::Fun:
{
auto argType = m_typeSystem.freshTypeVariable(true, {});
auto resultType = m_typeSystem.freshTypeVariable(true, {});
auto argType = m_typeSystem.freshTypeVariable(false, {});
auto resultType = m_typeSystem.freshTypeVariable(false, {});
expressionAnnotation.type =
helper.functionType(
helper.kindType(helper.tupleType({argType, resultType})),
@ -313,9 +360,30 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation)
switch (m_expressionContext)
{
case ExpressionContext::Term:
m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operations in term context not yet supported.");
operationAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
return false;
if (auto* operatorInfo = util::valueOrNullptr(m_analysis.annotation<TypeRegistration>().operators, _binaryOperation.getOperator()))
{
auto [typeClass, functionName] = *operatorInfo;
Type functionType = m_env->fresh(m_typeSystem.typeClassInfo(typeClass)->functions.at(functionName), true);
_binaryOperation.leftExpression().accept(*this);
solAssert(leftAnnotation.type);
_binaryOperation.rightExpression().accept(*this);
solAssert(rightAnnotation.type);
Type argTuple = helper.tupleType({*leftAnnotation.type, *rightAnnotation.type});
Type genericFunctionType = helper.functionType(argTuple, m_typeSystem.freshTypeVariable(false, {}));
unify(functionType, genericFunctionType, _binaryOperation.location());
operationAnnotation.type = m_env->resolve(std::get<1>(helper.destFunctionType(m_env->resolve(genericFunctionType))));
return false;
}
else
{
m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operations in term context not yet supported.");
operationAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
return false;
}
case ExpressionContext::Type:
if (_binaryOperation.getOperator() == Token::Colon)
{
@ -361,6 +429,24 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation)
return false;
}
void TypeInference::endVisit(VariableDeclarationStatement const& _variableDeclarationStatement)
{
solAssert(m_expressionContext == ExpressionContext::Term);
if (_variableDeclarationStatement.declarations().size () != 1)
{
m_errorReporter.typeError(0000_error, _variableDeclarationStatement.location(), "Multi variable declaration not supported.");
return;
}
auto& variableAnnotation = annotation(*_variableDeclarationStatement.declarations().front());
solAssert(variableAnnotation.type);
if (_variableDeclarationStatement.initialValue())
{
auto& expressionAnnotation = annotation(*_variableDeclarationStatement.initialValue());
solAssert(expressionAnnotation.type);
unify(*variableAnnotation.type, *expressionAnnotation.type, _variableDeclarationStatement.location());
}
}
bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
{
solAssert(!_variableDeclaration.value());
@ -390,9 +476,16 @@ bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
variableAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
return false;
case ExpressionContext::Type:
if (_variableDeclaration.typeExpression())
m_errorReporter.typeError(0000_error, _variableDeclaration.location(), "Variable declaration in type context with type expression.");
variableAnnotation.type = helper.kindType(m_typeSystem.freshTypeVariable(false, {}));
if (_variableDeclaration.typeExpression())
{
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Sort};
_variableDeclaration.typeExpression()->accept(*this);
auto& typeExpressionAnnotation = annotation(*_variableDeclaration.typeExpression());
solAssert(typeExpressionAnnotation.type);
unify(*variableAnnotation.type, *typeExpressionAnnotation.type, _variableDeclaration.typeExpression()->location());
}
return false;
case ExpressionContext::Sort:
m_errorReporter.typeError(0000_error, _variableDeclaration.location(), "Variable declaration in sort context.");
@ -432,102 +525,131 @@ TypeInference::Annotation& TypeInference::annotation(ASTNode const& _node)
return m_analysis.annotation<TypeInference>(_node);
}
bool TypeInference::visit(Identifier const& _identifier)
TypeInference::GlobalAnnotation& TypeInference::annotation()
{
auto& identifierAnnotation = annotation(_identifier);
solAssert(!identifierAnnotation.type);
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
return m_analysis.annotation<TypeInference>();
}
experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration)
{
TypeSystemHelpers helper{m_typeSystem};
switch(m_expressionContext)
{
case ExpressionContext::Term:
{
solAssert(referencedDeclaration);
if (
!dynamic_cast<FunctionDefinition const*>(referencedDeclaration) &&
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration)
!dynamic_cast<FunctionDefinition const*>(&_declaration) &&
!dynamic_cast<VariableDeclaration const*>(&_declaration) &&
!dynamic_cast<TypeClassDefinition const*>(&_declaration) &&
!dynamic_cast<TypeDefinition const*>(&_declaration)
)
{
SecondarySourceLocation ssl;
ssl.append("Referenced node.", referencedDeclaration->location());
m_errorReporter.fatalTypeError(0000_error, _identifier.location(), ssl, "Attempt to type identifier referring to unexpected node.");
ssl.append("Referenced node.", _declaration.location());
m_errorReporter.fatalTypeError(0000_error, _location, ssl, "Attempt to type identifier referring to unexpected node.");
}
auto& declarationAnnotation = annotation(*referencedDeclaration);
auto& declarationAnnotation = annotation(_declaration);
if (!declarationAnnotation.type)
referencedDeclaration->accept(*this);
_declaration.accept(*this);
solAssert(declarationAnnotation.type);
if (dynamic_cast<FunctionDefinition const*>(referencedDeclaration))
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, true);
else if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
identifierAnnotation.type = declarationAnnotation.type;
if (dynamic_cast<FunctionDefinition const*>(&_declaration))
return m_env->fresh(*declarationAnnotation.type, true);
else if (dynamic_cast<VariableDeclaration const*>(&_declaration))
return *declarationAnnotation.type;
else if (dynamic_cast<TypeClassDefinition const*>(&_declaration))
return *declarationAnnotation.type;
else if (dynamic_cast<TypeDefinition const*>(&_declaration))
return *declarationAnnotation.type;
else
solAssert(false);
break;
}
case ExpressionContext::Type:
{
if (referencedDeclaration)
if (
!dynamic_cast<VariableDeclaration const*>(&_declaration) &&
!dynamic_cast<TypeDefinition const*>(&_declaration)
)
{
if (
!dynamic_cast<VariableDeclaration const*>(referencedDeclaration) &&
!dynamic_cast<TypeDefinition const*>(referencedDeclaration)
)
{
SecondarySourceLocation ssl;
ssl.append("Referenced node.", referencedDeclaration->location());
m_errorReporter.fatalTypeError(0000_error, _identifier.location(), ssl, "Attempt to type identifier referring to unexpected node.");
}
SecondarySourceLocation ssl;
ssl.append("Referenced node.", _declaration.location());
m_errorReporter.fatalTypeError(0000_error, _location, ssl, "Attempt to type identifier referring to unexpected node.");
}
// TODO: Assert that this is a type class variable declaration.
auto& declarationAnnotation = annotation(*referencedDeclaration);
if (!declarationAnnotation.type)
referencedDeclaration->accept(*this);
// TODO: Assert that this is a type class variable declaration.
auto& declarationAnnotation = annotation(_declaration);
if (!declarationAnnotation.type)
_declaration.accept(*this);
solAssert(declarationAnnotation.type);
solAssert(declarationAnnotation.type);
if (dynamic_cast<VariableDeclaration const*>(referencedDeclaration))
{
// TODO: helper.destKindType(*declarationAnnotation.type);
identifierAnnotation.type = declarationAnnotation.type;
}
else if (dynamic_cast<TypeDefinition const*>(referencedDeclaration))
{
// TODO: helper.destKindType(*declarationAnnotation.type);
identifierAnnotation.type = m_env->fresh(*declarationAnnotation.type, true);
}
else
solAssert(false);
if (dynamic_cast<VariableDeclaration const*>(&_declaration))
{
// TODO: helper.destKindType(*declarationAnnotation.type);
return *declarationAnnotation.type;
}
else if (dynamic_cast<TypeDefinition const*>(&_declaration))
{
// TODO: helper.destKindType(*declarationAnnotation.type);
return m_env->fresh(*declarationAnnotation.type, true);
}
else
{
// TODO: register free type variable name!
identifierAnnotation.type = helper.kindType(m_typeSystem.freshTypeVariable(false, {}));
return false;
}
solAssert(false);
break;
}
case ExpressionContext::Sort:
{
solAssert(referencedDeclaration);
auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(referencedDeclaration);
if (
!dynamic_cast<TypeClassDefinition const*>(referencedDeclaration)
)
m_errorReporter.fatalTypeError(0000_error, _identifier.location(), "Expected type class.");
identifierAnnotation.type = helper.kindType(m_typeSystem.freshTypeVariable(false, Sort{{TypeClass{typeClass}}}));
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(&_declaration))
{
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term};
typeClass->accept(*this);
return helper.kindType(m_typeSystem.freshTypeVariable(false, Sort{{TypeClass{typeClass}}}));
}
else
{
m_errorReporter.typeError(0000_error, _location, "Expected type class.");
return helper.kindType(m_typeSystem.freshTypeVariable(false, {}));
}
break;
}
}
util::unreachable();
}
return true;
bool TypeInference::visit(Identifier const& _identifier)
{
auto& identifierAnnotation = annotation(_identifier);
solAssert(!identifierAnnotation.type);
if (auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration)
{
identifierAnnotation.type = handleIdentifierByReferencedDeclaration(_identifier.location(), *referencedDeclaration);
return false;
}
TypeSystemHelpers helper{m_typeSystem};
switch(m_expressionContext)
{
case ExpressionContext::Term:
// TODO: error handling
solAssert(false);
break;
case ExpressionContext::Type:
{
// TODO: register free type variable name!
identifierAnnotation.type = helper.kindType(m_typeSystem.freshTypeVariable(false, {}));
return false;
}
case ExpressionContext::Sort:
// TODO: error handling
solAssert(false);
break;
}
return false;
}
void TypeInference::endVisit(TupleExpression const& _tupleExpression)
@ -575,45 +697,107 @@ void TypeInference::endVisit(TupleExpression const& _tupleExpression)
}
}
bool TypeInference::visit(IdentifierPath const&)
bool TypeInference::visit(IdentifierPath const& _identifierPath)
{
auto& identifierAnnotation = annotation(_identifierPath);
solAssert(!identifierAnnotation.type);
if (auto const* referencedDeclaration = _identifierPath.annotation().referencedDeclaration)
{
identifierAnnotation.type = handleIdentifierByReferencedDeclaration(_identifierPath.location(), *referencedDeclaration);
return false;
}
// TODO: error handling
solAssert(false);
}
bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
{
auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_typeClassInstantiation.typeClass().annotation().referencedDeclaration);
auto& instantiationAnnotation = annotation(_typeClassInstantiation);
if (instantiationAnnotation.type)
return false;
instantiationAnnotation.type = m_voidType;
std::optional<TypeClass> typeClass = std::visit(util::GenericVisitor{
[&](ASTPointer<IdentifierPath> _typeClassName) -> std::optional<TypeClass> {
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_typeClassName->annotation().referencedDeclaration))
{
auto const* typeClassInfo = m_typeSystem.typeClassInfo(TypeClass{typeClass});
if (!typeClassInfo)
{
// visiting the type class will re-visit this instantiation
typeClass->accept(*this);
}
return TypeClass{typeClass};
}
else
{
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class.");
return nullopt;
}
},
[&](Token _token) -> std::optional<TypeClass> {
if (auto typeClass = typeClassFromToken(_token))
return typeClass;
else
{
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Invalid type class name.");
return nullopt;
}
}
}, _typeClassInstantiation.typeClass().name());
if (!typeClass)
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected type class.");
typeClass->accept(*this);
return false;
auto typeConstructor = typeConstructorFromTypeName(_typeClassInstantiation.typeConstructor());
if (!typeConstructor)
{
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor.");
return false;
}
vector<Type> arguments;
Arity arity{
{},
*typeClass
};
{
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
if (_typeClassInstantiation.argumentSorts())
{
_typeClassInstantiation.argumentSorts()->accept(*this);
auto& argumentSortAnnotation = annotation(*_typeClassInstantiation.argumentSorts());
solAssert(argumentSortAnnotation.type);
arguments = TypeSystemHelpers{m_typeSystem}.destTupleType(*argumentSortAnnotation.type);
for (auto& argument: arguments)
argument = TypeSystemHelpers{m_typeSystem}.destKindType(argument);
arity.argumentSorts = arguments | ranges::views::transform([&](Type _type) {
return m_env->sort(_type);
}) | ranges::to<vector<Sort>>;
}
}
Type type{TypeConstant{*typeConstructor, arguments}};
map<string, Type> functionTypes;
Type typeVar = m_typeSystem.freshTypeVariable(false, {});
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()] = m_env->fresh(*functionType, true);
auto typeVars = TypeSystemHelpers{m_typeSystem}.typeVars(functionTypes[functionDefinition->name()]);
solAssert(typeVars.size() == 1);
unify(typeVars.front(), typeVar);
}
for (auto subNode: _typeClassInstantiation.subNodes())
{
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);
solAssert(annotation(*functionDefinition).type);
functionTypes[functionDefinition->name()] = *annotation(*functionDefinition).type;
}
if (auto error = m_typeSystem.instantiateClass(type, arity, std::move(functionTypes)))
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), *error);
/*
TypeEnvironment newEnv = m_env->clone();
for (auto subNode: _typeClassInstantiation.subNodes())
{
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get());
@ -628,10 +812,41 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
}
if (!functionTypes.empty())
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Type class instantiation does not implement all required functions.");
*/
return false;
}
void TypeInference::endVisit(MemberAccess const& _memberAccess)
{
auto &memberAccessAnnotation = annotation(_memberAccess);
solAssert(!memberAccessAnnotation.type);
auto& expressionAnnotation = annotation(_memberAccess.expression());
solAssert(expressionAnnotation.type);
TypeSystemHelpers helper{m_typeSystem};
if (helper.isTypeConstant(*expressionAnnotation.type))
{
Type expressionType = *expressionAnnotation.type;
// TODO: unify this, s.t. this is always or never a kind type.
if (helper.isKindType(expressionType))
expressionType = helper.destKindType(expressionType);
auto constructor = std::get<0>(helper.destTypeConstant(expressionType));
if (auto* typeMember = util::valueOrNullptr(annotation().members.at(constructor), _memberAccess.memberName()))
{
Type type = m_env->fresh(typeMember->type, true);
annotation(_memberAccess).type = type;
return;
}
else
{
m_errorReporter.typeError(0000_error, _memberAccess.memberLocation(), "Member not found.");
annotation(_memberAccess).type = m_typeSystem.freshTypeVariable(false, {});
return;
}
}
m_errorReporter.typeError(0000_error, _memberAccess.expression().location(), "Unsupported member access expression.");
annotation(_memberAccess).type = m_typeSystem.freshTypeVariable(false, {});
}
bool TypeInference::visit(MemberAccess const& _memberAccess)
{
if (m_expressionContext != ExpressionContext::Term)
@ -639,8 +854,10 @@ bool TypeInference::visit(MemberAccess const& _memberAccess)
m_errorReporter.typeError(0000_error, _memberAccess.location(), "Member access outside term context.");
annotation(_memberAccess).type = m_typeSystem.freshTypeVariable(false, {});
return false;
}
return true;
/*
_memberAccess.expression().accept(*this);
if (auto const* identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()))
{
auto const* declaration = identifier->annotation().referencedDeclaration;
@ -675,19 +892,25 @@ bool TypeInference::visit(MemberAccess const& _memberAccess)
else
m_errorReporter.fatalTypeError(0000_error, _memberAccess.location(), "Member access to non-identifier.");
return false;
return false;*/
}
bool TypeInference::visit(TypeDefinition const& _typeDefinition)
{
TypeSystemHelpers helper{m_typeSystem};
auto& typeDefinitionAnnotation = annotation(_typeDefinition);
if (typeDefinitionAnnotation.type)
return false;
std::optional<Type> underlyingType;
if (_typeDefinition.typeExpression())
{
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
_typeDefinition.typeExpression()->accept(*this);
underlyingType = annotation(*_typeDefinition.typeExpression()).type;
// TODO: settle the kind type mess.
if (underlyingType && helper.isKindType(*underlyingType))
underlyingType = helper.destKindType(*underlyingType);
}
vector<Type> arguments;
@ -695,15 +918,24 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition)
for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i)
arguments.emplace_back(m_typeSystem.freshTypeVariable(true, {}));
TypeSystemHelpers helper{m_typeSystem};
Type type = m_typeSystem.type(TypeConstructor{&_typeDefinition}, arguments);
if (arguments.empty())
typeDefinitionAnnotation.type = helper.kindType(m_typeSystem.type(TypeConstructor{&_typeDefinition}, arguments));
else
typeDefinitionAnnotation.type =
helper.functionType(
helper.kindType(helper.tupleType(arguments)),
helper.kindType(m_typeSystem.type(TypeConstructor{&_typeDefinition}, arguments))
helper.kindType(type)
);
auto& typeMembers = annotation().members[&_typeDefinition];
if (underlyingType)
{
typeMembers.emplace("abs", TypeMember{helper.functionType(*underlyingType, type)});
typeMembers.emplace("rep", TypeMember{helper.functionType(type, *underlyingType)});
}
return false;
}
@ -751,7 +983,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
{
Type argTuple = helper.tupleType(argTypes);
Type genericFunctionType = helper.functionType(argTuple, m_typeSystem.freshTypeVariable(false, {}));
unify(genericFunctionType, functionType, _functionCall.location());
unify(functionType, genericFunctionType, _functionCall.location());
functionCallAnnotation.type = m_env->resolve(std::get<1>(helper.destFunctionType(m_env->resolve(genericFunctionType))));
break;
@ -760,7 +992,7 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
{
Type argTuple = helper.kindType(helper.tupleType(argTypes));
Type genericFunctionType = helper.functionType(argTuple, m_typeSystem.freshKindVariable(false, {}));
unify(genericFunctionType, functionType, _functionCall.location());
unify(functionType, genericFunctionType, _functionCall.location());
functionCallAnnotation.type = m_env->resolve(std::get<1>(helper.destFunctionType(m_env->resolve(genericFunctionType))));
break;
@ -768,6 +1000,182 @@ void TypeInference::endVisit(FunctionCall const& _functionCall)
case ExpressionContext::Sort:
solAssert(false);
}
}
// TODO: clean up rational parsing
namespace
{
optional<rational> parseRational(string const& _value)
{
rational value;
try
{
auto radixPoint = find(_value.begin(), _value.end(), '.');
if (radixPoint != _value.end())
{
if (
!all_of(radixPoint + 1, _value.end(), util::isDigit) ||
!all_of(_value.begin(), radixPoint, util::isDigit)
)
return nullopt;
// Only decimal notation allowed here, leading zeros would switch to octal.
auto fractionalBegin = find_if_not(
radixPoint + 1,
_value.end(),
[](char const& a) { return a == '0'; }
);
rational numerator;
rational denominator(1);
denominator = bigint(string(fractionalBegin, _value.end()));
denominator /= boost::multiprecision::pow(
bigint(10),
static_cast<unsigned>(distance(radixPoint + 1, _value.end()))
);
numerator = bigint(string(_value.begin(), radixPoint));
value = numerator + denominator;
}
else
value = bigint(_value);
return value;
}
catch (...)
{
return nullopt;
}
}
/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits.
bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10)
{
double const log2Of10AwayFromZero = 3.3219280948873624;
return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10);
}
optional<rational> rationalValue(Literal const& _literal)
{
rational value;
try
{
ASTString valueString = _literal.valueWithoutUnderscores();
auto expPoint = find(valueString.begin(), valueString.end(), 'e');
if (expPoint == valueString.end())
expPoint = find(valueString.begin(), valueString.end(), 'E');
if (boost::starts_with(valueString, "0x"))
{
// process as hex
value = bigint(valueString);
}
else if (expPoint != valueString.end())
{
// Parse mantissa and exponent. Checks numeric limit.
optional<rational> mantissa = parseRational(string(valueString.begin(), expPoint));
if (!mantissa)
return nullopt;
value = *mantissa;
// 0E... is always zero.
if (value == 0)
return nullopt;
bigint exp = bigint(string(expPoint + 1, valueString.end()));
if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
return nullopt;
uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
if (exp < 0)
{
if (!fitsPrecisionBase10(abs(value.denominator()), expAbs))
return nullopt;
value /= boost::multiprecision::pow(
bigint(10),
expAbs
);
}
else if (exp > 0)
{
if (!fitsPrecisionBase10(abs(value.numerator()), expAbs))
return nullopt;
value *= boost::multiprecision::pow(
bigint(10),
expAbs
);
}
}
else
{
// parse as rational number
optional<rational> tmp = parseRational(valueString);
if (!tmp)
return nullopt;
value = *tmp;
}
}
catch (...)
{
return nullopt;
}
switch (_literal.subDenomination())
{
case Literal::SubDenomination::None:
case Literal::SubDenomination::Wei:
case Literal::SubDenomination::Second:
break;
case Literal::SubDenomination::Gwei:
value *= bigint("1000000000");
break;
case Literal::SubDenomination::Ether:
value *= bigint("1000000000000000000");
break;
case Literal::SubDenomination::Minute:
value *= bigint("60");
break;
case Literal::SubDenomination::Hour:
value *= bigint("3600");
break;
case Literal::SubDenomination::Day:
value *= bigint("86400");
break;
case Literal::SubDenomination::Week:
value *= bigint("604800");
break;
case Literal::SubDenomination::Year:
value *= bigint("31536000");
break;
}
return value;
}
}
bool TypeInference::visit(Literal const& _literal)
{
auto& literalAnnotation = annotation(_literal);
literalAnnotation.type = m_typeSystem.freshTypeVariable(false, {});
if (_literal.token() != Token::Number)
{
m_errorReporter.typeError(0000_error, _literal.location(), "Only number literals are supported.");
return false;
}
optional<rational> value = rationalValue(_literal);
if (!value)
{
m_errorReporter.typeError(0000_error, _literal.location(), "Invalid number literals.");
return false;
}
if (value->denominator() != 1)
{
m_errorReporter.typeError(0000_error, _literal.location(), "Only integers are supported.");
return false;
}
return false;
}

View File

@ -39,8 +39,17 @@ public:
/// Expressions, variable declarations, function declarations.
std::optional<Type> type;
};
struct TypeMember
{
Type type;
};
struct GlobalAnnotation
{
std::map<TypeConstructor, std::map<std::string, TypeMember>> members;
};
bool visit(Block const&) override { return true; }
bool visit(VariableDeclarationStatement const&) override { return true; }
void endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
bool visit(VariableDeclaration const& _variableDeclaration) override;
bool visit(FunctionDefinition const& _functionDefinition) override;
@ -62,6 +71,7 @@ public:
void endVisit(Return const& _return) override;
bool visit(MemberAccess const& _memberAccess) override;
void endVisit(MemberAccess const& _memberAccess) override;
bool visit(ElementaryTypeNameExpression const& _expression) override;
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
@ -73,6 +83,8 @@ public:
bool visitNode(ASTNode const& _node) override;
bool visit(BinaryOperation const& _operation) override;
bool visit(Literal const& _literal) override;
private:
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
@ -85,14 +97,16 @@ private:
std::optional<Type> m_currentFunctionType;
Annotation& annotation(ASTNode const& _node);
GlobalAnnotation& annotation();
void unify(Type _a, Type _b, langutil::SourceLocation _location = {});
void unify(Type _a, Type _b, langutil::SourceLocation _location = {}, TypeEnvironment* _env = nullptr);
enum class ExpressionContext
{
Term,
Type,
Sort
};
Type handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration);
ExpressionContext m_expressionContext = ExpressionContext::Term;
};

View File

@ -19,6 +19,7 @@
#include <libsolidity/analysis/experimental/TypeRegistration.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <liblangutil/Exceptions.h>
#include <libyul/AsmAnalysis.h>
@ -43,6 +44,36 @@ m_typeSystem(_analysis.typeSystem())
{BuiltinType::Integer, "integer", 0}
})
m_typeSystem.declareTypeConstructor(type, name, arity);
auto declareBuiltinClass = [&](BuiltinClass _class, auto _memberCreator, Sort _sort = {}) {
Type type = m_typeSystem.freshTypeVariable(false, std::move(_sort));
auto error = m_typeSystem.declareTypeClass(
TypeClass{_class},
type,
_memberCreator(type)
);
solAssert(!error, *error);
};
TypeSystemHelpers helper{m_typeSystem};
using MemberList = std::map<std::string, Type>;
declareBuiltinClass(BuiltinClass::Integer, [&](Type _typeVar) -> MemberList {
return {
{
"fromInteger",
helper.functionType(TypeConstant{{BuiltinType::Integer}, {}}, _typeVar)
}
};
});
declareBuiltinClass(BuiltinClass::Mul, [&](Type _typeVar) -> MemberList {
return {
{
"mul",
helper.functionType(helper.tupleType({_typeVar, _typeVar}), _typeVar)
}
};
});
annotation().operators[Token::Mul] = std::make_tuple(TypeClass{BuiltinClass::Mul}, "mul");
}
bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
@ -51,90 +82,36 @@ bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
return !m_errorReporter.hasErrors();
}
bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition)
{
if (!m_visitedClasses.insert(_typeClassDefinition.id()).second)
return false;
return false;
}
bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation)
{
auto const* classDefintion = dynamic_cast<TypeClassDefinition const*>(_typeClassInstantiation.typeClass().annotation().referencedDeclaration);
if (!classDefintion)
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class.");
classDefintion->accept(*this);
TypeClass typeClass{classDefintion};
TypeName const& typeName = _typeClassInstantiation.typeConstructor();
TypeConstructor typeConstructor = [&]() -> TypeConstructor {
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&typeName))
{
switch(elementaryTypeName->typeName().token())
{
case Token::Word:
return BuiltinType::Word;
case Token::Void:
return BuiltinType::Void;
case Token::Integer:
return BuiltinType::Integer;
case Token::Pair:
return BuiltinType::Pair;
case Token::Function:
return BuiltinType::Function;
case Token::Unit:
return BuiltinType::Function;
default:
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(), "Only elementary types are supported.");
return BuiltinType::Void;
}
}();
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())
optional<TypeClass> typeClass = typeClassFromTypeClassName(_typeClassInstantiation.typeClass());
if (!typeClass)
{
if (auto const* referencedDeclaration = argumentSort->annotation().referencedDeclaration)
{
if (auto const* typeClassDefinition = dynamic_cast<TypeClassDefinition const*>(referencedDeclaration))
// TODO: multi arities
arity.argumentSorts.emplace_back(Sort{{TypeClass{typeClassDefinition}}});
else
m_errorReporter.fatalTypeError(0000_error, argumentSort->location(), "Argument sort has to be a type class.");
}
else
{
// TODO: error Handling
m_errorReporter.fatalTypeError(0000_error, argumentSort->location(), "Invalid sort.");
}
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class.");
return false;
}
m_typeSystem.instantiateClass(typeConstructor, arity);
optional<TypeConstructor> typeConstructor = typeConstructorFromTypeName(_typeClassInstantiation.typeConstructor());
if (!typeConstructor)
{
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name.");
return false;
}
auto& instantiations = std::visit(util::GenericVisitor{
[&](TypeClassDefinition const* classDefinition) -> auto&
{
return annotation(*classDefinition).instantiations;
},
[&](BuiltinClass _builtinClass) -> auto&
{
return annotation().builtinClassInstantiations[_builtinClass];
}
}, typeClass->declaration);
if (
auto [instantiation, newlyInserted] = annotation(*classDefintion).instantiations.emplace(typeConstructor, &_typeClassInstantiation);
auto [instantiation, newlyInserted] = instantiations.emplace(*typeConstructor, &_typeClassInstantiation);
!newlyInserted
)
{
@ -160,3 +137,9 @@ TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)
{
return m_analysis.annotation<TypeRegistration>(_node);
}
TypeRegistration::GlobalAnnotation& TypeRegistration::annotation()
{
return m_analysis.annotation<TypeRegistration>();
}

View File

@ -30,19 +30,25 @@ class Analysis;
class TypeRegistration: public ASTConstVisitor
{
public:
using TypeClassInstantiations = std::map<TypeConstructor, TypeClassInstantiation const*>;
struct Annotation
{
Type type;
std::map<TypeConstructor, TypeClassInstantiation const*> instantiations;
TypeClassInstantiations instantiations;
};
struct GlobalAnnotation
{
std::map<BuiltinClass, TypeClassInstantiations> builtinClassInstantiations;
std::map<Token, std::tuple<TypeClass, std::string>> operators;
};
TypeRegistration(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);
private:
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
bool visit(TypeDefinition const& _typeDefinition) override;
Annotation& annotation(ASTNode const& _node);
GlobalAnnotation& annotation();
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;

View File

@ -2497,8 +2497,8 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> _typeConstructor,
std::vector<ASTPointer<IdentifierPath>> const& _argumentSorts,
ASTPointer<IdentifierPath> _class,
ASTPointer<ParameterList> _argumentSorts,
ASTPointer<TypeClassName> _class,
std::vector<ASTPointer<ASTNode>> _subNodes
):
ASTNode(_id, _location),
@ -2512,16 +2512,16 @@ public:
void accept(ASTConstVisitor& _visitor) const override;
TypeName const& typeConstructor() const { return *m_typeConstructor; }
std::vector<ASTPointer<IdentifierPath>> const& argumentSorts() const { return m_argumentSorts; }
IdentifierPath const& typeClass() const { return *m_class; }
ParameterList const* argumentSorts() const { return m_argumentSorts.get(); }
TypeClassName const& typeClass() const { return *m_class; }
std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
bool experimentalSolidityOnly() const override { return true; }
private:
ASTPointer<TypeName> m_typeConstructor;
std::vector<ASTPointer<IdentifierPath>> m_argumentSorts;
ASTPointer<IdentifierPath> m_class;
ASTPointer<ParameterList> m_argumentSorts;
ASTPointer<TypeClassName> m_class;
std::vector<ASTPointer<ASTNode>> m_subNodes;
};
@ -2559,6 +2559,31 @@ private:
ASTPointer<Expression> m_typeExpression;
};
class TypeClassName: public ASTNode
{
public:
TypeClassName(
int64_t _id,
SourceLocation const& _location,
std::variant<Token, ASTPointer<IdentifierPath>> _name
):
ASTNode(_id, _location),
m_name(std::move(_name))
{
if (Token const* token = std::get_if<Token>(&_name))
solAssert(TokenTraits::isBuiltinTypeClassName(*token));
}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
bool experimentalSolidityOnly() const override { return true; }
std::variant<Token, ASTPointer<IdentifierPath>> name() const { return m_name; }
private:
std::variant<Token, ASTPointer<IdentifierPath>> m_name;
};
/// @}
}

View File

@ -103,6 +103,8 @@ class StructuredDocumentation;
/// @{
class TypeClassDefinition;
class TypeClassInstantiation;
class TypeClassName;
class TypeDefinition;
/// @}
class VariableScope;

View File

@ -114,6 +114,7 @@ public:
virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); }
virtual bool visit(TypeClassInstantiation& _node) { return visitNode(_node); }
virtual bool visit(TypeDefinition& _node) { return visitNode(_node); }
virtual bool visit(TypeClassName& _node) { return visitNode(_node); }
/// @}
virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); }
@ -176,6 +177,7 @@ public:
virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); }
virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); }
virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); }
virtual void endVisit(TypeClassName& _node) { endVisitNode(_node); }
/// @}
protected:
@ -260,6 +262,7 @@ public:
virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); }
virtual bool visit(TypeClassInstantiation const& _node) { return visitNode(_node); }
virtual bool visit(TypeDefinition const& _node) { return visitNode(_node); }
virtual bool visit(TypeClassName const& _node) { return visitNode(_node); }
/// @}
virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
@ -322,6 +325,7 @@ public:
virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); }
virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(TypeClassName const& _node) { endVisitNode(_node); }
/// @}
protected:

View File

@ -1059,7 +1059,8 @@ void TypeClassInstantiation::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this))
{
m_typeConstructor->accept(_visitor);
listAccept(m_argumentSorts, _visitor);
if(m_argumentSorts)
m_argumentSorts->accept(_visitor);
m_class->accept(_visitor);
listAccept(m_subNodes, _visitor);
}
@ -1071,7 +1072,8 @@ void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this))
{
m_typeConstructor->accept(_visitor);
listAccept(m_argumentSorts, _visitor);
if(m_argumentSorts)
m_argumentSorts->accept(_visitor);
m_class->accept(_visitor);
listAccept(m_subNodes, _visitor);
}
@ -1101,6 +1103,27 @@ void TypeDefinition::accept(ASTConstVisitor& _visitor) const
}
_visitor.endVisit(*this);
}
void TypeClassName::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
if (auto* path = std::get_if<ASTPointer<IdentifierPath>>(&m_name))
(*path)->accept(_visitor);
}
_visitor.endVisit(*this);
}
void TypeClassName::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
if (auto* path = std::get_if<ASTPointer<IdentifierPath>>(&m_name))
(*path)->accept(_visitor);
}
_visitor.endVisit(*this);
}
/// @}
}

View File

@ -29,6 +29,16 @@ using namespace std;
using namespace solidity;
using namespace solidity::frontend::experimental;
bool less<TypeConstructor>::operator()(TypeConstructor const& _lhs, TypeConstructor const& _rhs) const
{
return std::visit(util::GenericVisitor{
[](BuiltinType _left, BuiltinType _right) { return _left < _right; },
[](frontend::Declaration const* _left, frontend::Declaration const* _right) { return _left->id() < _right->id(); },
[](BuiltinType, frontend::Declaration const*) { return true; },
[](frontend::Declaration const*, BuiltinType) { return false; },
}, _lhs, _rhs);
}
bool TypeClass::operator<(TypeClass const& _rhs) const
{
return std::visit(util::GenericVisitor{
@ -61,6 +71,10 @@ string TypeClass::toString() const
return "kind";
case BuiltinClass::Constraint:
return "contraint";
case BuiltinClass::Integer:
return "integer";
case BuiltinClass::Mul:
return "*";
}
solAssert(false);
},

View File

@ -51,7 +51,17 @@ enum class BuiltinType
};
using TypeConstructor = std::variant<BuiltinType, Declaration const*>;
}
namespace std
{
template<>
struct less<solidity::frontend::experimental::TypeConstructor>
{
bool operator()(solidity::frontend::experimental::TypeConstructor const& _lhs, solidity::frontend::experimental::TypeConstructor const& _rhs) const;
};
}
namespace solidity::frontend::experimental
{
struct TypeConstant
{
TypeConstructor constructor;
@ -62,7 +72,9 @@ enum class BuiltinClass
{
Type,
Kind,
Constraint
Constraint,
Integer,
Mul
};
struct TypeClass

View File

@ -382,14 +382,28 @@ void TypeSystem::declareTypeConstructor(TypeConstructor _typeConstructor, std::s
solAssert(newlyInserted, "Type constructor already declared.");
}
void TypeSystem::declareTypeClass(TypeConstructor _classDeclaration, std::string _name)
std::optional<std::string> TypeSystem::declareTypeClass(TypeClass _class, Type _typeVariable, std::map<std::string, Type> _functions)
{
bool newlyInserted = m_typeConstructors.emplace(std::make_pair(_classDeclaration, TypeConstructorInfo{
_name,
{Arity{vector<Sort>{}, TypeClass{BuiltinClass::Kind}}}
})).second;
// TODO: proper error handling.
solAssert(newlyInserted, "Type class already declared.");
TypeVariable const* typeVariable = get_if<TypeVariable>(&_typeVariable);
if (!typeVariable)
return "Invalid type variable.";
for (auto [functionName, functionType]: _functions)
{
auto typeVars = TypeSystemHelpers{*this}.typeVars(functionType);
if (typeVars.empty())
return "Function " + functionName + " does not depend on class variable.";
if (typeVars.size() > 2)
return "Function " + functionName + " depends on multiple type variables.";
if (get<TypeVariable>(typeVars.front()).index() != typeVariable->index())
return "Function " + functionName + " depends on invalid type variable.";
}
if (!m_typeClasses.emplace(std::make_pair(_class, TypeClassInfo{
_typeVariable,
std::move(_functions)
})).second)
return "Type class already declared";
return nullopt;
}
@ -435,10 +449,48 @@ experimental::Type TypeEnvironment::fresh(Type _type, bool _generalize)
return freshImpl(_type, _generalize, freshImpl);
}
void TypeSystem::instantiateClass(TypeConstructor _typeConstructor, Arity _arity)
std::optional<std::string> TypeSystem::instantiateClass(Type _instanceVariable, Arity _arity, map<string, Type> _functionTypes)
{
// TODO: proper error handling
auto& typeConstructorInfo = m_typeConstructors.at(_typeConstructor);
solAssert(_arity.argumentSorts.size() == typeConstructorInfo.arguments(), "Invalid arity.");
if (!TypeSystemHelpers{*this}.isTypeConstant(_instanceVariable))
return "Invalid instance variable.";
auto [typeConstructor, typeArguments] = TypeSystemHelpers{*this}.destTypeConstant(_instanceVariable);
auto& typeConstructorInfo = m_typeConstructors.at(typeConstructor);
if (_arity.argumentSorts.size() != typeConstructorInfo.arguments())
return "Invalid arity.";
if (typeArguments.size() != typeConstructorInfo.arguments())
return "Invalid arity.";
auto const* classInfo = typeClassInfo(_arity.typeClass);
if (!classInfo)
return "Unknown class.";
TypeEnvironment newEnv = m_globalTypeEnvironment.clone();
std::set<size_t> typeVariables;
Type classVariable = classInfo->typeVariable;
if (!newEnv.unify(classVariable, _instanceVariable).empty())
// TODO: error reporting
return "Unification of class and instance variable failed.";
for (auto [name, classFunctionType]: classInfo->functions)
{
if (!_functionTypes.count(name))
return "Missing function: " + name;
Type instanceFunctionType = _functionTypes.at(name);
_functionTypes.erase(name);
if (!newEnv.typeEquals(instanceFunctionType, classFunctionType))
return "Type mismatch for function " + name + " " + newEnv.typeToString(instanceFunctionType) + " != " + newEnv.typeToString(classFunctionType);
}
typeConstructorInfo.arities.emplace_back(_arity);
if (!_functionTypes.empty())
{
// TODO: list function names.
return "Additional functions in class instantiation.";
}
return nullopt;
}

View File

@ -66,6 +66,11 @@ public:
return arities.front().argumentSorts.size();
}
};
struct TypeClassInfo
{
Type typeVariable;
std::map<std::string, Type> functions;
};
TypeSystem();
TypeSystem(TypeSystem const&) = delete;
TypeSystem const& operator=(TypeSystem const&) = delete;
@ -86,9 +91,13 @@ public:
// TODO: error handling
return m_typeConstructors.at(_typeConstructor);
}
TypeClassInfo const* typeClassInfo(TypeClass _class) const
{
return util::valueOrNullptr(m_typeClasses, _class);
}
void declareTypeClass(TypeConstructor _classDeclaration, std::string _name);
void instantiateClass(TypeConstructor _typeConstructor, Arity _arity);
[[nodiscard]] std::optional<std::string> declareTypeClass(TypeClass _class, Type _typeVariable, std::map<std::string, Type> _functions);
[[nodiscard]] std::optional<std::string> instantiateClass(Type _instanceVariable, Arity _arity, std::map<std::string, Type> _functions);
Type freshTypeVariable(bool _generic, Sort _sort);
Type freshKindVariable(bool _generic, Sort _sort);
@ -100,6 +109,7 @@ public:
private:
size_t m_numTypeVariables = 0;
std::map<TypeConstructor, TypeConstructorInfo> m_typeConstructors;
std::map<TypeClass, TypeClassInfo> m_typeClasses;
TypeEnvironment m_globalTypeEnvironment{*this};
};

View File

@ -18,7 +18,7 @@
#include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <libsolidity/ast/AST.h>
#include <libsolutil/Visitor.h>
#include <range/v3/to_container.hpp>
@ -27,9 +27,74 @@
#include <range/v3/view/reverse.hpp>
using namespace std;
using namespace solidity::langutil;
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
std::optional<TypeConstructor> experimental::typeConstructorFromTypeName(TypeName const& _typeName)
{
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
{
if (auto constructor = typeConstructorFromToken(elementaryTypeName->typeName().token()))
return *constructor;
}
else if (auto const* userDefinedType = dynamic_cast<UserDefinedTypeName const*>(&_typeName))
{
if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration)
return referencedDeclaration;
}
return nullopt;
}
std::optional<TypeConstructor> experimental::typeConstructorFromToken(langutil::Token _token)
{
switch(_token)
{
case Token::Void:
return BuiltinType::Void;
case Token::Fun:
return BuiltinType::Function;
case Token::Unit:
return BuiltinType::Unit;
case Token::Pair:
return BuiltinType::Pair;
case Token::Word:
return BuiltinType::Word;
case Token::Integer:
return BuiltinType::Integer;
default:
return nullopt;
}
}
std::optional<TypeClass> experimental::typeClassFromToken(langutil::Token _token)
{
switch (_token)
{
case Token::Integer:
return TypeClass{BuiltinClass::Integer};
case Token::Mul:
return TypeClass{BuiltinClass::Mul};
default:
return nullopt;
}
}
std::optional<TypeClass> experimental::typeClassFromTypeClassName(TypeClassName const& _typeClass)
{
return std::visit(util::GenericVisitor{
[&](ASTPointer<IdentifierPath> _path) -> optional<TypeClass> {
auto classDefinition = dynamic_cast<TypeClassDefinition const*>(_path->annotation().referencedDeclaration);
if (!classDefinition)
return nullopt;
return TypeClass{classDefinition};
},
[&](Token _token) -> optional<TypeClass> {
return typeClassFromToken(_token);
}
}, _typeClass.name());
}
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
{
if (_elements.empty())
@ -127,6 +192,7 @@ bool TypeSystemHelpers::isFunctionType(Type _type) const
vector<experimental::Type> TypeSystemHelpers::typeVars(Type _type) const
{
set<size_t> indices;
vector<Type> typeVars;
auto typeVarsImpl = [&](Type _type, auto _recurse) -> void {
std::visit(util::GenericVisitor{
@ -135,7 +201,8 @@ vector<experimental::Type> TypeSystemHelpers::typeVars(Type _type) const
_recurse(arg, _recurse);
},
[&](TypeVariable const& _var) {
typeVars.emplace_back(_var);
if (indices.emplace(_var.index()).second)
typeVars.emplace_back(_var);
},
// TODO: move to env helpers?
}, typeSystem.env().resolve(_type));

View File

@ -18,10 +18,17 @@
#pragma once
#include <libsolidity/ast/experimental/TypeSystem.h>
#include <libsolidity/ast/ASTForward.h>
#include <liblangutil/Token.h>
namespace solidity::frontend::experimental
{
std::optional<TypeConstructor> typeConstructorFromTypeName(TypeName const& _typeName);
std::optional<TypeConstructor> typeConstructorFromToken(langutil::Token _token);
std::optional<TypeClass> typeClassFromTypeClassName(TypeClassName const& _typeClass);
std::optional<TypeClass> typeClassFromToken(langutil::Token _token);
struct TypeSystemHelpers
{
TypeSystem const& typeSystem;

View File

@ -122,12 +122,16 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly)
bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
{
if (_variableDeclarationStatement.initialValue())
_variableDeclarationStatement.initialValue()->accept(*this);
solAssert(_variableDeclarationStatement.declarations().size() == 1, "multi variable declarations not supported");
solAssert(!_variableDeclarationStatement.initialValue(), "initial values not yet supported");
VariableDeclaration const* variableDeclaration = _variableDeclarationStatement.declarations().front().get();
solAssert(variableDeclaration);
// TODO: check the type of the variable; register local variable; initialize
m_code << "let " << IRNames::localVariable(*variableDeclaration) << "\n";
m_code << "let " << IRNames::localVariable(*variableDeclaration);
if (_variableDeclarationStatement.initialValue())
m_code << " := " << IRNames::localVariable(*_variableDeclarationStatement.initialValue());
m_code << "\n";
return false;
}
@ -138,9 +142,18 @@ bool IRGeneratorForStatements::visit(ExpressionStatement const&)
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
{
auto const* rhsVar = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration);
solAssert(rhsVar, "Can only reference identifiers referring to variables.");
m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*rhsVar) << "\n";
if (auto const* var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
{
m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*var) << "\n";
}
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_identifier.annotation().referencedDeclaration))
solAssert(m_expressionDeclaration.emplace(&_identifier, function).second);
else if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_identifier.annotation().referencedDeclaration))
solAssert(m_expressionDeclaration.emplace(&_identifier, typeClass).second);
else if (auto const* typeDefinition = dynamic_cast<TypeDefinition const*>(_identifier.annotation().referencedDeclaration))
solAssert(m_expressionDeclaration.emplace(&_identifier, typeDefinition).second);
else
solAssert(false, "Unsupported Identifier");
return false;
}
@ -158,51 +171,129 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
m_code << "leave\n";
}
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
experimental::Type IRGeneratorForStatements::type(ASTNode const& _node) const
{
for(auto arg: _functionCall.arguments())
arg->accept(*this);
auto type = m_context.analysis.annotation<TypeInference>(_node).type;
solAssert(type);
return *type;
}
void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation)
{
TypeSystemHelpers helper{m_context.analysis.typeSystem()};
Type leftType = type(_binaryOperation.leftExpression());
Type rightType = type(_binaryOperation.rightExpression());
Type resultType = type(_binaryOperation);
Type functionType = helper.functionType(helper.tupleType({leftType, rightType}), resultType);
auto [typeClass, memberName] = m_context.analysis.annotation<TypeRegistration>().operators.at(_binaryOperation.getOperator());
auto const& functionDefinition = resolveTypeClassFunction(typeClass, memberName, functionType);
// TODO: deduplicate with FunctionCall
// TODO: get around resolveRecursive by passing the environment further down?
functionType = m_context.env->resolveRecursive(functionType);
m_context.enqueueFunctionDefinition(&functionDefinition, functionType);
// TODO: account for return stack size
m_code << "let " << IRNames::localVariable(_binaryOperation) << " := " << IRNames::function(*m_context.env, functionDefinition, functionType) << "("
<< IRNames::localVariable(_binaryOperation.leftExpression()) << ", " << IRNames::localVariable(_binaryOperation.rightExpression()) << ")\n";
}
namespace
{
TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGenerationContext const& _context, TypeClass _class)
{
return std::visit(util::GenericVisitor{
[&](BuiltinClass _builtinClass) -> auto const& {
return _context.analysis.annotation<TypeRegistration>().builtinClassInstantiations.at(_builtinClass);
},
[&](TypeClassDefinition const* _classDefinition) -> auto const& {
return _context.analysis.annotation<TypeRegistration>(*_classDefinition).instantiations;
}
}, _class.declaration);
}
}
FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, string _name, Type _type)
{
TypeSystemHelpers helper{m_context.analysis.typeSystem()};
auto const* typeClassInfo = m_context.analysis.typeSystem().typeClassInfo(_class);
solAssert(typeClassInfo);
Type genericFunctionType = typeClassInfo->functions.at(_name);
TypeEnvironment env = m_context.env->clone();
auto typeVars = helper.typeVars(genericFunctionType);
solAssert(typeVars.size() == 1);
solAssert(env.unify(genericFunctionType, _type).empty());
auto typeClassInstantiation = get<0>(helper.destTypeConstant(env.resolve(typeVars.front())));
auto const& instantiations = typeClassInstantiations(m_context, _class);
TypeClassInstantiation const* instantiation = instantiations.at(typeClassInstantiation);
FunctionDefinition const* functionDefinition = nullptr;
if (auto const* identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
for (auto const& node: instantiation->subNodes())
{
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);
auto typeConstructor = std::get<0>(TypeSystemHelpers{m_context.analysis.typeSystem()}.destTypeConstant(
m_context.env->resolve(*expressionAnnotation.type)
));
auto const* typeClass = dynamic_cast<Identifier const*>(&memberAccess->expression());
solAssert(typeClass, "Function call to member access only supported for type classes.");
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() == _name)
{
auto const* def = dynamic_cast<FunctionDefinition const*>(node.get());
solAssert(def);
if (def->name() == memberAccess->memberName())
{
functionDefinition = def;
break;
}
functionDefinition = def;
break;
}
}
else
solAssert(false, "Complex function call expressions not supported.");
solAssert(functionDefinition);
auto functionType = m_context.analysis.annotation<TypeInference>(_functionCall.expression()).type;
solAssert(functionType);
return *functionDefinition;
}
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
TypeSystemHelpers helper{m_context.analysis.typeSystem()};
auto expressionType = type(_memberAccess.expression());
// TODO: avoid kind destruction
if (helper.isKindType(expressionType))
expressionType = helper.destKindType(expressionType);
auto constructor = std::get<0>(helper.destTypeConstant(expressionType));
auto memberAccessType = type(_memberAccess);
std::visit(util::GenericVisitor{
[](BuiltinType) { solAssert(false); },
[&](Declaration const *_declaration)
{
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_declaration))
solAssert(m_expressionDeclaration.emplace(
&_memberAccess,
&resolveTypeClassFunction(TypeClass{typeClass}, _memberAccess.memberName(), memberAccessType)
).second);
else if (dynamic_cast<TypeDefinition const*>(_declaration))
{
if (_memberAccess.memberName() == "abs" || _memberAccess.memberName() == "rep")
solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::Identity).second);
else
solAssert(false);
}
else
solAssert(false);
}
}, constructor);
}
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{
Type functionType = type(_functionCall.expression());
auto declaration = m_expressionDeclaration.at(&_functionCall.expression());
if (auto builtin = get_if<Builtins>(&declaration))
{
switch(*builtin)
{
case Builtins::Identity:
solAssert(_functionCall.arguments().size() == 1);
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::localVariable(*_functionCall.arguments().front()) << "\n";
return;
}
solAssert(false);
}
FunctionDefinition const* functionDefinition = dynamic_cast<FunctionDefinition const*>(get<Declaration const*>(declaration));
solAssert(functionDefinition);
// TODO: get around resolveRecursive by passing the environment further down?
functionType = m_context.env->resolveRecursive(*functionType);
m_context.enqueueFunctionDefinition(functionDefinition, *functionType);
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*m_context.env, *functionDefinition, *functionType) << "(";
functionType = m_context.env->resolveRecursive(functionType);
m_context.enqueueFunctionDefinition(functionDefinition, functionType);
// TODO: account for return stack size
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*m_context.env, *functionDefinition, functionType) << "(";
auto const& arguments = _functionCall.arguments();
if (arguments.size() > 1)
for (auto arg: arguments | ranges::views::drop_last(1))
@ -210,7 +301,11 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
if (!arguments.empty())
m_code << IRNames::localVariable(*arguments.back());
m_code << ")\n";
return false;
}
bool IRGeneratorForStatements::visit(FunctionCall const&)
{
return true;
}
bool IRGeneratorForStatements::visit(Assignment const& _assignment)

View File

@ -38,8 +38,13 @@ private:
bool visit(ExpressionStatement const& _expressionStatement) override;
bool visit(Assignment const& _assignment) override;
bool visit(Identifier const& _identifier) override;
bool visit(FunctionCall const&) override;
bool visit(FunctionCall const& _functionCall) override;
void endVisit(FunctionCall const& _functionCall) override;
bool visit(MemberAccess const&) override { return true; }
void endVisit(MemberAccess const& _memberAccess) override;
bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(BinaryOperation const&) override { return true; }
void endVisit(BinaryOperation const& _binaryOperation) override;
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
bool visit(Return const&) override { return true; }
void endVisit(Return const& _return) override;
@ -47,6 +52,14 @@ private:
bool visitNode(ASTNode const& _node) override;
IRGenerationContext& m_context;
std::stringstream m_code;
enum class Builtins
{
Identity
};
std::map<Expression const*, std::variant<Declaration const*, Builtins>> m_expressionDeclaration;
Type type(ASTNode const& _node) const;
FunctionDefinition const& resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type);
};
}

View File

@ -1723,7 +1723,7 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
if (m_scanner->currentToken() == Token::Colon)
{
advance();
type = parseExpression();
type = parseBinaryExpression();
nodeFactory.setEndPositionFromNode(type);
}
@ -1791,6 +1791,26 @@ ASTPointer<TypeClassDefinition> Parser::parseTypeClassDefinition()
);
}
ASTPointer<TypeClassName> Parser::parseTypeClassName()
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
std::variant<Token, ASTPointer<IdentifierPath>> name;
if (TokenTraits::isBuiltinTypeClassName(m_scanner->currentToken()))
{
nodeFactory.markEndPosition();
name = m_scanner->currentToken();
advance();
}
else
{
auto identifierPath = parseIdentifierPath();
name = identifierPath;
nodeFactory.setEndPositionFromNode(identifierPath);
}
return nodeFactory.createNode<TypeClassName>(name);
}
ASTPointer<TypeClassInstantiation> Parser::parseTypeClassInstantiation()
{
solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit);
@ -1803,22 +1823,12 @@ ASTPointer<TypeClassInstantiation> Parser::parseTypeClassInstantiation()
// TODO: parseTypeConstructor()
ASTPointer<TypeName> typeConstructor = parseTypeName();
expectToken(Token::Colon);
vector<ASTPointer<IdentifierPath>> argumentSorts;
ASTPointer<ParameterList> argumentSorts;
if (m_scanner->currentToken() == Token::LParen)
{
expectToken(Token::LParen);
if (m_scanner->currentToken() != Token::RParen)
{
argumentSorts.emplace_back(parseIdentifierPath());
while (m_scanner->currentToken() == Token::Comma)
{
expectToken(Token::Comma);
argumentSorts.emplace_back(parseIdentifierPath());
}
}
expectToken(Token::RParen);
argumentSorts = parseParameterList();
}
ASTPointer<IdentifierPath> sort = parseIdentifierPath();
ASTPointer<TypeClassName> typeClassName = parseTypeClassName();
expectToken(Token::LBrace);
while (true)
{
@ -1835,7 +1845,7 @@ ASTPointer<TypeClassInstantiation> Parser::parseTypeClassInstantiation()
return nodeFactory.createNode<TypeClassInstantiation>(
typeConstructor,
argumentSorts,
sort,
typeClassName,
subNodes
);
}

View File

@ -179,6 +179,7 @@ private:
ASTPointer<TypeClassDefinition> parseTypeClassDefinition();
ASTPointer<TypeClassInstantiation> parseTypeClassInstantiation();
ASTPointer<TypeDefinition> parseTypeDefinition();
ASTPointer<TypeClassName> parseTypeClassName();
///@}
///@{

View File

@ -1,58 +1,37 @@
pragma experimental solidity;
class a:A {
function testValue(x:a) -> y:word;
}
class a:B {
function testValue(x:a) -> y:word;
}
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;
}
type uint256 = word;
instantiation uint256 : A {
function testValue(x:uint256) -> y:word {
instantiation uint256: * {
function mul(x, y) -> z {
let a = uint256.rep(x);
let b = uint256.rep(y);
assembly {
y := 21
a := mul(a,b)
}
z = uint256.abs(a);
}
}
instantiation word: * {
function mul(x, y) -> z {
assembly {
z := mul(x,y)
}
}
}
contract C {
fallback() external {
let x : word : A;
let y;
let z: (word, word);
let w: uint256;
let x : word;
assembly {
x := 0x42
x := 0x10
}
z = f(z);
y = f(x);
y = B.testValue(x);
y = A.testValue(w);
let w: uint256 = uint256.abs(x);
w = w * w;
let y : word;
assembly { y := 2 }
y = uint256.rep(w) * y;
assembly {
mstore(0, y)
return(0, 32)