This commit is contained in:
Daniel Kirchner 2023-06-27 00:16:28 +02:00
parent 48b35bd921
commit 8135e28821
14 changed files with 655 additions and 511 deletions

View File

@ -45,7 +45,7 @@ bool DebugWarner::visitNode(ASTNode const& _node)
Type type = *typeInferenceAnnotation.type; Type type = *typeInferenceAnnotation.type;
Sort sort = m_analysis.typeSystem().env().sort(type); Sort sort = m_analysis.typeSystem().env().sort(type);
std::string sortString; std::string sortString;
if (sort.classes.size() != 1 || *sort.classes.begin() != TypeClass{{BuiltinClass::Type}}) if (sort.classes.size() != 1 || *sort.classes.begin() != m_analysis.typeSystem().primitiveClass(PrimitiveClass::Type))
sortString = ":" + TypeSystemHelpers{m_analysis.typeSystem()}.sortToString(m_analysis.typeSystem().env().sort(type)); sortString = ":" + TypeSystemHelpers{m_analysis.typeSystem()}.sortToString(m_analysis.typeSystem().env().sort(type));
m_errorReporter.info( m_errorReporter.info(
0000_error, 0000_error,

View File

@ -44,11 +44,11 @@ m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter()), m_errorReporter(_analysis.errorReporter()),
m_typeSystem(_analysis.typeSystem()) m_typeSystem(_analysis.typeSystem())
{ {
m_voidType = m_typeSystem.type(BuiltinType::Void, {}); m_voidType = m_typeSystem.type(PrimitiveType::Void, {});
m_wordType = m_typeSystem.type(BuiltinType::Word, {}); m_wordType = m_typeSystem.type(PrimitiveType::Word, {});
m_integerType = m_typeSystem.type(BuiltinType::Integer, {}); m_integerType = m_typeSystem.type(PrimitiveType::Integer, {});
m_unitType = m_typeSystem.type(BuiltinType::Unit, {}); m_unitType = m_typeSystem.type(PrimitiveType::Unit, {});
m_boolType = m_typeSystem.type(BuiltinType::Bool, {}); m_boolType = m_typeSystem.type(PrimitiveType::Bool, {});
m_env = &m_typeSystem.env(); m_env = &m_typeSystem.env();
} }
@ -61,17 +61,17 @@ bool TypeInference::analyze(SourceUnit const& _sourceUnit)
bool TypeInference::visit(FunctionDefinition const& _functionDefinition) bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
{ {
solAssert(m_expressionContext == ExpressionContext::Term); solAssert(m_expressionContext == ExpressionContext::Term);
ScopedSaveAndRestore signatureRestore(m_currentFunctionType, nullopt);
auto& functionAnnotation = annotation(_functionDefinition); auto& functionAnnotation = annotation(_functionDefinition);
if (functionAnnotation.type) if (functionAnnotation.type)
return false; return false;
ScopedSaveAndRestore signatureRestore(m_currentFunctionType, nullopt);
_functionDefinition.parameterList().accept(*this); _functionDefinition.parameterList().accept(*this);
if (_functionDefinition.returnParameterList()) if (_functionDefinition.returnParameterList())
_functionDefinition.returnParameterList()->accept(*this); _functionDefinition.returnParameterList()->accept(*this);
auto getListType = [&](ParameterList const* _list) { return _list ? getType(*_list) : m_unitType; }; auto getListType = [&](ParameterList const* _list) { return _list ? getType(*_list) : m_unitType; };
Type functionType = TypeSystemHelpers{m_typeSystem}.functionType( Type functionType = TypeSystemHelpers{m_typeSystem}.functionType(
getListType(&_functionDefinition.parameterList()), getListType(&_functionDefinition.parameterList()),
getListType(_functionDefinition.returnParameterList().get()) getListType(_functionDefinition.returnParameterList().get())
@ -83,21 +83,17 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
_functionDefinition.body().accept(*this); _functionDefinition.body().accept(*this);
functionAnnotation.type = functionType; functionAnnotation.type = functionType;
m_errorReporter.info(0000_error, _functionDefinition.location(), TypeEnvironmentHelpers{*m_env}.typeToString(*functionAnnotation.type));
return false; return false;
} }
void TypeInference::endVisit(Return const& _return) void TypeInference::endVisit(Return const& _return)
{ {
solAssert(m_currentFunctionType); solAssert(m_currentFunctionType);
if (_return.expression())
{
Type returnExpressionType = getType(*_return.expression());
Type functionReturnType = get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType)); Type functionReturnType = get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType));
unify(functionReturnType, returnExpressionType, _return.location()); if (_return.expression())
} unify(functionReturnType, getType(*_return.expression()), _return.location());
else
unify(functionReturnType, m_unitType, _return.location());
} }
void TypeInference::endVisit(ParameterList const& _parameterList) void TypeInference::endVisit(ParameterList const& _parameterList)
@ -115,8 +111,7 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
auto& typeClassAnnotation = annotation(_typeClassDefinition); auto& typeClassAnnotation = annotation(_typeClassDefinition);
if (typeClassAnnotation.type) if (typeClassAnnotation.type)
return false; return false;
m_typeSystem.declareTypeConstructor(&_typeClassDefinition, _typeClassDefinition.name(), 0); typeClassAnnotation.type = type(&_typeClassDefinition, {});
typeClassAnnotation.type = TypeConstant{&_typeClassDefinition, {}};
{ {
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
_typeClassDefinition.typeVariable().accept(*this); _typeClassDefinition.typeVariable().accept(*this);
@ -126,32 +121,36 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition)
Type typeVar = m_typeSystem.freshTypeVariable({}); Type typeVar = m_typeSystem.freshTypeVariable({});
auto& typeMembers = annotation().members[&_typeClassDefinition]; auto& typeMembers = annotation().members[typeConstructor(&_typeClassDefinition)];
for (auto subNode: _typeClassDefinition.subNodes()) for (auto subNode: _typeClassDefinition.subNodes())
{ {
subNode->accept(*this); subNode->accept(*this);
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get()); auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(subNode.get());
solAssert(functionDefinition); solAssert(functionDefinition);
auto functionDefinitionType = annotation(*functionDefinition).type; auto functionType = m_env->fresh(getType(*functionDefinition));
solAssert(functionDefinitionType);
auto functionType = m_env->fresh(*functionDefinitionType);
functionTypes[functionDefinition->name()] = functionType; functionTypes[functionDefinition->name()] = functionType;
auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType);
if(typeVars.size() != 1) if(typeVars.size() != 1)
m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class may only depend on the type class variable.");
unify(typeVars.front(), typeVar); unify(typeVars.front(), typeVar, functionDefinition->location());
if (!typeMembers.emplace(functionDefinition->name(), TypeMember{functionType}).second) if (!typeMembers.emplace(functionDefinition->name(), TypeMember{functionType}).second)
m_errorReporter.fatalTypeError(0000_error, functionDefinition->location(), "Function in type class declared multiple times."); 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))) TypeClass typeClass = std::visit(util::GenericVisitor{
m_errorReporter.fatalTypeError(0000_error, _typeClassDefinition.location(), *error); [](TypeClass _class) -> TypeClass { return _class; },
[&](std::string _error) -> TypeClass {
m_errorReporter.fatalTypeError(0000_error, _typeClassDefinition.location(), _error);
util::unreachable();
}
}, m_typeSystem.declareTypeClass(typeVar, std::move(functionTypes), _typeClassDefinition.name(), &_typeClassDefinition));
unify(getType(_typeClassDefinition.typeVariable()), m_typeSystem.freshTypeVariable({{{&_typeClassDefinition}}}), _typeClassDefinition.location());
unify(getType(_typeClassDefinition.typeVariable()), m_typeSystem.freshTypeVariable({{typeClass}}), _typeClassDefinition.location());
for (auto instantiation: m_analysis.annotation<TypeRegistration>(_typeClassDefinition).instantiations | ranges::views::values) for (auto instantiation: m_analysis.annotation<TypeRegistration>(_typeClassDefinition).instantiations | ranges::views::values)
// TODO: recursion-safety? // TODO: recursion-safety? Order of instantiation?
instantiation->accept(*this); instantiation->accept(*this);
return false; return false;
@ -169,22 +168,21 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
{ {
if (_context == yul::IdentifierContext::NonExternal) if (_context == yul::IdentifierContext::NonExternal)
{ {
// TODO: do we need this?
// Hack until we can disallow any shadowing: If we found an internal reference, // Hack until we can disallow any shadowing: If we found an internal reference,
// clear the external references, so that codegen does not use it. // clear the external references, so that codegen does not use it.
_inlineAssembly.annotation().externalReferences.erase(& _identifier); _inlineAssembly.annotation().externalReferences.erase(& _identifier);
return false; return false;
} }
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); InlineAssemblyAnnotation::ExternalIdentifierInfo* identifierInfo = util::valueOrNullptr(_inlineAssembly.annotation().externalReferences, &_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end()) if (!identifierInfo)
return false; return false;
InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; Declaration const* declaration = identifierInfo->declaration;
Declaration const* declaration = identifierInfo.declaration;
solAssert(!!declaration, ""); solAssert(!!declaration, "");
solAssert(identifierInfo->suffix == "", "");
solAssert(identifierInfo.suffix == "", "");
unify(getType(*declaration), m_wordType, originLocationOf(_identifier)); unify(getType(*declaration), m_wordType, originLocationOf(_identifier));
identifierInfo.valueSize = 1; identifierInfo->valueSize = 1;
return true; return true;
}; };
solAssert(!_inlineAssembly.annotation().analysisInfo, ""); solAssert(!_inlineAssembly.annotation().analysisInfo, "");
@ -203,55 +201,37 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
bool TypeInference::visit(ElementaryTypeNameExpression const& _expression) bool TypeInference::visit(ElementaryTypeNameExpression const& _expression)
{ {
auto& expressionAnnotation = annotation(_expression); auto& expressionAnnotation = annotation(_expression);
solAssert(!expressionAnnotation.type);
if (m_expressionContext != ExpressionContext::Type) if (m_expressionContext != ExpressionContext::Type)
{ {
m_errorReporter.typeError(0000_error, _expression.location(), "Elementary type name expression only supported in type context."); m_errorReporter.typeError(0000_error, _expression.location(), "Elementary type name expression only supported in type context.");
expressionAnnotation.type = m_typeSystem.freshTypeVariable({}); expressionAnnotation.type = m_typeSystem.freshTypeVariable({});
return false; return false;
} }
if (auto constructor = m_analysis.annotation<TypeRegistration>(_expression).typeConstructor)
{
vector<Type> arguments;
std::generate_n(std::back_inserter(arguments), m_typeSystem.constructorInfo(*constructor).arguments(), [&]() {
return m_typeSystem.freshTypeVariable({});
});
if (arguments.empty())
expressionAnnotation.type = m_typeSystem.type(*constructor, arguments);
else
{
TypeSystemHelpers helper{m_typeSystem}; TypeSystemHelpers helper{m_typeSystem};
switch(_expression.type().typeName().token())
{
case Token::Word:
expressionAnnotation.type = m_wordType;
break;
case Token::Void:
expressionAnnotation.type = m_voidType;
break;
case Token::Integer:
expressionAnnotation.type = m_integerType;
break;
case Token::Unit:
expressionAnnotation.type = m_unitType;
break;
case Token::Bool:
expressionAnnotation.type = m_boolType;
break;
case Token::Pair:
{
auto leftType = m_typeSystem.freshTypeVariable({});
auto rightType = m_typeSystem.freshTypeVariable({});
expressionAnnotation.type = expressionAnnotation.type =
helper.typeFunctionType( helper.typeFunctionType(
helper.tupleType({leftType, rightType}), helper.tupleType(arguments),
m_typeSystem.type(BuiltinType::Pair, {leftType, rightType}) m_typeSystem.type(*constructor, arguments)
); );
break;
} }
case Token::Fun: }
else
{ {
auto argType = m_typeSystem.freshTypeVariable({}); m_errorReporter.typeError(0000_error, _expression.location(), "No type constructor registered for elementary type name.");
auto resultType = m_typeSystem.freshTypeVariable({});
expressionAnnotation.type = helper.typeFunctionType(
helper.tupleType({argType, resultType}),
m_typeSystem.type(BuiltinType::Function, {argType, resultType})
);
break;
}
default:
m_errorReporter.typeError(0000_error, _expression.location(), "Only elementary types are supported.");
expressionAnnotation.type = m_typeSystem.freshTypeVariable({}); expressionAnnotation.type = m_typeSystem.freshTypeVariable({});
break;
} }
return false; return false;
} }
@ -259,6 +239,7 @@ bool TypeInference::visit(ElementaryTypeNameExpression const& _expression)
bool TypeInference::visit(BinaryOperation const& _binaryOperation) bool TypeInference::visit(BinaryOperation const& _binaryOperation)
{ {
auto& operationAnnotation = annotation(_binaryOperation); auto& operationAnnotation = annotation(_binaryOperation);
solAssert(!operationAnnotation.type);
TypeSystemHelpers helper{m_typeSystem}; TypeSystemHelpers helper{m_typeSystem};
switch (m_expressionContext) switch (m_expressionContext)
{ {
@ -266,14 +247,15 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation)
if (auto* operatorInfo = util::valueOrNullptr(m_analysis.annotation<TypeRegistration>().operators, _binaryOperation.getOperator())) if (auto* operatorInfo = util::valueOrNullptr(m_analysis.annotation<TypeRegistration>().operators, _binaryOperation.getOperator()))
{ {
auto [typeClass, functionName] = *operatorInfo; auto [typeClass, functionName] = *operatorInfo;
Type functionType = m_env->fresh(m_typeSystem.typeClassInfo(typeClass)->functions.at(functionName)); optional<Type> functionType = m_env->typeClassFunction(typeClass, functionName);
solAssert(functionType);
_binaryOperation.leftExpression().accept(*this); _binaryOperation.leftExpression().accept(*this);
_binaryOperation.rightExpression().accept(*this); _binaryOperation.rightExpression().accept(*this);
Type argTuple = helper.tupleType({getType(_binaryOperation.leftExpression()), getType(_binaryOperation.rightExpression())}); Type argTuple = helper.tupleType({getType(_binaryOperation.leftExpression()), getType(_binaryOperation.rightExpression())});
Type genericFunctionType = helper.functionType(argTuple, m_typeSystem.freshTypeVariable({})); Type genericFunctionType = helper.functionType(argTuple, m_typeSystem.freshTypeVariable({}));
unify(functionType, genericFunctionType, _binaryOperation.location()); unify(*functionType, genericFunctionType, _binaryOperation.location());
operationAnnotation.type = m_env->resolve(std::get<1>(helper.destFunctionType(m_env->resolve(genericFunctionType)))); operationAnnotation.type = m_env->resolve(std::get<1>(helper.destFunctionType(m_env->resolve(genericFunctionType))));
@ -281,7 +263,7 @@ bool TypeInference::visit(BinaryOperation const& _binaryOperation)
} }
else else
{ {
m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operations in term context not yet supported."); m_errorReporter.typeError(0000_error, _binaryOperation.location(), "Binary operation in term context not yet supported.");
operationAnnotation.type = m_typeSystem.freshTypeVariable({}); operationAnnotation.type = m_typeSystem.freshTypeVariable({});
return false; return false;
} }
@ -430,7 +412,7 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut
m_errorReporter.fatalTypeError(0000_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); 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. // TODO: Assert that this is a type class variable declaration?
auto& declarationAnnotation = annotation(_declaration); auto& declarationAnnotation = annotation(_declaration);
if (!declarationAnnotation.type) if (!declarationAnnotation.type)
_declaration.accept(*this); _declaration.accept(*this);
@ -451,7 +433,12 @@ experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langut
{ {
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term};
typeClass->accept(*this); typeClass->accept(*this);
return m_typeSystem.freshTypeVariable(Sort{{TypeClass{typeClass}}}); if (!annotation(*typeClass).typeClass)
{
m_errorReporter.typeError(0000_error, _location, "Unregistered type class.");
return m_typeSystem.freshTypeVariable({});
}
return m_typeSystem.freshTypeVariable(Sort{{*annotation(*typeClass).typeClass}});
} }
else else
{ {
@ -539,7 +526,8 @@ bool TypeInference::visit(IdentifierPath const& _identifierPath)
bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
{ {
// TODO: Deal with dependencies between type class instantiations. ScopedSaveAndRestore activeInstantiations{m_activeInstantiations, m_activeInstantiations + set<TypeClassInstantiation const*>{&_typeClassInstantiation}};
// Note: recursion is resolved due to special handling during unification.
auto& instantiationAnnotation = annotation(_typeClassInstantiation); auto& instantiationAnnotation = annotation(_typeClassInstantiation);
if (instantiationAnnotation.type) if (instantiationAnnotation.type)
return false; return false;
@ -547,14 +535,11 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
std::optional<TypeClass> typeClass = std::visit(util::GenericVisitor{ std::optional<TypeClass> typeClass = std::visit(util::GenericVisitor{
[&](ASTPointer<IdentifierPath> _typeClassName) -> std::optional<TypeClass> { [&](ASTPointer<IdentifierPath> _typeClassName) -> std::optional<TypeClass> {
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_typeClassName->annotation().referencedDeclaration)) 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 // visiting the type class will re-visit this instantiation
typeClass->accept(*this); typeClass->accept(*this);
} // TODO: more error handling? Should be covered by the visit above.
return TypeClass{typeClass}; return annotation(*typeClass).typeClass;
} }
else else
{ {
@ -563,19 +548,18 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation)
} }
}, },
[&](Token _token) -> std::optional<TypeClass> { [&](Token _token) -> std::optional<TypeClass> {
if (auto typeClass = typeClassFromToken(_token)) if (auto builtinClass = builtinClassFromToken(_token))
return typeClass; if (auto typeClass = util::valueOrNullptr(m_analysis.annotation<TypeRegistration>().builtinClasses, *builtinClass))
else return *typeClass;
{
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Invalid type class name."); m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), "Invalid type class name.");
return nullopt; return nullopt;
} }
}
}, _typeClassInstantiation.typeClass().name()); }, _typeClassInstantiation.typeClass().name());
if (!typeClass) if (!typeClass)
return false; return false;
auto typeConstructor = typeConstructorFromTypeName(_typeClassInstantiation.typeConstructor()); // TODO: _typeClassInstantiation.typeConstructor().accept(*this); ?
auto typeConstructor = m_analysis.annotation<TypeRegistration>(_typeClassInstantiation.typeConstructor()).typeConstructor;
if (!typeConstructor) if (!typeConstructor)
{ {
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor.");
@ -680,21 +664,20 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition)
vector<Type> arguments; vector<Type> arguments;
if (_typeDefinition.arguments()) if (_typeDefinition.arguments())
for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i)
// TODO: GENERALIZE?
arguments.emplace_back(m_typeSystem.freshTypeVariable({})); arguments.emplace_back(m_typeSystem.freshTypeVariable({}));
Type type = m_typeSystem.type(TypeConstructor{&_typeDefinition}, arguments); Type definedType = type(&_typeDefinition, arguments);
if (arguments.empty()) if (arguments.empty())
typeDefinitionAnnotation.type = m_typeSystem.type(TypeConstructor{&_typeDefinition}, arguments); typeDefinitionAnnotation.type = definedType;
else else
typeDefinitionAnnotation.type = helper.typeFunctionType(helper.tupleType(arguments), type); typeDefinitionAnnotation.type = helper.typeFunctionType(helper.tupleType(arguments), definedType);
auto [members, newlyInserted] = annotation().members.emplace(&_typeDefinition, map<string, TypeMember>{}); auto [members, newlyInserted] = annotation().members.emplace(typeConstructor(&_typeDefinition), map<string, TypeMember>{});
solAssert(newlyInserted); solAssert(newlyInserted);
if (underlyingType) if (underlyingType)
{ {
members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, type)}); members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)});
members->second.emplace("rep", TypeMember{helper.functionType(type, *underlyingType)}); members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)});
} }
return false; return false;
} }
@ -920,17 +903,89 @@ bool TypeInference::visit(Literal const& _literal)
m_errorReporter.typeError(0000_error, _literal.location(), "Only integers are supported."); m_errorReporter.typeError(0000_error, _literal.location(), "Only integers are supported.");
return false; return false;
} }
literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{TypeClass{BuiltinClass::Integer}}}); literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{m_analysis.annotation<TypeRegistration>().builtinClasses.at(BuiltinClass::Integer)}});
return false; return false;
} }
namespace
{
// TODO: put at a nice place to deduplicate.
TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(Analysis const& _analysis, TypeClass _class)
{
auto const* typeClassDeclaration = _analysis.typeSystem().typeClassDeclaration(_class);
if (typeClassDeclaration)
return _analysis.annotation<TypeRegistration>(*typeClassDeclaration).instantiations;
// TODO: better mechanism than fetching by name.
auto& annotation = _analysis.annotation<TypeRegistration>();
return annotation.builtinClassInstantiations.at(annotation.builtinClassesByName.at(_analysis.typeSystem().typeClassName(_class)));
}
}
void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location, TypeEnvironment* _env) void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location, TypeEnvironment* _env)
{ {
TypeSystemHelpers helper{m_typeSystem};
if (!_env) if (!_env)
_env = m_env; _env = m_env;
for (auto failure: _env->unify(_a, _b)) auto unificationFailures = _env->unify(_a, _b);
if (!m_activeInstantiations.empty())
{ {
TypeEnvironmentHelpers helper{*_env}; // Attempt to resolve interdependencies between type class instantiations.
std::vector<TypeClassInstantiation const*> missingInstantiations;
bool recursion = false;
bool onlyMissingInstantiations = [&]() {
for (auto failure: unificationFailures)
{
if (auto* sortMismatch = get_if<TypeEnvironment::SortMismatch>(&failure))
if (helper.isTypeConstant(sortMismatch->type))
{
TypeConstructor constructor = std::get<0>(helper.destTypeConstant(sortMismatch->type));
for (auto typeClass: sortMismatch->sort.classes)
{
if (auto const* instantiation = util::valueOrDefault(typeClassInstantiations(m_analysis, typeClass), constructor, nullptr))
{
if (m_activeInstantiations.count(instantiation))
{
langutil::SecondarySourceLocation ssl;
for (auto activeInstantiation: m_activeInstantiations)
ssl.append("Involved instantiation", activeInstantiation->location());
m_errorReporter.typeError(
0000_error,
_location,
ssl,
"Recursion during type class instantiation."
);
recursion = true;
return false;
}
missingInstantiations.emplace_back(instantiation);
}
else
return false;
}
continue;
}
return false;
}
return true;
}();
if (recursion)
return;
if (onlyMissingInstantiations)
{
for (auto instantiation: missingInstantiations)
instantiation->accept(*this);
unificationFailures = _env->unify(_a, _b);
}
}
for (auto failure: unificationFailures)
{
TypeEnvironmentHelpers envHelper{*_env};
std::visit(util::GenericVisitor{ std::visit(util::GenericVisitor{
[&](TypeEnvironment::TypeMismatch _typeMismatch) { [&](TypeEnvironment::TypeMismatch _typeMismatch) {
m_errorReporter.typeError( m_errorReporter.typeError(
@ -938,16 +993,26 @@ void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location,
_location, _location,
fmt::format( fmt::format(
"Cannot unify {} and {}.", "Cannot unify {} and {}.",
helper.typeToString(_typeMismatch.a), envHelper.typeToString(_typeMismatch.a),
helper.typeToString(_typeMismatch.b)) envHelper.typeToString(_typeMismatch.b))
); );
}, },
[&](TypeEnvironment::SortMismatch _sortMismatch) { [&](TypeEnvironment::SortMismatch _sortMismatch) {
m_errorReporter.typeError(0000_error, _location, fmt::format( m_errorReporter.typeError(0000_error, _location, fmt::format(
"{} does not have sort {}", "{} does not have sort {}",
helper.typeToString(_sortMismatch.type), envHelper.typeToString(_sortMismatch.type),
TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort) TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort)
)); ));
},
[&](TypeEnvironment::RecursiveUnification _recursiveUnification) {
m_errorReporter.typeError(
0000_error,
_location,
fmt::format(
"Recursive unification: {} occurs in {}.",
envHelper.typeToString(_recursiveUnification.var),
envHelper.typeToString(_recursiveUnification.type))
);
} }
}, failure); }, failure);
} }
@ -959,6 +1024,17 @@ experimental::Type TypeInference::getType(ASTNode const& _node) const
solAssert(result); solAssert(result);
return *result; return *result;
} }
TypeConstructor TypeInference::typeConstructor(Declaration const* _type) const
{
if (auto const& constructor = m_analysis.annotation<TypeRegistration>(*_type).typeConstructor)
return *constructor;
m_errorReporter.fatalTypeError(0000_error, _type->location(), "Unregistered type.");
util::unreachable();
}
experimental::Type TypeInference::type(Declaration const* _type, vector<Type> _arguments) const
{
return m_typeSystem.type(typeConstructor(_type), std::move(_arguments));
}
bool TypeInference::visitNode(ASTNode const& _node) bool TypeInference::visitNode(ASTNode const& _node)
{ {

View File

@ -38,6 +38,8 @@ public:
{ {
/// Expressions, variable declarations, function declarations. /// Expressions, variable declarations, function declarations.
std::optional<Type> type; std::optional<Type> type;
// Type classes.
std::optional<TypeClass> typeClass;
}; };
struct TypeMember struct TypeMember
{ {
@ -106,7 +108,10 @@ private:
void unify(Type _a, Type _b, langutil::SourceLocation _location = {}, TypeEnvironment* _env = nullptr); void unify(Type _a, Type _b, langutil::SourceLocation _location = {}, TypeEnvironment* _env = nullptr);
enum class ExpressionContext { Term, Type, Sort }; enum class ExpressionContext { Term, Type, Sort };
Type handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration); Type handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration);
TypeConstructor typeConstructor(Declaration const* _type) const;
Type type(Declaration const* _type, std::vector<Type> _arguments) const;
ExpressionContext m_expressionContext = ExpressionContext::Term; ExpressionContext m_expressionContext = ExpressionContext::Term;
std::set<TypeClassInstantiation const*, ASTCompareByID<TypeClassInstantiation>> m_activeInstantiations;
}; };
} }

View File

@ -36,68 +36,63 @@ m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter()), m_errorReporter(_analysis.errorReporter()),
m_typeSystem(_analysis.typeSystem()) m_typeSystem(_analysis.typeSystem())
{ {
for (auto [type, name, arity]: std::initializer_list<std::tuple<BuiltinType, const char*, uint64_t>> { // TODO: move builtin class declarations to TypeInference
{BuiltinType::Void, "void", 0}, auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class, auto _memberCreator, Sort _sort = {}) -> TypeClass {
{BuiltinType::Unit, "unit", 0},
{BuiltinType::Pair, "pair", 2},
{BuiltinType::Word, "word", 0},
{BuiltinType::Integer, "integer", 0},
{BuiltinType::Bool, "bool", 0}
})
m_typeSystem.declareTypeConstructor(type, name, arity);
auto declareBuiltinClass = [&](BuiltinClass _class, auto _memberCreator, Sort _sort = {}) {
Type type = m_typeSystem.freshTypeVariable(std::move(_sort)); Type type = m_typeSystem.freshTypeVariable(std::move(_sort));
auto error = m_typeSystem.declareTypeClass( auto result = m_typeSystem.declareTypeClass(
TypeClass{_class},
type, type,
_memberCreator(type) _memberCreator(type),
_name,
nullptr
); );
if (auto error = get_if<string>(&result))
solAssert(!error, *error); solAssert(!error, *error);
solAssert(annotation().builtinClassesByName.emplace(_name, _class).second);
return annotation().builtinClasses.emplace(_class, get<TypeClass>(result)).first->second;
}; };
TypeSystemHelpers helper{m_typeSystem}; TypeSystemHelpers helper{m_typeSystem};
using MemberList = std::map<std::string, Type>; using MemberList = std::map<std::string, Type>;
declareBuiltinClass(BuiltinClass::Integer, [&](Type _typeVar) -> MemberList { declareBuiltinClass("integer", BuiltinClass::Integer, [&](Type _typeVar) -> MemberList {
return { return {
{ {
"fromInteger", "fromInteger",
helper.functionType(TypeConstant{{BuiltinType::Integer}, {}}, _typeVar) helper.functionType(m_typeSystem.type(PrimitiveType::Integer, {}), _typeVar)
} }
}; };
}); });
auto defineBinaryMonoidalOperator = [&](BuiltinClass _class, Token _token, std::string _name) { auto defineBinaryMonoidalOperator = [&](std::string _className, BuiltinClass _class, Token _token, std::string _functionName) {
declareBuiltinClass(_class, [&](Type _typeVar) -> MemberList { TypeClass typeClass = declareBuiltinClass(_className, _class, [&](Type _typeVar) -> MemberList {
return { return {
{ {
_name, _functionName,
helper.functionType(helper.tupleType({_typeVar, _typeVar}), _typeVar) helper.functionType(helper.tupleType({_typeVar, _typeVar}), _typeVar)
} }
}; };
}); });
annotation().operators[_token] = std::make_tuple(TypeClass{_class}, _name); annotation().operators.emplace(_token, std::make_tuple(typeClass, _functionName));
}; };
defineBinaryMonoidalOperator(BuiltinClass::Mul, Token::Mul, "mul"); defineBinaryMonoidalOperator("*", BuiltinClass::Mul, Token::Mul, "mul");
defineBinaryMonoidalOperator(BuiltinClass::Add, Token::Add, "add"); defineBinaryMonoidalOperator("+", BuiltinClass::Add, Token::Add, "add");
auto defineBinaryCompareOperator = [&](BuiltinClass _class, Token _token, std::string _name) { auto defineBinaryCompareOperator = [&](std::string _className, BuiltinClass _class, Token _token, std::string _functionName) {
declareBuiltinClass(_class, [&](Type _typeVar) -> MemberList { TypeClass typeClass = declareBuiltinClass(_className, _class, [&](Type _typeVar) -> MemberList {
return { return {
{ {
_name, _functionName,
helper.functionType(helper.tupleType({_typeVar, _typeVar}), TypeConstant{BuiltinType::Bool, {}}) helper.functionType(helper.tupleType({_typeVar, _typeVar}), m_typeSystem.type(PrimitiveType::Bool, {}))
} }
}; };
}); });
annotation().operators[_token] = std::make_tuple(TypeClass{_class}, _name); annotation().operators.emplace(_token, std::make_tuple(typeClass, _functionName));
}; };
defineBinaryCompareOperator(BuiltinClass::Equal, Token::Equal, "eq"); defineBinaryCompareOperator("==", BuiltinClass::Equal, Token::Equal, "eq");
defineBinaryCompareOperator(BuiltinClass::Less, Token::LessThan, "lt"); defineBinaryCompareOperator("<", BuiltinClass::Less, Token::LessThan, "lt");
defineBinaryCompareOperator(BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq"); defineBinaryCompareOperator("<=", BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq");
defineBinaryCompareOperator(BuiltinClass::Greater, Token::GreaterThan, "gt"); defineBinaryCompareOperator(">", BuiltinClass::Greater, Token::GreaterThan, "gt");
defineBinaryCompareOperator(BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq"); defineBinaryCompareOperator(">=", BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq");
} }
bool TypeRegistration::analyze(SourceUnit const& _sourceUnit) bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
@ -106,36 +101,115 @@ bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
return !m_errorReporter.hasErrors(); return !m_errorReporter.hasErrors();
} }
bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation) bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition)
{ {
optional<TypeClass> typeClass = typeClassFromTypeClassName(_typeClassInstantiation.typeClass()); if (annotation(_typeClassDefinition).typeConstructor)
if (!typeClass) return false;
annotation(_typeClassDefinition).typeConstructor = m_typeSystem.declareTypeConstructor(
_typeClassDefinition.name(),
"t_" + *_typeClassDefinition.annotation().canonicalName + "_" + util::toString(_typeClassDefinition.id()),
0,
&_typeClassDefinition
);
return true;
}
bool TypeRegistration::visit(ElementaryTypeName const& _typeName)
{
if (annotation(_typeName).typeConstructor)
return false;
annotation(_typeName).typeConstructor = [&]() -> optional<TypeConstructor> {
switch(_typeName.typeName().token())
{ {
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); case Token::Void:
return m_typeSystem.constructor(PrimitiveType::Void);
case Token::Fun:
return m_typeSystem.constructor(PrimitiveType::Function);
case Token::Unit:
return m_typeSystem.constructor(PrimitiveType::Unit);
case Token::Pair:
return m_typeSystem.constructor(PrimitiveType::Pair);
case Token::Word:
return m_typeSystem.constructor(PrimitiveType::Word);
case Token::Integer:
return m_typeSystem.constructor(PrimitiveType::Integer);
case Token::Bool:
return m_typeSystem.constructor(PrimitiveType::Bool);
default:
m_errorReporter.fatalTypeError(0000_error, _typeName.location(), "Expected primitive type.");
return nullopt;
}
}();
return true;
}
void TypeRegistration::endVisit(ElementaryTypeNameExpression const & _typeNameExpression)
{
if (annotation(_typeNameExpression).typeConstructor)
return;
// TODO: this is not visited in the ElementaryTypeNameExpression visit - is that intentional?
_typeNameExpression.type().accept(*this);
if (auto constructor = annotation(_typeNameExpression.type()).typeConstructor)
annotation(_typeNameExpression).typeConstructor = constructor;
else
solAssert(m_errorReporter.hasErrors());
}
bool TypeRegistration::visit(UserDefinedTypeName const& _userDefinedTypeName)
{
if (annotation(_userDefinedTypeName).typeConstructor)
return false;
auto const* declaration = _userDefinedTypeName.pathNode().annotation().referencedDeclaration;
if (!declaration)
{
// TODO: fatal/non-fatal
m_errorReporter.fatalTypeError(0000_error, _userDefinedTypeName.pathNode().location(), "Expected declaration.");
return false; return false;
} }
declaration->accept(*this);
if (!(annotation(_userDefinedTypeName).typeConstructor = annotation(*declaration).typeConstructor))
{
// TODO: fatal/non-fatal
m_errorReporter.fatalTypeError(0000_error, _userDefinedTypeName.pathNode().location(), "Expected type declaration.");
return false;
}
return true;
}
optional<TypeConstructor> typeConstructor = typeConstructorFromTypeName(_typeClassInstantiation.typeConstructor()); bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation)
{
if (annotation(_typeClassInstantiation).typeConstructor)
return false;
_typeClassInstantiation.typeConstructor().accept(*this);
auto typeConstructor = annotation(_typeClassInstantiation.typeConstructor()).typeConstructor;
if (!typeConstructor) if (!typeConstructor)
{ {
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name."); m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name.");
return false; return false;
} }
auto* instantiations = std::visit(util::GenericVisitor{
auto& instantiations = std::visit(util::GenericVisitor{ [&](ASTPointer<IdentifierPath> _path) -> TypeClassInstantiations*
[&](TypeClassDefinition const* classDefinition) -> auto&
{ {
return annotation(*classDefinition).instantiations; if (TypeClassDefinition const* classDefinition = dynamic_cast<TypeClassDefinition const*>(_path->annotation().referencedDeclaration))
return &annotation(*classDefinition).instantiations;
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class.");
return nullptr;
}, },
[&](BuiltinClass _builtinClass) -> auto& [&](Token _token) -> TypeClassInstantiations*
{ {
return annotation().builtinClassInstantiations[_builtinClass]; if (auto typeClass = builtinClassFromToken(_token))
return &annotation().builtinClassInstantiations[*typeClass];
m_errorReporter.typeError(0000_error, _typeClassInstantiation.typeClass().location(), "Expected a type class.");
return nullptr;
} }
}, typeClass->declaration); }, _typeClassInstantiation.typeClass().name());
if (!instantiations)
return false;
if ( if (
auto [instantiation, newlyInserted] = instantiations.emplace(*typeConstructor, &_typeClassInstantiation); auto [instantiation, newlyInserted] = instantiations->emplace(*typeConstructor, &_typeClassInstantiation);
!newlyInserted !newlyInserted
) )
{ {
@ -144,17 +218,20 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati
m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); m_errorReporter.typeError(0000_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation.");
} }
return false; return true;
} }
bool TypeRegistration::visit(TypeDefinition const& _typeDefinition) bool TypeRegistration::visit(TypeDefinition const& _typeDefinition)
{ {
m_typeSystem.declareTypeConstructor( if (annotation(_typeDefinition).typeConstructor)
TypeConstructor{&_typeDefinition},
_typeDefinition.name(),
_typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0
);
return false; return false;
annotation(_typeDefinition).typeConstructor = m_typeSystem.declareTypeConstructor(
_typeDefinition.name(),
"t_" + *_typeDefinition.annotation().canonicalName + "_" + util::toString(_typeDefinition.id()),
_typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0,
&_typeDefinition
);
return true;
} }
TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node) TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)
@ -162,7 +239,6 @@ TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)
return m_analysis.annotation<TypeRegistration>(_node); return m_analysis.annotation<TypeRegistration>(_node);
} }
TypeRegistration::GlobalAnnotation& TypeRegistration::annotation() TypeRegistration::GlobalAnnotation& TypeRegistration::annotation()
{ {
return m_analysis.annotation<TypeRegistration>(); return m_analysis.annotation<TypeRegistration>();

View File

@ -27,17 +27,34 @@ namespace solidity::frontend::experimental
class Analysis; class Analysis;
enum class BuiltinClass
{
Integer,
Mul,
Add,
Equal,
Less,
LessOrEqual,
Greater,
GreaterOrEqual
};
class TypeRegistration: public ASTConstVisitor class TypeRegistration: public ASTConstVisitor
{ {
public: public:
using TypeClassInstantiations = std::map<TypeConstructor, TypeClassInstantiation const*>; using TypeClassInstantiations = std::map<TypeConstructor, TypeClassInstantiation const*>;
struct Annotation struct Annotation
{ {
Type type; // For type class definititions.
TypeClassInstantiations instantiations; TypeClassInstantiations instantiations;
// For type definitions, type class definitions, type names and type name expressions.
std::optional<TypeConstructor> typeConstructor;
}; };
struct GlobalAnnotation struct GlobalAnnotation
{ {
std::map<BuiltinClass, TypeClass> builtinClasses;
std::map<std::string, BuiltinClass> builtinClassesByName;
std::map<PrimitiveClass, TypeClassInstantiations> primitiveClassInstantiations;
std::map<BuiltinClass, TypeClassInstantiations> builtinClassInstantiations; std::map<BuiltinClass, TypeClassInstantiations> builtinClassInstantiations;
std::map<Token, std::tuple<TypeClass, std::string>> operators; std::map<Token, std::tuple<TypeClass, std::string>> operators;
}; };
@ -45,8 +62,12 @@ public:
bool analyze(SourceUnit const& _sourceUnit); bool analyze(SourceUnit const& _sourceUnit);
private: private:
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override; bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
bool visit(TypeDefinition const& _typeDefinition) override; bool visit(TypeDefinition const& _typeDefinition) override;
bool visit(UserDefinedTypeName const& _typeName) override;
void endVisit(ElementaryTypeNameExpression const& _typeName) override;
bool visit(ElementaryTypeName const& _typeName) override;
Annotation& annotation(ASTNode const& _node); Annotation& annotation(ASTNode const& _node);
GlobalAnnotation& annotation(); GlobalAnnotation& annotation();

View File

@ -29,69 +29,6 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::frontend::experimental; 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{
[](BuiltinClass _left, BuiltinClass _right) { return _left < _right; },
[](TypeClassDefinition const* _left, TypeClassDefinition const* _right) { return _left->id() < _right->id(); },
[](BuiltinClass, TypeClassDefinition const*) { return true; },
[](TypeClassDefinition const*, BuiltinClass) { return false; },
}, declaration, _rhs.declaration);
}
bool TypeClass::operator==(TypeClass const& _rhs) const
{
return std::visit(util::GenericVisitor{
[](BuiltinClass _left, BuiltinClass _right) { return _left == _right; },
[](TypeClassDefinition const* _left, TypeClassDefinition const* _right) { return _left->id() == _right->id(); },
[](BuiltinClass, TypeClassDefinition const*) { return false; },
[](TypeClassDefinition const*, BuiltinClass) { return false; },
}, declaration, _rhs.declaration);
}
string TypeClass::toString() const
{
return std::visit(util::GenericVisitor{
[](BuiltinClass _class) -> string {
switch(_class)
{
case BuiltinClass::Type:
return "type";
case BuiltinClass::Kind:
return "kind";
case BuiltinClass::Integer:
return "integer";
case BuiltinClass::Mul:
return "*";
case BuiltinClass::Add:
return "+";
case BuiltinClass::Equal:
return "==";
case BuiltinClass::Less:
return "<";
case BuiltinClass::LessOrEqual:
return "<=";
case BuiltinClass::Greater:
return ">";
case BuiltinClass::GreaterOrEqual:
return ">=";
}
solAssert(false);
},
[](TypeClassDefinition const* _declaration) { return _declaration->name(); },
}, declaration);
}
bool Sort::operator==(Sort const& _rhs) const bool Sort::operator==(Sort const& _rhs) const
{ {
if (classes.size() != _rhs.classes.size()) if (classes.size() != _rhs.classes.size())

View File

@ -18,16 +18,9 @@
#pragma once #pragma once
#include <set> #include <set>
#include <string>
#include <variant> #include <variant>
#include <vector> #include <vector>
namespace solidity::frontend
{
class Declaration;
class TypeClassDefinition;
}
namespace solidity::frontend::experimental namespace solidity::frontend::experimental
{ {
@ -36,12 +29,10 @@ class TypeSystem;
struct TypeConstant; struct TypeConstant;
struct TypeVariable; struct TypeVariable;
using Type = std::variant<TypeConstant, TypeVariable>; using Type = std::variant<std::monostate, TypeConstant, TypeVariable>;
enum class BuiltinType enum class PrimitiveType
{ {
Type,
Sort,
Void, Void,
Function, Function,
TypeFunction, TypeFunction,
@ -52,45 +43,70 @@ enum class BuiltinType
Integer Integer
}; };
using TypeConstructor = std::variant<BuiltinType, Declaration const*>; enum class PrimitiveClass
}
namespace std
{ {
template<> Type,
struct less<solidity::frontend::experimental::TypeConstructor> Kind
{
bool operator()(solidity::frontend::experimental::TypeConstructor const& _lhs, solidity::frontend::experimental::TypeConstructor const& _rhs) const;
}; };
}
namespace solidity::frontend::experimental struct TypeConstructor
{ {
public:
TypeConstructor(TypeConstructor const& _typeConstructor): m_index(_typeConstructor.m_index) {}
TypeConstructor& operator=(TypeConstructor const& _typeConstructor)
{
m_index = _typeConstructor.m_index;
return *this;
}
bool operator<(TypeConstructor const& _rhs) const
{
return m_index < _rhs.m_index;
}
bool operator==(TypeConstructor const& _rhs) const
{
return m_index == _rhs.m_index;
}
bool operator!=(TypeConstructor const& _rhs) const
{
return m_index != _rhs.m_index;
}
private:
friend class TypeSystem;
TypeConstructor(size_t _index): m_index(_index) {}
size_t m_index = 0;
};
struct TypeConstant struct TypeConstant
{ {
TypeConstructor constructor; TypeConstructor constructor;
std::vector<Type> arguments; std::vector<Type> arguments;
}; };
enum class BuiltinClass
{
Type,
Kind,
Integer,
Mul,
Add,
Equal,
Less,
LessOrEqual,
Greater,
GreaterOrEqual
};
struct TypeClass struct TypeClass
{ {
std::variant<BuiltinClass, TypeClassDefinition const*> declaration; public:
std::string toString() const; TypeClass(TypeClass const& _typeClass): m_index(_typeClass.m_index) {}
bool operator<(TypeClass const& _rhs) const; TypeClass& operator=(TypeClass const& _typeConstructor)
bool operator==(TypeClass const& _rhs) const; {
bool operator!=(TypeClass const& _rhs) const { return !operator==(_rhs); } m_index = _typeConstructor.m_index;
return *this;
}
bool operator<(TypeClass const& _rhs) const
{
return m_index < _rhs.m_index;
}
bool operator==(TypeClass const& _rhs) const
{
return m_index == _rhs.m_index;
}
bool operator!=(TypeClass const& _rhs) const
{
return m_index != _rhs.m_index;
}
private:
friend class TypeSystem;
TypeClass(size_t _index): m_index(_index) {}
size_t m_index = 0;
}; };
struct Sort struct Sort

View File

@ -33,6 +33,7 @@
#include <fmt/format.h> #include <fmt/format.h>
using namespace std; using namespace std;
using namespace solidity;
using namespace solidity::frontend; using namespace solidity::frontend;
using namespace solidity::frontend::experimental; using namespace solidity::frontend::experimental;
@ -122,17 +123,36 @@ TypeEnvironment TypeEnvironment::clone() const
TypeSystem::TypeSystem() TypeSystem::TypeSystem()
{ {
Sort typeSort{{TypeClass{BuiltinClass::Type}}}; auto declarePrimitiveClass = [&](std::string _name) {
m_typeConstructors[BuiltinType::TypeFunction] = TypeConstructorInfo{ return std::visit(util::GenericVisitor{
"tfun", [](std::string _error) -> TypeClass {
{Arity{vector<Sort>{{typeSort},{typeSort}}, TypeClass{BuiltinClass::Kind}}} solAssert(false, _error);
}; },
m_typeConstructors[BuiltinType::Function] = TypeConstructorInfo{ [](TypeClass _class) -> TypeClass { return _class; }
"fun", }, declareTypeClass(freshVariable({}), {}, _name, nullptr));
{
Arity{vector<Sort>{{typeSort, typeSort}}, TypeClass{BuiltinClass::Type}},
}
}; };
m_primitiveTypeClasses.emplace(PrimitiveClass::Type, declarePrimitiveClass("type"));
m_primitiveTypeClasses.emplace(PrimitiveClass::Kind, declarePrimitiveClass("kind"));
for (auto [type, name, arity]: std::initializer_list<std::tuple<PrimitiveType, const char*, uint64_t>> {
{PrimitiveType::TypeFunction, "tfun", 2},
{PrimitiveType::Function, "fun", 2},
{PrimitiveType::Void, "void", 0},
{PrimitiveType::Unit, "unit", 0},
{PrimitiveType::Pair, "pair", 2},
{PrimitiveType::Word, "word", 0},
{PrimitiveType::Integer, "integer", 0},
{PrimitiveType::Bool, "bool", 0},
})
m_primitiveTypeConstructors.emplace(type, declareTypeConstructor(name, name, arity, nullptr));
TypeClass classType = primitiveClass(PrimitiveClass::Type);
TypeClass classKind = primitiveClass(PrimitiveClass::Kind);
Sort typeSort{{classType}};
m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::TypeFunction).m_index).arities = {Arity{vector<Sort>{{typeSort},{typeSort}}, classKind}};
m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{vector<Sort>{{typeSort, typeSort}}, classType}};
m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{vector<Sort>{{typeSort, typeSort}}, classType}};
} }
experimental::Type TypeSystem::freshVariable(Sort _sort) experimental::Type TypeSystem::freshVariable(Sort _sort)
@ -143,18 +163,21 @@ experimental::Type TypeSystem::freshVariable(Sort _sort)
experimental::Type TypeSystem::freshTypeVariable(Sort _sort) experimental::Type TypeSystem::freshTypeVariable(Sort _sort)
{ {
_sort.classes.emplace(TypeClass{BuiltinClass::Type}); _sort.classes.emplace(primitiveClass(PrimitiveClass::Type));
return freshVariable(_sort); return freshVariable(_sort);
} }
experimental::Type TypeSystem::freshKindVariable(Sort _sort) experimental::Type TypeSystem::freshKindVariable(Sort _sort)
{ {
_sort.classes.emplace(TypeClass{BuiltinClass::Kind}); _sort.classes.emplace(primitiveClass(PrimitiveClass::Kind));
return freshVariable(_sort); return freshVariable(_sort);
} }
vector<TypeEnvironment::UnificationFailure> TypeEnvironment::instantiate(TypeVariable _variable, Type _type) vector<TypeEnvironment::UnificationFailure> TypeEnvironment::instantiate(TypeVariable _variable, Type _type)
{ {
for (auto typeVar: TypeEnvironmentHelpers{*this}.typeVars(_type))
if (typeVar.index() == _variable.index())
return {UnificationFailure{RecursiveUnification{_variable, _type}}};
Sort typeSort = sort(_type); Sort typeSort = sort(_type);
if (!(_variable.sort() <= typeSort)) if (!(_variable.sort() <= typeSort))
{ {
@ -189,6 +212,9 @@ experimental::Type TypeEnvironment::resolveRecursive(Type _type) const
[&](TypeVariable const&) -> Type { [&](TypeVariable const&) -> Type {
return _type; return _type;
}, },
[&](std::monostate) -> Type {
return _type;
}
}, resolve(_type)); }, resolve(_type));
} }
@ -221,21 +247,25 @@ Sort TypeEnvironment::sort(Type _type) const
return sort; return sort;
}, },
[](TypeVariable const& _variable) -> Sort { return _variable.sort(); }, [](TypeVariable const& _variable) -> Sort { return _variable.sort(); },
[](std::monostate) -> Sort { solAssert(false); }
}, _type); }, _type);
} }
void TypeSystem::declareTypeConstructor(TypeConstructor _typeConstructor, std::string _name, size_t _arguments) TypeConstructor TypeSystem::declareTypeConstructor(string _name, string _canonicalName, size_t _arguments, Declaration const* _declaration)
{ {
Sort baseSort{{TypeClass{BuiltinClass::Type}}}; solAssert(m_canonicalTypeNames.insert(_canonicalName).second, "Duplicate canonical type name.");
bool newlyInserted = m_typeConstructors.emplace(std::make_pair(_typeConstructor, TypeConstructorInfo{ Sort baseSort{{primitiveClass(PrimitiveClass::Type)}};
size_t index = m_typeConstructors.size();
m_typeConstructors.emplace_back(TypeConstructorInfo{
_name, _name,
{Arity{vector<Sort>{_arguments, baseSort}, TypeClass{BuiltinClass::Type}}} _canonicalName,
})).second; {Arity{vector<Sort>{_arguments, baseSort}, primitiveClass(PrimitiveClass::Type)}},
// TODO: proper error handling. _declaration
solAssert(newlyInserted, "Type constructor already declared."); });
return TypeConstructor{index};
} }
std::optional<std::string> TypeSystem::declareTypeClass(TypeClass _class, Type _typeVariable, std::map<std::string, Type> _functions) std::variant<TypeClass, std::string> TypeSystem::declareTypeClass(Type _typeVariable, std::map<std::string, Type> _functions, std::string _name, Declaration const* _declaration)
{ {
TypeVariable const* typeVariable = get_if<TypeVariable>(&_typeVariable); TypeVariable const* typeVariable = get_if<TypeVariable>(&_typeVariable);
if (!typeVariable) if (!typeVariable)
@ -251,19 +281,34 @@ std::optional<std::string> TypeSystem::declareTypeClass(TypeClass _class, Type _
return "Function " + functionName + " depends on invalid type variable."; return "Function " + functionName + " depends on invalid type variable.";
} }
if (!m_typeClasses.emplace(std::make_pair(_class, TypeClassInfo{ size_t index = m_typeClasses.size();
m_typeClasses.emplace_back(TypeClassInfo{
_typeVariable, _typeVariable,
std::move(_functions) std::move(_functions),
})).second) _name,
return "Type class already declared"; _declaration
return nullopt; });
TypeClass typeClass{index};
return typeClass;
}
std::optional<experimental::Type> TypeEnvironment::typeClassFunction(TypeClass _class, std::string _name)
{
auto* type = util::valueOrNullptr(m_typeSystem.typeClassInfo(_class).functions, _name);
if (!type)
return nullopt;
Type functionType = fresh(*type);
auto typeVars = TypeEnvironmentHelpers{*this}.typeVars(functionType);
solAssert(typeVars.size() == 1);
solAssert(unify(typeVars.front(), m_typeSystem.freshTypeVariable({{_class}})).empty());
return functionType;
} }
experimental::Type TypeSystem::type(TypeConstructor _constructor, std::vector<Type> _arguments) const experimental::Type TypeSystem::type(TypeConstructor _constructor, std::vector<Type> _arguments) const
{ {
// TODO: proper error handling // TODO: proper error handling
auto const& info = m_typeConstructors.at(_constructor); auto const& info = m_typeConstructors.at(_constructor.m_index);
solAssert(info.arguments() == _arguments.size(), "Invalid arity."); solAssert(info.arguments() == _arguments.size(), "Invalid arity.");
return TypeConstant{_constructor, _arguments}; return TypeConstant{_constructor, _arguments};
} }
@ -292,6 +337,7 @@ experimental::Type TypeEnvironment::fresh(Type _type)
} }
return mapping[_var.index()] = m_typeSystem.freshTypeVariable(_var.sort()); return mapping[_var.index()] = m_typeSystem.freshTypeVariable(_var.sort());
}, },
[](std::monostate) -> Type { solAssert(false); }
}, resolve(_type)); }, resolve(_type));
}; };
return freshImpl(_type, freshImpl); return freshImpl(_type, freshImpl);
@ -302,26 +348,24 @@ std::optional<std::string> TypeSystem::instantiateClass(Type _instanceVariable,
if (!TypeSystemHelpers{*this}.isTypeConstant(_instanceVariable)) if (!TypeSystemHelpers{*this}.isTypeConstant(_instanceVariable))
return "Invalid instance variable."; return "Invalid instance variable.";
auto [typeConstructor, typeArguments] = TypeSystemHelpers{*this}.destTypeConstant(_instanceVariable); auto [typeConstructor, typeArguments] = TypeSystemHelpers{*this}.destTypeConstant(_instanceVariable);
auto& typeConstructorInfo = m_typeConstructors.at(typeConstructor); auto& typeConstructorInfo = m_typeConstructors.at(typeConstructor.m_index);
if (_arity.argumentSorts.size() != typeConstructorInfo.arguments()) if (_arity.argumentSorts.size() != typeConstructorInfo.arguments())
return "Invalid arity."; return "Invalid arity.";
if (typeArguments.size() != typeConstructorInfo.arguments()) if (typeArguments.size() != typeConstructorInfo.arguments())
return "Invalid arity."; return "Invalid arity.";
auto const* classInfo = typeClassInfo(_arity.typeClass); auto const& classInfo = typeClassInfo(_arity.typeClass);
if (!classInfo)
return "Unknown class.";
TypeEnvironment newEnv = m_globalTypeEnvironment.clone(); TypeEnvironment newEnv = m_globalTypeEnvironment.clone();
std::set<size_t> typeVariables; std::set<size_t> typeVariables;
Type classVariable = classInfo->typeVariable; Type classVariable = classInfo.typeVariable;
if (!newEnv.unify(classVariable, _instanceVariable).empty()) if (!newEnv.unify(classVariable, _instanceVariable).empty())
// TODO: error reporting // TODO: error reporting
return "Unification of class and instance variable failed."; return "Unification of class and instance variable failed.";
for (auto [name, classFunctionType]: classInfo->functions) for (auto [name, classFunctionType]: classInfo.functions)
{ {
if (!_functionTypes.count(name)) if (!_functionTypes.count(name))
return "Missing function: " + name; return "Missing function: " + name;

View File

@ -25,6 +25,10 @@
#include <variant> #include <variant>
#include <vector> #include <vector>
namespace solidity::frontend
{
class Declaration;
}
namespace solidity::frontend::experimental namespace solidity::frontend::experimental
{ {
@ -40,12 +44,14 @@ public:
Type fresh(Type _type); Type fresh(Type _type);
struct TypeMismatch { Type a; Type b; }; struct TypeMismatch { Type a; Type b; };
struct SortMismatch { Type type; Sort sort; }; struct SortMismatch { Type type; Sort sort; };
using UnificationFailure = std::variant<TypeMismatch, SortMismatch>; struct RecursiveUnification { Type var; Type type; };
using UnificationFailure = std::variant<TypeMismatch, SortMismatch, RecursiveUnification>;
[[nodiscard]] std::vector<UnificationFailure> unify(Type _a, Type _b); [[nodiscard]] std::vector<UnificationFailure> unify(Type _a, Type _b);
Sort sort(Type _type) const; Sort sort(Type _type) const;
bool typeEquals(Type _lhs, Type _rhs) const; bool typeEquals(Type _lhs, Type _rhs) const;
TypeSystem& typeSystem() { return m_typeSystem; } TypeSystem& typeSystem() { return m_typeSystem; }
TypeSystem const& typeSystem() const { return m_typeSystem; } TypeSystem const& typeSystem() const { return m_typeSystem; }
std::optional<Type> typeClassFunction(TypeClass _class, std::string _name);
private: private:
TypeEnvironment(TypeEnvironment&& _env): m_typeSystem(_env.m_typeSystem), m_typeVariables(std::move(_env.m_typeVariables)) {} TypeEnvironment(TypeEnvironment&& _env): m_typeSystem(_env.m_typeSystem), m_typeVariables(std::move(_env.m_typeVariables)) {}
[[nodiscard]] std::vector<TypeEnvironment::UnificationFailure> instantiate(TypeVariable _variable, Type _type); [[nodiscard]] std::vector<TypeEnvironment::UnificationFailure> instantiate(TypeVariable _variable, Type _type);
@ -59,7 +65,9 @@ public:
struct TypeConstructorInfo struct TypeConstructorInfo
{ {
std::string name; std::string name;
std::string canonicalName;
std::vector<Arity> arities; std::vector<Arity> arities;
Declaration const* typeDeclaration = nullptr;
size_t arguments() const size_t arguments() const
{ {
solAssert(!arities.empty()); solAssert(!arities.empty());
@ -70,33 +78,52 @@ public:
{ {
Type typeVariable; Type typeVariable;
std::map<std::string, Type> functions; std::map<std::string, Type> functions;
std::string name;
Declaration const* classDeclaration = nullptr;
}; };
TypeSystem(); TypeSystem();
TypeSystem(TypeSystem const&) = delete; TypeSystem(TypeSystem const&) = delete;
TypeSystem const& operator=(TypeSystem const&) = delete; TypeSystem const& operator=(TypeSystem const&) = delete;
Type type(PrimitiveType _typeConstructor, std::vector<Type> _arguments) const
{
return type(m_primitiveTypeConstructors.at(_typeConstructor), std::move(_arguments));
}
Type type(TypeConstructor _typeConstructor, std::vector<Type> _arguments) const; Type type(TypeConstructor _typeConstructor, std::vector<Type> _arguments) const;
std::string typeName(TypeConstructor _typeConstructor) const std::string typeName(TypeConstructor _typeConstructor) const
{ {
// TODO: proper error handling // TODO: proper error handling
return m_typeConstructors.at(_typeConstructor).name; return m_typeConstructors.at(_typeConstructor.m_index).name;
}
std::string canonicalName(TypeConstructor _typeConstructor) const
{
// TODO: proper error handling
return m_typeConstructors.at(_typeConstructor.m_index).canonicalName;
}
TypeConstructor declareTypeConstructor(std::string _name, std::string _canonicalName, size_t _arguments, Declaration const* _declaration);
TypeConstructor constructor(PrimitiveType _type) const
{
return m_primitiveTypeConstructors.at(_type);
}
TypeClass primitiveClass(PrimitiveClass _class) const
{
return m_primitiveTypeClasses.at(_class);
} }
void declareTypeConstructor(TypeConstructor _typeConstructor, std::string _name, size_t _arguments);
size_t constructorArguments(TypeConstructor _typeConstructor) const size_t constructorArguments(TypeConstructor _typeConstructor) const
{ {
// TODO: error handling // TODO: error handling
return m_typeConstructors.at(_typeConstructor).arguments(); return m_typeConstructors.at(_typeConstructor.m_index).arguments();
} }
TypeConstructorInfo const& constructorInfo(TypeConstructor _typeConstructor) const TypeConstructorInfo const& constructorInfo(TypeConstructor _typeConstructor) const
{ {
// TODO: error handling // TODO: error handling
return m_typeConstructors.at(_typeConstructor); return m_typeConstructors.at(_typeConstructor.m_index);
} }
TypeClassInfo const* typeClassInfo(TypeClass _class) const TypeConstructorInfo const& constructorInfo(PrimitiveType _typeConstructor) const
{ {
return util::valueOrNullptr(m_typeClasses, _class); return constructorInfo(constructor(_typeConstructor));
} }
[[nodiscard]] std::optional<std::string> declareTypeClass(TypeClass _class, Type _typeVariable, std::map<std::string, Type> _functions); std::variant<TypeClass, std::string> declareTypeClass(Type _typeVariable, std::map<std::string, Type> _functions, std::string _name, Declaration const* _declaration);
[[nodiscard]] std::optional<std::string> instantiateClass(Type _instanceVariable, Arity _arity, std::map<std::string, Type> _functions); [[nodiscard]] std::optional<std::string> instantiateClass(Type _instanceVariable, Arity _arity, std::map<std::string, Type> _functions);
Type freshTypeVariable(Sort _sort); Type freshTypeVariable(Sort _sort);
@ -106,10 +133,20 @@ public:
TypeEnvironment& env() { return m_globalTypeEnvironment; } TypeEnvironment& env() { return m_globalTypeEnvironment; }
Type freshVariable(Sort _sort); Type freshVariable(Sort _sort);
std::string typeClassName(TypeClass _class) const { return m_typeClasses.at(_class.m_index).name; }
Declaration const* typeClassDeclaration(TypeClass _class) const { return m_typeClasses.at(_class.m_index).classDeclaration; }
private: private:
friend class TypeEnvironment;
TypeClassInfo const& typeClassInfo(TypeClass _class) const
{
return m_typeClasses.at(_class.m_index);
}
size_t m_numTypeVariables = 0; size_t m_numTypeVariables = 0;
std::map<TypeConstructor, TypeConstructorInfo> m_typeConstructors; std::map<PrimitiveType, TypeConstructor> m_primitiveTypeConstructors;
std::map<TypeClass, TypeClassInfo> m_typeClasses; std::map<PrimitiveClass, TypeClass> m_primitiveTypeClasses;
std::set<std::string> m_canonicalTypeNames;
std::vector<TypeConstructorInfo> m_typeConstructors;
std::vector<TypeClassInfo> m_typeClasses;
TypeEnvironment m_globalTypeEnvironment{*this}; TypeEnvironment m_globalTypeEnvironment{*this};
}; };

View File

@ -19,6 +19,10 @@
#include <libsolidity/ast/experimental/TypeSystemHelper.h> #include <libsolidity/ast/experimental/TypeSystemHelper.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <libsolidity/analysis/experimental/TypeRegistration.h>
#include <libsolutil/Visitor.h> #include <libsolutil/Visitor.h>
#include <range/v3/to_container.hpp> #include <range/v3/to_container.hpp>
@ -33,69 +37,70 @@ using namespace solidity::langutil;
using namespace solidity::frontend; using namespace solidity::frontend;
using namespace solidity::frontend::experimental; using namespace solidity::frontend::experimental;
std::optional<TypeConstructor> experimental::typeConstructorFromTypeName(TypeName const& _typeName) /*std::optional<TypeConstructor> experimental::typeConstructorFromTypeName(Analysis const& _analysis, TypeName const& _typeName)
{ {
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName)) if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
{ {
if (auto constructor = typeConstructorFromToken(elementaryTypeName->typeName().token())) if (auto constructor = typeConstructorFromToken(_analysis, elementaryTypeName->typeName().token()))
return *constructor; return *constructor;
} }
else if (auto const* userDefinedType = dynamic_cast<UserDefinedTypeName const*>(&_typeName)) else if (auto const* userDefinedType = dynamic_cast<UserDefinedTypeName const*>(&_typeName))
{ {
if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration) if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration)
return referencedDeclaration; return _analysis.annotation<TypeRegistration>(*referencedDeclaration).typeConstructor;
} }
return nullopt; return nullopt;
} }*/
/*
std::optional<TypeConstructor> experimental::typeConstructorFromToken(langutil::Token _token) std::optional<TypeConstructor> experimental::typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token)
{ {
TypeSystem const& typeSystem = _analysis.typeSystem();
switch(_token) switch(_token)
{ {
case Token::Void: case Token::Void:
return BuiltinType::Void; return typeSystem.builtinConstructor(BuiltinType::Void);
case Token::Fun: case Token::Fun:
return BuiltinType::Function; return typeSystem.builtinConstructor(BuiltinType::Function);
case Token::Unit: case Token::Unit:
return BuiltinType::Unit; return typeSystem.builtinConstructor(BuiltinType::Unit);
case Token::Pair: case Token::Pair:
return BuiltinType::Pair; return typeSystem.builtinConstructor(BuiltinType::Pair);
case Token::Word: case Token::Word:
return BuiltinType::Word; return typeSystem.builtinConstructor(BuiltinType::Word);
case Token::Integer: case Token::Integer:
return BuiltinType::Integer; return typeSystem.builtinConstructor(BuiltinType::Integer);
case Token::Bool: case Token::Bool:
return BuiltinType::Bool; return typeSystem.builtinConstructor(BuiltinType::Bool);
default: default:
return nullopt; return nullopt;
} }
} }*/
std::optional<TypeClass> experimental::typeClassFromToken(langutil::Token _token) std::optional<BuiltinClass> experimental::builtinClassFromToken(langutil::Token _token)
{ {
switch (_token) switch (_token)
{ {
case Token::Integer: case Token::Integer:
return TypeClass{BuiltinClass::Integer}; return BuiltinClass::Integer;
case Token::Mul: case Token::Mul:
return TypeClass{BuiltinClass::Mul}; return BuiltinClass::Mul;
case Token::Add: case Token::Add:
return TypeClass{BuiltinClass::Add}; return BuiltinClass::Add;
case Token::Equal: case Token::Equal:
return TypeClass{BuiltinClass::Equal}; return BuiltinClass::Equal;
case Token::LessThan: case Token::LessThan:
return TypeClass{BuiltinClass::Less}; return BuiltinClass::Less;
case Token::LessThanOrEqual: case Token::LessThanOrEqual:
return TypeClass{BuiltinClass::LessOrEqual}; return BuiltinClass::LessOrEqual;
case Token::GreaterThan: case Token::GreaterThan:
return TypeClass{BuiltinClass::Greater}; return BuiltinClass::Greater;
case Token::GreaterThanOrEqual: case Token::GreaterThanOrEqual:
return TypeClass{BuiltinClass::GreaterOrEqual}; return BuiltinClass::GreaterOrEqual;
default: default:
return nullopt; return nullopt;
} }
} }
/*
std::optional<TypeClass> experimental::typeClassFromTypeClassName(TypeClassName const& _typeClass) std::optional<TypeClass> experimental::typeClassFromTypeClassName(TypeClassName const& _typeClass)
{ {
return std::visit(util::GenericVisitor{ return std::visit(util::GenericVisitor{
@ -110,16 +115,16 @@ std::optional<TypeClass> experimental::typeClassFromTypeClassName(TypeClassName
} }
}, _typeClass.name()); }, _typeClass.name());
} }
*/
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
{ {
if (_elements.empty()) if (_elements.empty())
return typeSystem.type(BuiltinType::Unit, {}); return typeSystem.type(PrimitiveType::Unit, {});
if (_elements.size() == 1) if (_elements.size() == 1)
return _elements.front(); return _elements.front();
Type result = _elements.back(); Type result = _elements.back();
for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1)) for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1))
result = typeSystem.type(BuiltinType::Pair, {type, result}); result = typeSystem.type(PrimitiveType::Pair, {type, result});
return result; return result;
} }
@ -127,15 +132,11 @@ vector<experimental::Type> TypeSystemHelpers::destTupleType(Type _tupleType) con
{ {
if (!isTypeConstant(_tupleType)) if (!isTypeConstant(_tupleType))
return {_tupleType}; return {_tupleType};
TypeConstructor pairConstructor = typeSystem.constructor(PrimitiveType::Pair);
auto [constructor, arguments] = destTypeConstant(_tupleType); auto [constructor, arguments] = destTypeConstant(_tupleType);
if (auto const* builtinType = get_if<BuiltinType>(&constructor)) if (constructor == typeSystem.constructor(PrimitiveType::Unit))
{
if (*builtinType == BuiltinType::Unit)
return {}; return {};
else if (*builtinType != BuiltinType::Pair) if (constructor != pairConstructor)
return {_tupleType};
}
else
return {_tupleType}; return {_tupleType};
solAssert(arguments.size() == 2); solAssert(arguments.size() == 2);
@ -147,8 +148,7 @@ vector<experimental::Type> TypeSystemHelpers::destTupleType(Type _tupleType) con
if (!isTypeConstant(tail)) if (!isTypeConstant(tail))
break; break;
auto [tailConstructor, tailArguments] = destTypeConstant(tail); auto [tailConstructor, tailArguments] = destTypeConstant(tail);
auto const* builtinType = get_if<BuiltinType>(&tailConstructor); if (tailConstructor != pairConstructor)
if(!builtinType || *builtinType != BuiltinType::Pair)
break; break;
solAssert(tailArguments.size() == 2); solAssert(tailArguments.size() == 2);
result.emplace_back(tailArguments.front()); result.emplace_back(tailArguments.front());
@ -185,14 +185,13 @@ bool TypeSystemHelpers::isTypeConstant(Type _type) const
experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const
{ {
return typeSystem.type(BuiltinType::Function, {_argType, _resultType}); return typeSystem.type(PrimitiveType::Function, {_argType, _resultType});
} }
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
{ {
auto [constructor, arguments] = destTypeConstant(_functionType); auto [constructor, arguments] = destTypeConstant(_functionType);
auto const* builtinType = get_if<BuiltinType>(&constructor); solAssert(constructor == typeSystem.constructor(PrimitiveType::Function));
solAssert(builtinType && *builtinType == BuiltinType::Function);
solAssert(arguments.size() == 2); solAssert(arguments.size() == 2);
return make_tuple(arguments.front(), arguments.back()); return make_tuple(arguments.front(), arguments.back());
} }
@ -202,20 +201,18 @@ bool TypeSystemHelpers::isFunctionType(Type _type) const
if (!isTypeConstant(_type)) if (!isTypeConstant(_type))
return false; return false;
auto constructor = get<0>(destTypeConstant(_type)); auto constructor = get<0>(destTypeConstant(_type));
auto const* builtinType = get_if<BuiltinType>(&constructor); return constructor == typeSystem.constructor(PrimitiveType::Function);
return builtinType && *builtinType == BuiltinType::Function;
} }
experimental::Type TypeSystemHelpers::typeFunctionType(experimental::Type _argType, experimental::Type _resultType) const experimental::Type TypeSystemHelpers::typeFunctionType(experimental::Type _argType, experimental::Type _resultType) const
{ {
return typeSystem.type(BuiltinType::TypeFunction, {_argType, _resultType}); return typeSystem.type(PrimitiveType::TypeFunction, {_argType, _resultType});
} }
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destTypeFunctionType(Type _functionType) const tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destTypeFunctionType(Type _functionType) const
{ {
auto [constructor, arguments] = destTypeConstant(_functionType); auto [constructor, arguments] = destTypeConstant(_functionType);
auto const* builtinType = get_if<BuiltinType>(&constructor); solAssert(constructor == typeSystem.constructor(PrimitiveType::TypeFunction));
solAssert(builtinType && *builtinType == BuiltinType::TypeFunction);
solAssert(arguments.size() == 2); solAssert(arguments.size() == 2);
return make_tuple(arguments.front(), arguments.back()); return make_tuple(arguments.front(), arguments.back());
} }
@ -225,8 +222,7 @@ bool TypeSystemHelpers::isTypeFunctionType(Type _type) const
if (!isTypeConstant(_type)) if (!isTypeConstant(_type))
return false; return false;
auto constructor = get<0>(destTypeConstant(_type)); auto constructor = get<0>(destTypeConstant(_type));
auto const* builtinType = get_if<BuiltinType>(&constructor); return constructor == typeSystem.constructor(PrimitiveType::TypeFunction);
return builtinType && *builtinType == BuiltinType::TypeFunction;
} }
vector<experimental::Type> TypeEnvironmentHelpers::typeVars(Type _type) const vector<experimental::Type> TypeEnvironmentHelpers::typeVars(Type _type) const
@ -243,7 +239,7 @@ vector<experimental::Type> TypeEnvironmentHelpers::typeVars(Type _type) const
if (indices.emplace(_var.index()).second) if (indices.emplace(_var.index()).second)
typeVars.emplace_back(_var); typeVars.emplace_back(_var);
}, },
// TODO: move to env helpers? [](std::monostate) { solAssert(false); }
}, env.resolve(_type)); }, env.resolve(_type));
}; };
typeVarsImpl(_type, typeVarsImpl); typeVarsImpl(_type, typeVarsImpl);
@ -259,25 +255,25 @@ std::string TypeSystemHelpers::sortToString(Sort _sort) const
case 0: case 0:
return "()"; return "()";
case 1: case 1:
return _sort.classes.begin()->toString(); return typeSystem.typeClassName(*_sort.classes.begin());
default: default:
{ {
std::stringstream stream; std::stringstream stream;
stream << "("; stream << "(";
for (auto typeClass: _sort.classes | ranges::views::drop_last(1)) for (auto typeClass: _sort.classes | ranges::views::drop_last(1))
stream << typeClass.toString() << ", "; stream << typeSystem.typeClassName(typeClass) << ", ";
stream << _sort.classes.rbegin()->toString() << ")"; stream << typeSystem.typeClassName(*_sort.classes.rbegin()) << ")";
return stream.str(); return stream.str();
} }
} }
} }
std::string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const
{ {
return visit(util::GenericVisitor{ return visit(util::GenericVisitor{
[&](TypeConstant const& _type) { [&](TypeConstant _type) -> string {
std::stringstream stream; std::stringstream stream;
auto printTypeArguments = [&]() { stream << env.typeSystem().constructorInfo(_type.constructor).canonicalName;
if (!_type.arguments.empty()) if (!_type.arguments.empty())
{ {
stream << "$"; stream << "$";
@ -286,56 +282,12 @@ std::string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const
stream << canonicalTypeName(_type.arguments.back()); stream << canonicalTypeName(_type.arguments.back());
stream << "$"; stream << "$";
} }
};
std::visit(util::GenericVisitor{
[&](Declaration const* _declaration) {
if (auto const* typeDeclarationAnnotation = dynamic_cast<TypeDeclarationAnnotation const*>(&_declaration->annotation()))
stream << *typeDeclarationAnnotation->canonicalName;
else
// TODO: canonical name
stream << _declaration->name();
printTypeArguments();
},
[&](BuiltinType _builtinType) {
switch(_builtinType)
{
case BuiltinType::Type:
stream << "type";
break;
case BuiltinType::Sort:
stream << "sort";
break;
case BuiltinType::Void:
stream << "void";
break;
case BuiltinType::Function:
stream << "fun";
break;
case BuiltinType::TypeFunction:
stream << "tfun";
break;
case BuiltinType::Unit:
stream << "unit";
break;
case BuiltinType::Pair:
stream << "pair";
break;
case BuiltinType::Word:
stream << "word";
break;
case BuiltinType::Bool:
stream << "bool";
break;
case BuiltinType::Integer:
stream << "integer";
break;
}
printTypeArguments();
}
}, _type.constructor);
return stream.str(); return stream.str();
}, },
[](TypeVariable const&)-> string { [](TypeVariable) -> string {
solAssert(false);
},
[](std::monostate) -> string {
solAssert(false); solAssert(false);
}, },
}, env.resolve(_type)); }, env.resolve(_type));
@ -343,10 +295,30 @@ std::string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const
std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const
{ {
std::map<TypeConstructor, std::function<string(std::vector<Type>)>> formatters{
{env.typeSystem().constructor(PrimitiveType::Function), [&](auto const& _args) {
solAssert(_args.size() == 2);
return fmt::format("{} -> {}", typeToString(_args.front()), typeToString(_args.back()));
}},
{env.typeSystem().constructor(PrimitiveType::Unit), [&](auto const& _args) {
solAssert(_args.size() == 0);
return "()";
}},
{env.typeSystem().constructor(PrimitiveType::Pair), [&](auto const&) {
auto tupleTypes = TypeSystemHelpers{env.typeSystem()}.destTupleType(_type);
string result = "(";
for (auto type: tupleTypes | ranges::views::drop_last(1))
result += typeToString(type) + ", ";
result += typeToString(tupleTypes.back()) + ")";
return result;
}},
};
return std::visit(util::GenericVisitor{ return std::visit(util::GenericVisitor{
[&](TypeConstant const& _type) { [&](TypeConstant const& _type) {
if (auto* formatter = util::valueOrNullptr(formatters, _type.constructor))
return (*formatter)(_type.arguments);
std::stringstream stream; std::stringstream stream;
auto printTypeArguments = [&]() { stream << env.typeSystem().constructorInfo(_type.constructor).name;
if (!_type.arguments.empty()) if (!_type.arguments.empty())
{ {
stream << "("; stream << "(";
@ -355,48 +327,9 @@ std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const
stream << typeToString(_type.arguments.back()); stream << typeToString(_type.arguments.back());
stream << ")"; stream << ")";
} }
};
std::visit(util::GenericVisitor{
[&](Declaration const* _declaration) {
stream << env.typeSystem().typeName(_declaration);
printTypeArguments();
},
[&](BuiltinType _builtinType) {
switch (_builtinType)
{
case BuiltinType::Function:
solAssert(_type.arguments.size() == 2);
stream << fmt::format("{} -> {}", typeToString(_type.arguments.front()), typeToString(_type.arguments.back()));
break;
case BuiltinType::Unit:
solAssert(_type.arguments.empty());
stream << "()";
break;
case BuiltinType::Pair:
{
auto tupleTypes = TypeSystemHelpers{env.typeSystem()}.destTupleType(_type);
stream << "(";
for (auto type: tupleTypes | ranges::views::drop_last(1))
stream << typeToString(type) << ", ";
stream << typeToString(tupleTypes.back()) << ")";
break;
}
case BuiltinType::Type:
{
solAssert(_type.arguments.size() == 1);
stream << "TYPE(" << typeToString(_type.arguments.front()) << ")";
break;
}
default:
stream << env.typeSystem().typeName(_builtinType);
printTypeArguments();
break;
}
}
}, _type.constructor);
return stream.str(); return stream.str();
}, },
[](TypeVariable const& _type) { [&](TypeVariable const& _type) {
std::stringstream stream; std::stringstream stream;
std::string varName; std::string varName;
size_t index = _type.index(); size_t index = _type.index();
@ -410,17 +343,18 @@ std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const
case 0: case 0:
break; break;
case 1: case 1:
stream << ":" << _type.sort().classes.begin()->toString(); stream << ":" << env.typeSystem().typeClassName(*_type.sort().classes.begin());
break; break;
default: default:
stream << ":("; stream << ":(";
for (auto typeClass: _type.sort().classes | ranges::views::drop_last(1)) for (auto typeClass: _type.sort().classes | ranges::views::drop_last(1))
stream << typeClass.toString() << ", "; stream << env.typeSystem().typeClassName(typeClass) << ", ";
stream << _type.sort().classes.rbegin()->toString(); stream << env.typeSystem().typeClassName(*_type.sort().classes.rbegin());
stream << ")"; stream << ")";
break; break;
} }
return stream.str(); return stream.str();
}, },
[](std::monostate) -> string { solAssert(false); }
}, env.resolve(_type)); }, env.resolve(_type));
} }

View File

@ -23,11 +23,12 @@
namespace solidity::frontend::experimental namespace solidity::frontend::experimental
{ {
class Analysis;
std::optional<TypeConstructor> typeConstructorFromTypeName(TypeName const& _typeName); enum class BuiltinClass;
std::optional<TypeConstructor> typeConstructorFromToken(langutil::Token _token); //std::optional<TypeConstructor> typeConstructorFromTypeName(Analysis const& _analysis, TypeName const& _typeName);
std::optional<TypeClass> typeClassFromTypeClassName(TypeClassName const& _typeClass); //std::optional<TypeConstructor> typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token);
std::optional<TypeClass> typeClassFromToken(langutil::Token _token); //std::optional<TypeClass> typeClassFromTypeClassName(TypeClassName const& _typeClass);
std::optional<BuiltinClass> builtinClassFromToken(langutil::Token _token);
struct TypeSystemHelpers struct TypeSystemHelpers
{ {

View File

@ -45,8 +45,8 @@ struct IRGenerationContext
} }
struct QueuedFunction struct QueuedFunction
{ {
FunctionDefinition const* function; FunctionDefinition const* function = nullptr;
Type type; Type type = std::monostate{};
}; };
std::list<QueuedFunction> functionQueue; std::list<QueuedFunction> functionQueue;
std::map<FunctionDefinition const*, std::vector<Type>> generatedFunctions; std::map<FunctionDefinition const*, std::vector<Type>> generatedFunctions;

View File

@ -108,13 +108,13 @@ string IRGenerator::generate(ContractDefinition const& _contract)
while (!m_context.functionQueue.empty()) while (!m_context.functionQueue.empty())
{ {
auto [function, type] = m_context.functionQueue.front(); auto queueEntry = m_context.functionQueue.front();
m_context.functionQueue.pop_front(); m_context.functionQueue.pop_front();
auto& generatedTypes = m_context.generatedFunctions[function]; auto& generatedTypes = m_context.generatedFunctions.insert(std::make_pair(queueEntry.function, vector<Type>{})).first->second;
if (!util::contains_if(generatedTypes, [&, type=type](auto _generatedType) { return m_context.env->typeEquals(_generatedType, type); })) if (!util::contains_if(generatedTypes, [&](auto const& _generatedType) { return m_context.env->typeEquals(_generatedType, queueEntry.type); }))
{ {
m_context.generatedFunctions[function].emplace_back(type); generatedTypes.emplace_back(queueEntry.type);
code << generate(*function, type); code << generate(*queueEntry.function, queueEntry.type);
} }
} }

View File

@ -99,7 +99,7 @@ private:
solAssert(varDecl, "External reference in inline assembly to something that is not a variable declaration."); solAssert(varDecl, "External reference in inline assembly to something that is not a variable declaration.");
auto type = m_context.analysis.annotation<TypeInference>(*varDecl).type; auto type = m_context.analysis.annotation<TypeInference>(*varDecl).type;
solAssert(type); solAssert(type);
solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(BuiltinType::Word, {}))); solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(PrimitiveType::Word, {})));
string value = IRNames::localVariable(*varDecl); string value = IRNames::localVariable(*varDecl);
return yul::Identifier{_identifier.debugData, yul::YulString{value}}; return yul::Identifier{_identifier.debugData, yul::YulString{value}};
} }
@ -200,28 +200,25 @@ namespace
{ {
TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGenerationContext const& _context, TypeClass _class) TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGenerationContext const& _context, TypeClass _class)
{ {
return std::visit(util::GenericVisitor{ auto const* typeClassDeclaration = _context.analysis.typeSystem().typeClassDeclaration(_class);
[&](BuiltinClass _builtinClass) -> auto const& { if (typeClassDeclaration)
return _context.analysis.annotation<TypeRegistration>().builtinClassInstantiations.at(_builtinClass); return _context.analysis.annotation<TypeRegistration>(*typeClassDeclaration).instantiations;
}, // TODO: better mechanism than fetching by name.
[&](TypeClassDefinition const* _classDefinition) -> auto const& { auto& annotation = _context.analysis.annotation<TypeRegistration>();
return _context.analysis.annotation<TypeRegistration>(*_classDefinition).instantiations; return annotation.builtinClassInstantiations.at(annotation.builtinClassesByName.at(_context.analysis.typeSystem().typeClassName(_class)));
}
}, _class.declaration);
} }
} }
FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, string _name, Type _type) FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, string _name, Type _type)
{ {
TypeSystemHelpers helper{m_context.analysis.typeSystem()}; 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(); TypeEnvironment env = m_context.env->clone();
auto typeVars = TypeEnvironmentHelpers{env}.typeVars(genericFunctionType); std::optional<Type> genericFunctionType = env.typeClassFunction(_class, _name);
solAssert(genericFunctionType);
auto typeVars = TypeEnvironmentHelpers{env}.typeVars(*genericFunctionType);
solAssert(typeVars.size() == 1); solAssert(typeVars.size() == 1);
solAssert(env.unify(genericFunctionType, _type).empty()); solAssert(env.unify(*genericFunctionType, _type).empty());
auto typeClassInstantiation = get<0>(helper.destTypeConstant(env.resolve(typeVars.front()))); auto typeClassInstantiation = get<0>(helper.destTypeConstant(env.resolve(typeVars.front())));
auto const& instantiations = typeClassInstantiations(m_context, _class); auto const& instantiations = typeClassInstantiations(m_context, _class);
@ -247,16 +244,18 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
auto expressionType = type(_memberAccess.expression()); auto expressionType = type(_memberAccess.expression());
auto constructor = std::get<0>(helper.destTypeConstant(expressionType)); auto constructor = std::get<0>(helper.destTypeConstant(expressionType));
auto memberAccessType = type(_memberAccess); auto memberAccessType = type(_memberAccess);
std::visit(util::GenericVisitor{ auto const* declaration = m_context.analysis.typeSystem().constructorInfo(constructor).typeDeclaration;
[](BuiltinType) { solAssert(false); }, solAssert(declaration);
[&](Declaration const *_declaration) if (auto const* typeClassDefinition = dynamic_cast<TypeClassDefinition const*>(declaration))
{ {
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_declaration)) optional<TypeClass> typeClass = m_context.analysis.annotation<TypeInference>(*typeClassDefinition).typeClass;
solAssert(typeClass);
solAssert(m_expressionDeclaration.emplace( solAssert(m_expressionDeclaration.emplace(
&_memberAccess, &_memberAccess,
&resolveTypeClassFunction(TypeClass{typeClass}, _memberAccess.memberName(), memberAccessType) &resolveTypeClassFunction(*typeClass, _memberAccess.memberName(), memberAccessType)
).second); ).second);
else if (dynamic_cast<TypeDefinition const*>(_declaration)) }
else if (dynamic_cast<TypeDefinition const*>(declaration))
{ {
if (_memberAccess.memberName() == "abs" || _memberAccess.memberName() == "rep") if (_memberAccess.memberName() == "abs" || _memberAccess.memberName() == "rep")
solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::Identity).second); solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::Identity).second);
@ -265,8 +264,6 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
} }
else else
solAssert(false); solAssert(false);
}
}, constructor);
} }
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)