Rework error handlig of user type operators

This commit is contained in:
wechman
2022-09-28 13:07:58 +02:00
parent 4fac7b54b3
commit 6feb8aea73
10 changed files with 152 additions and 86 deletions
+89 -52
View File
@@ -1750,37 +1750,26 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
solAssert(!builtinResult || !userDefinedOperatorResult);
if (userDefinedOperatorResult)
{
if (userDefinedFunctionType->returnParameterTypes().size() != 1)
{
m_errorReporter.typeError(
3138_error,
_operation.location(),
"User defined operator " + string(TokenTraits::toString(_operation.getOperator())) +
" needs to return exactly one value."
);
_operation.annotation().type = subExprType;
}
else if (*userDefinedFunctionType->returnParameterTypes().front() != *userDefinedFunctionType->parameterTypes().front())
Type const* normalizedSubExprType = subExprType;
Type const* normalizedParameterType = userDefinedFunctionType->parameterTypes().front();
if (auto const* subExprReference = dynamic_cast<ReferenceType const*>(normalizedSubExprType))
normalizedSubExprType = TypeProvider::withLocationIfReference(subExprReference->location(), normalizedSubExprType);
if (auto const* parameterReferenceType = dynamic_cast<ReferenceType const*>(normalizedParameterType))
normalizedParameterType = TypeProvider::withLocationIfReference(parameterReferenceType->location(), normalizedParameterType);
if (*normalizedSubExprType != *normalizedParameterType)
{
m_errorReporter.typeError(
7983_error,
_operation.location(),
"User defined operator " + string(TokenTraits::toString(_operation.getOperator())) +
" needs to return value of type " +
" needs a value of type " +
userDefinedFunctionType->parameterTypes().front()->humanReadableName() + "."
);
_operation.annotation().type = subExprType;
}
else
{
solAssert(userDefinedFunctionType->parameterTypes().size() == 1);
solAssert(
*userDefinedFunctionType->parameterTypes().at(0) ==
*userDefinedFunctionType->returnParameterTypes().at(0)
);
_operation.annotation().type = userDefinedFunctionType->returnParameterTypes().at(0);
}
_operation.annotation().type = subExprType;
}
else if (builtinResult)
_operation.annotation().type = builtinResult;
@@ -1859,13 +1848,32 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
commonType = builtinResult.get();
else if (userDefinedOperatorResult)
{
if (userDefinedFunctionType->parameterTypes().size() != 2 ||
*userDefinedFunctionType->parameterTypes().at(0) != *userDefinedFunctionType->parameterTypes().at(1))
Type const* normalizedParameterType = userDefinedFunctionType->parameterTypes().at(0);
Type const* normalizedLeftType = leftType;
Type const* normalizedRightType = rightType;
if (auto const* parameterReference = dynamic_cast<ReferenceType const*>(normalizedParameterType))
normalizedParameterType = TypeProvider::withLocationIfReference(parameterReference->location(), normalizedParameterType);
if (auto const* leftReferenceType = dynamic_cast<ReferenceType const*>(normalizedLeftType))
normalizedLeftType = TypeProvider::withLocationIfReference(leftReferenceType->location(), normalizedLeftType);
if (auto const* rightReferenceType = dynamic_cast<ReferenceType const*>(normalizedRightType))
normalizedRightType = TypeProvider::withLocationIfReference(rightReferenceType->location(), normalizedRightType);
if (
userDefinedFunctionType->parameterTypes().size() != 2 ||
*normalizedLeftType != *normalizedParameterType ||
*normalizedRightType != *normalizedParameterType
)
m_errorReporter.typeError(
5653_error,
_operation.location(),
"User defined operator " + string(TokenTraits::toString(_operation.getOperator())) +
" needs to have two parameters of equal type."
"User defined operator " +
string(TokenTraits::toString(_operation.getOperator())) +
" not compatible with types " +
leftType->humanReadableName() +
" and " +
rightType->humanReadableName() +
"."
);
else if (userDefinedFunctionType->returnParameterTypes().size() == 1)
commonType = userDefinedFunctionType->parameterTypes().at(0);
@@ -1877,26 +1885,11 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
TypeProvider::boolean() :
commonType;
if (userDefinedOperatorResult)
{
if (userDefinedFunctionType->returnParameterTypes().size() != 1)
m_errorReporter.typeError(
1208_error,
_operation.location(),
"User defined operator " + string(TokenTraits::toString(_operation.getOperator())) +
" needs to return exactly one value."
);
else if (*userDefinedFunctionType->returnParameterTypes().front() != *_operation.annotation().type)
m_errorReporter.typeError(
3841_error,
_operation.location(),
"User defined operator " + string(TokenTraits::toString(_operation.getOperator())) +
" needs to return value of type " +
_operation.annotation().type->humanReadableName() + "."
);
}
else if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL)
if (
!userDefinedOperatorResult &&
(_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL)
)
{
string operation = _operation.getOperator() == Token::Exp ? "exponentiation" : "shift";
if (
@@ -3929,7 +3922,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType())
);
if (!result)
if (!result && !operator_)
m_errorReporter.typeError(
3100_error,
path->location(),
@@ -3971,23 +3964,62 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
1884_error,
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
"needs to have two parameters of equal type to be used for the operator " +
"needs to have two parameters of type " +
_usingFor.typeName()->annotation().type->canonicalName() +
" and the same data location to be used for the operator " +
TokenTraits::friendlyName(*operator_) +
"."
);
else if (
!TokenTraits::isBinaryOp(*operator_) &&
TokenTraits::isUnaryOp(*operator_) &&
functionType->parameterTypesIncludingSelf().size() != 1
(
functionType->parameterTypesIncludingSelf().size() != 1 ||
(
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->parameterTypesIncludingSelf().front()) !=
*TypeProvider::withLocationIfReference(DataLocation::Storage, _usingFor.typeName()->annotation().type)
)
)
)
m_errorReporter.typeError(
1147_error,
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
"needs to have exactly one parameter to be used for the operator " +
"needs to have exactly one parameter of type " +
_usingFor.typeName()->annotation().type->canonicalName() +
" to be used for the operator " +
TokenTraits::friendlyName(*operator_) +
"."
);
else if (
(
functionType->parameterTypesIncludingSelf().size() == 2 &&
(
(*functionType->parameterTypesIncludingSelf().at(0) != *functionType->parameterTypesIncludingSelf().at(1)) ||
(
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->parameterTypesIncludingSelf().at(0)) !=
*TypeProvider::withLocationIfReference(DataLocation::Storage, _usingFor.typeName()->annotation().type)
)
)
) ||
(
functionType->parameterTypesIncludingSelf().size() == 1 &&
(
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->parameterTypesIncludingSelf().at(0)) !=
*TypeProvider::withLocationIfReference(DataLocation::Storage, _usingFor.typeName()->annotation().type)
)
)
)
m_errorReporter.typeError(
7617_error,
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
"needs to have one or two parameters of type " +
_usingFor.typeName()->annotation().type->canonicalName() +
" and the same data location to be used for the operator " +
TokenTraits::friendlyName(*operator_) +
"."
);
else if (
functionType->parameterTypesIncludingSelf().size() != 1 &&
functionType->parameterTypesIncludingSelf().size() != 2
@@ -3996,7 +4028,9 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
8112_error,
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
"needs to have one or two parameters to be used for the operator " +
"needs to have one or two parameters of type " +
_usingFor.typeName()->annotation().type->canonicalName() +
" and the same data location to be used for the operator " +
TokenTraits::friendlyName(*operator_) +
"."
);
@@ -4018,7 +4052,10 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
!TokenTraits::isCompareOp(*operator_) &&
(
functionType->returnParameterTypes().size() != 1 ||
*functionType->returnParameterTypes().front() != *functionType->parameterTypesIncludingSelf().front()
(
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->returnParameterTypes().front()) !=
*TypeProvider::withLocationIfReference(DataLocation::Storage, _usingFor.typeName()->annotation().type)
)
)
)
m_errorReporter.typeError(
@@ -4026,7 +4063,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
"needs to return exactly one value of type " +
functionType->parameterTypesIncludingSelf().front()->canonicalName() +
_usingFor.typeName()->annotation().type->canonicalName() +
" to be used for the operator " +
TokenTraits::friendlyName(*operator_) +
"."
+9 -1
View File
@@ -404,8 +404,16 @@ Result<FunctionDefinition const*> Type::userDefinedOperator(Token _token, ASTNod
);
solAssert(functionType && !functionType->parameterTypes().empty());
Type const* normalizedType = this;
if (auto const* referenceType = dynamic_cast<ReferenceType const*>(normalizedType))
normalizedType = TypeProvider::withLocationIfReference(referenceType->location(), normalizedType);
Type const* normalizedParameterType = functionType->parameterTypes().front();
if (auto const* referenceType = dynamic_cast<ReferenceType const*>(normalizedParameterType))
normalizedParameterType = TypeProvider::withLocationIfReference(referenceType->location(), normalizedParameterType);
if (
isImplicitlyConvertibleTo(*functionType->parameterTypes().front()) &&
*normalizedType == *normalizedParameterType &&
(
(_unaryOperation && function.parameterList().parameters().size() == 1) ||
(!_unaryOperation && function.parameterList().parameters().size() == 2)