mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9038 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
@@ -41,8 +41,8 @@ bool hasEqualNameAndParameters(T const& _a, B const& _b)
|
||||
{
|
||||
return
|
||||
_a.name() == _b.name() &&
|
||||
FunctionType(_a).asCallableFunction(false)->hasEqualParameterTypes(
|
||||
*FunctionType(_b).asCallableFunction(false)
|
||||
FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes(
|
||||
*FunctionType(_b).asExternallyCallableFunction(false)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
||||
// under non error circumstances this should be true
|
||||
if (functionType->interfaceFunctionType())
|
||||
externalDeclarations[functionType->externalSignature()].emplace_back(
|
||||
f, functionType->asCallableFunction(false)
|
||||
f, functionType->asExternallyCallableFunction(false)
|
||||
);
|
||||
}
|
||||
for (VariableDeclaration const* v: contract->stateVariables())
|
||||
@@ -355,7 +355,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
||||
// under non error circumstances this should be true
|
||||
if (functionType->interfaceFunctionType())
|
||||
externalDeclarations[functionType->externalSignature()].emplace_back(
|
||||
v, functionType->asCallableFunction(false)
|
||||
v, functionType->asExternallyCallableFunction(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,10 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
||||
case VariableOccurrence::Kind::Return:
|
||||
if (unassignedVariables.count(&variableOccurrence.declaration()))
|
||||
{
|
||||
if (variableOccurrence.declaration().type()->dataStoredIn(DataLocation::Storage))
|
||||
if (
|
||||
variableOccurrence.declaration().type()->dataStoredIn(DataLocation::Storage) ||
|
||||
variableOccurrence.declaration().type()->dataStoredIn(DataLocation::CallData)
|
||||
)
|
||||
// Merely store the unassigned access. We do not generate an error right away, since this
|
||||
// path might still always revert. It is only an error if this is propagated to the exit
|
||||
// node of the function (i.e. there is a path with an uninitialized access).
|
||||
@@ -135,13 +138,16 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
||||
if (variableOccurrence->occurrence())
|
||||
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
||||
|
||||
bool isStorage = variableOccurrence->declaration().type()->dataStoredIn(DataLocation::Storage);
|
||||
m_errorReporter.typeError(
|
||||
3464_error,
|
||||
variableOccurrence->occurrence() ?
|
||||
*variableOccurrence->occurrence() :
|
||||
variableOccurrence->declaration().location(),
|
||||
ssl,
|
||||
string("This variable is of storage pointer type and can be ") +
|
||||
"This variable is of " +
|
||||
string(isStorage ? "storage" : "calldata") +
|
||||
" pointer type and can be " +
|
||||
(variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") +
|
||||
" without prior assignment, which would lead to undefined behaviour."
|
||||
);
|
||||
|
||||
@@ -151,7 +151,7 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
|
||||
{
|
||||
_typeName.annotation().type = TypeProvider::emptyTuple();
|
||||
m_errorReporter.fatalTypeError(
|
||||
9755_error,
|
||||
5172_error,
|
||||
_typeName.location(),
|
||||
"Name has to refer to a struct, enum or contract."
|
||||
);
|
||||
@@ -176,7 +176,7 @@ bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
|
||||
break;
|
||||
default:
|
||||
m_errorReporter.fatalTypeError(
|
||||
7653_error,
|
||||
6012_error,
|
||||
_typeName.location(),
|
||||
"Invalid visibility, can only be \"external\" or \"internal\"."
|
||||
);
|
||||
@@ -186,7 +186,7 @@ bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
|
||||
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External)
|
||||
{
|
||||
m_errorReporter.fatalTypeError(
|
||||
6138_error,
|
||||
7415_error,
|
||||
_typeName.location(),
|
||||
"Only external function types can be payable."
|
||||
);
|
||||
@@ -246,7 +246,7 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
}
|
||||
if (baseType->storageBytes() == 0)
|
||||
m_errorReporter.fatalTypeError(
|
||||
9390_error,
|
||||
6493_error,
|
||||
_typeName.baseType().location(),
|
||||
"Illegal base type of storage size zero for array."
|
||||
);
|
||||
@@ -259,16 +259,16 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
u256 lengthValue = 0;
|
||||
if (!lengthType || !lengthType->mobileType())
|
||||
m_errorReporter.typeError(
|
||||
8922_error,
|
||||
5462_error,
|
||||
length->location(),
|
||||
"Invalid array length, expected integer literal or constant expression."
|
||||
);
|
||||
else if (lengthType->isZero())
|
||||
m_errorReporter.typeError(1220_error, length->location(), "Array with zero length specified.");
|
||||
m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified.");
|
||||
else if (lengthType->isFractional())
|
||||
m_errorReporter.typeError(4323_error, length->location(), "Array with fractional length specified.");
|
||||
m_errorReporter.typeError(3208_error, length->location(), "Array with fractional length specified.");
|
||||
else if (lengthType->isNegative())
|
||||
m_errorReporter.typeError(9308_error, length->location(), "Array with negative length specified.");
|
||||
m_errorReporter.typeError(3658_error, length->location(), "Array with negative length specified.");
|
||||
else
|
||||
lengthValue = lengthType->literalValue(nullptr);
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
|
||||
@@ -345,7 +345,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
errorString += " for variable";
|
||||
}
|
||||
errorString += ", but " + locationToString(varLoc) + " was given.";
|
||||
m_errorReporter.typeError(6160_error, _variable.location(), errorString);
|
||||
m_errorReporter.typeError(6651_error, _variable.location(), errorString);
|
||||
|
||||
solAssert(!allowedDataLocations.empty(), "");
|
||||
varLoc = *allowedDataLocations.begin();
|
||||
|
||||
@@ -311,8 +311,8 @@ Token OverrideProxy::functionKind() const
|
||||
FunctionType const* OverrideProxy::functionType() const
|
||||
{
|
||||
return std::visit(GenericVisitor{
|
||||
[&](FunctionDefinition const* _item) { return FunctionType(*_item).asCallableFunction(false); },
|
||||
[&](VariableDeclaration const* _item) { return FunctionType(*_item).asCallableFunction(false); },
|
||||
[&](FunctionDefinition const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); },
|
||||
[&](VariableDeclaration const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); },
|
||||
[&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested function type of modifier."); return nullptr; }
|
||||
}, m_item);
|
||||
}
|
||||
@@ -559,7 +559,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
2837_error,
|
||||
6959_error,
|
||||
"Overriding function changes state mutability from \"" +
|
||||
stateMutabilityToString(_super.stateMutability()) +
|
||||
"\" to \"" +
|
||||
|
||||
@@ -119,7 +119,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
else
|
||||
errorMessage += " Did you mean " + std::move(suggestions) + "?";
|
||||
}
|
||||
m_errorReporter.declarationError(8051_error, _identifier.location(), errorMessage);
|
||||
m_errorReporter.declarationError(7576_error, _identifier.location(), errorMessage);
|
||||
}
|
||||
else if (declarations.size() == 1)
|
||||
_identifier.annotation().referencedDeclaration = declarations.front();
|
||||
@@ -157,7 +157,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
|
||||
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
|
||||
if (!declaration)
|
||||
{
|
||||
m_errorReporter.fatalDeclarationError(7556_error, _typeName.location(), "Identifier not found or not unique.");
|
||||
m_errorReporter.fatalDeclarationError(7920_error, _typeName.location(), "Identifier not found or not unique.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
||||
if (realName.empty())
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
9553_error,
|
||||
4794_error,
|
||||
_identifier.location,
|
||||
"In variable names _slot and _offset can only be used as a suffix."
|
||||
);
|
||||
@@ -221,7 +221,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
||||
if (declarations.size() > 1)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
8827_error,
|
||||
4718_error,
|
||||
_identifier.location,
|
||||
"Multiple matching identifiers. Resolving overloaded identifiers is not supported."
|
||||
);
|
||||
@@ -233,7 +233,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
|
||||
if (var->isLocalVariable() && m_yulInsideFunction)
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
8477_error,
|
||||
6578_error,
|
||||
_identifier.location,
|
||||
"Cannot access local Solidity variables from inside an inline assembly function."
|
||||
);
|
||||
@@ -255,7 +255,7 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.'));
|
||||
if (isSlot || isOffset)
|
||||
m_errorReporter.declarationError(
|
||||
8820_error,
|
||||
9155_error,
|
||||
identifier.location,
|
||||
"In variable declarations _slot and _offset can not be used as a suffix."
|
||||
);
|
||||
@@ -269,7 +269,7 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
ssl.append("The shadowed declaration is here:", decl->location());
|
||||
if (!ssl.infos.empty())
|
||||
m_errorReporter.declarationError(
|
||||
6005_error,
|
||||
3859_error,
|
||||
identifier.location,
|
||||
ssl,
|
||||
namePrefix.size() < identifier.name.str().size() ?
|
||||
|
||||
@@ -273,6 +273,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
"The msize instruction cannot be used when the Yul optimizer is activated because "
|
||||
"it can change its semantics. Either disable the Yul optimizer or do not use the instruction."
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,18 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
|
||||
typeArgument->location(),
|
||||
"Decoding type " + actualType->toString(false) + " not supported."
|
||||
);
|
||||
|
||||
if (auto referenceType = dynamic_cast<ReferenceType const*>(actualType))
|
||||
{
|
||||
auto result = referenceType->validForLocation(referenceType->location());
|
||||
if (!result)
|
||||
m_errorReporter.typeError(
|
||||
6118_error,
|
||||
typeArgument->location(),
|
||||
result.message()
|
||||
);
|
||||
}
|
||||
|
||||
components.push_back(actualType);
|
||||
}
|
||||
else
|
||||
@@ -1511,6 +1523,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
{
|
||||
if (!inlineArrayType)
|
||||
m_errorReporter.fatalTypeError(6378_error, _tuple.location(), "Unable to deduce common type for array elements.");
|
||||
else if (!inlineArrayType->nameable())
|
||||
m_errorReporter.fatalTypeError(
|
||||
9656_error,
|
||||
_tuple.location(),
|
||||
"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element."
|
||||
);
|
||||
else if (!inlineArrayType->canLiveOutsideStorage())
|
||||
m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage.");
|
||||
|
||||
@@ -1996,19 +2014,16 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
bool const isStructConstructorCall =
|
||||
_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
|
||||
|
||||
string msg;
|
||||
|
||||
if (isVariadic)
|
||||
msg +=
|
||||
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||
string msg = isVariadic ?
|
||||
"Need at least " +
|
||||
toString(parameterTypes.size()) +
|
||||
" arguments for " +
|
||||
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
||||
", but provided only " +
|
||||
toString(arguments.size()) +
|
||||
".";
|
||||
else
|
||||
msg +=
|
||||
"."
|
||||
:
|
||||
"Wrong argument count for " +
|
||||
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
||||
": " +
|
||||
@@ -2018,49 +2033,64 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
toString(parameterTypes.size()) +
|
||||
".";
|
||||
|
||||
// Extend error message in case we try to construct a struct with mapping member.
|
||||
if (isStructConstructorCall)
|
||||
{
|
||||
/// For error message: Struct members that were removed during conversion to memory.
|
||||
TypePointer const expressionType = type(_functionCall.expression());
|
||||
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
||||
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
||||
|
||||
if (!membersRemovedForStructConstructor.empty())
|
||||
// Extend error message in case we try to construct a struct with mapping member.
|
||||
if (isStructConstructorCall)
|
||||
{
|
||||
msg += " Members that have to be skipped in memory:";
|
||||
for (auto const& member: membersRemovedForStructConstructor)
|
||||
msg += " " + member;
|
||||
/// For error message: Struct members that were removed during conversion to memory.
|
||||
TypePointer const expressionType = type(_functionCall.expression());
|
||||
auto const& t = dynamic_cast<TypeType const&>(*expressionType);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
||||
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
||||
|
||||
if (!membersRemovedForStructConstructor.empty())
|
||||
{
|
||||
msg += " Members that have to be skipped in memory:";
|
||||
for (auto const& member: membersRemovedForStructConstructor)
|
||||
msg += " " + member;
|
||||
}
|
||||
|
||||
return { isVariadic ? 1123_error : 9755_error, msg };
|
||||
}
|
||||
}
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||
)
|
||||
{
|
||||
if (arguments.empty())
|
||||
msg +=
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||
)
|
||||
{
|
||||
if (arguments.empty())
|
||||
return {
|
||||
isVariadic ? 7653_error : 6138_error,
|
||||
msg +
|
||||
" This function requires a single bytes argument."
|
||||
" Use \"\" as argument to provide empty calldata."
|
||||
};
|
||||
else
|
||||
return {
|
||||
isVariadic ? 9390_error : 8922_error,
|
||||
msg +
|
||||
" This function requires a single bytes argument."
|
||||
" If all your arguments are value types, you can use"
|
||||
" abi.encode(...) to properly generate it."
|
||||
};
|
||||
}
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
||||
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
||||
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
||||
)
|
||||
return {
|
||||
isVariadic ? 1220_error : 4323_error,
|
||||
msg +
|
||||
" This function requires a single bytes argument."
|
||||
" Use \"\" as argument to provide empty calldata.";
|
||||
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
||||
" behaviour or abi.encode(...) to use ABI encoding."
|
||||
};
|
||||
else
|
||||
msg +=
|
||||
" This function requires a single bytes argument."
|
||||
" If all your arguments are value types, you can use"
|
||||
" abi.encode(...) to properly generate it.";
|
||||
}
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
||||
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
||||
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
||||
)
|
||||
msg +=
|
||||
" This function requires a single bytes argument."
|
||||
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
||||
" behaviour or abi.encode(...) to use ABI encoding.";
|
||||
m_errorReporter.typeError(1093_error, _functionCall.location(), msg);
|
||||
return { isVariadic ? 9308_error : 6160_error, msg };
|
||||
}();
|
||||
|
||||
m_errorReporter.typeError(errorId, _functionCall.location(), description);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2136,33 +2166,43 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
solAssert(!!paramArgMap[i], "unmapped parameter");
|
||||
if (!type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
||||
{
|
||||
string msg =
|
||||
"Invalid type for argument in function call. "
|
||||
"Invalid implicit conversion from " +
|
||||
type(*paramArgMap[i])->toString() +
|
||||
" to " +
|
||||
parameterTypes[i]->toString() +
|
||||
" requested.";
|
||||
if (
|
||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||
)
|
||||
msg +=
|
||||
" This function requires a single bytes argument."
|
||||
" If all your arguments are value types, you can"
|
||||
" use abi.encode(...) to properly generate it.";
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
||||
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
||||
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
||||
)
|
||||
msg +=
|
||||
" This function requires a single bytes argument."
|
||||
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
||||
" behaviour or abi.encode(...) to use ABI encoding.";
|
||||
m_errorReporter.typeError(6706_error, paramArgMap[i]->location(), msg);
|
||||
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||
string msg =
|
||||
"Invalid type for argument in function call. "
|
||||
"Invalid implicit conversion from " +
|
||||
type(*paramArgMap[i])->toString() +
|
||||
" to " +
|
||||
parameterTypes[i]->toString() +
|
||||
" requested.";
|
||||
if (
|
||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||
_functionType->kind() == FunctionType::Kind::BareDelegateCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareStaticCall
|
||||
)
|
||||
return {
|
||||
8051_error,
|
||||
msg +
|
||||
" This function requires a single bytes argument."
|
||||
" If all your arguments are value types, you can"
|
||||
" use abi.encode(...) to properly generate it."
|
||||
};
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::KECCAK256 ||
|
||||
_functionType->kind() == FunctionType::Kind::SHA256 ||
|
||||
_functionType->kind() == FunctionType::Kind::RIPEMD160
|
||||
)
|
||||
return {
|
||||
7556_error,
|
||||
msg +
|
||||
" This function requires a single bytes argument."
|
||||
" Use abi.encodePacked(...) to obtain the pre-0.5.0"
|
||||
" behaviour or abi.encode(...) to use ABI encoding."
|
||||
};
|
||||
else
|
||||
return { 9553_error, msg };
|
||||
}();
|
||||
m_errorReporter.typeError(errorId, paramArgMap[i]->location(), description);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2535,7 +2575,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
|
||||
if (possibleMembers.empty())
|
||||
{
|
||||
if (initialMemberCount == 0)
|
||||
if (initialMemberCount == 0 && !dynamic_cast<ArraySliceType const*>(exprType))
|
||||
{
|
||||
// Try to see if the member was removed because it is only available for storage types.
|
||||
auto storageType = TypeProvider::withLocationIfReference(
|
||||
@@ -2551,61 +2591,70 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
" outside of storage."
|
||||
);
|
||||
}
|
||||
string errorMsg = "Member \"" + memberName + "\" not found or not visible "
|
||||
|
||||
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||
string errorMsg = "Member \"" + memberName + "\" not found or not visible "
|
||||
"after argument-dependent lookup in " + exprType->toString() + ".";
|
||||
|
||||
if (auto const& funType = dynamic_cast<FunctionType const*>(exprType))
|
||||
{
|
||||
auto const& t = funType->returnParameterTypes();
|
||||
|
||||
if (memberName == "value")
|
||||
if (auto const* funType = dynamic_cast<FunctionType const*>(exprType))
|
||||
{
|
||||
if (funType->kind() == FunctionType::Kind::Creation)
|
||||
errorMsg = "Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available.";
|
||||
else if (
|
||||
funType->kind() == FunctionType::Kind::DelegateCall ||
|
||||
funType->kind() == FunctionType::Kind::BareDelegateCall
|
||||
)
|
||||
errorMsg = "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting.";
|
||||
else
|
||||
errorMsg = "Member \"value\" is only available for payable functions.";
|
||||
}
|
||||
else if (
|
||||
t.size() == 1 &&
|
||||
(t.front()->category() == Type::Category::Struct ||
|
||||
t.front()->category() == Type::Category::Contract)
|
||||
)
|
||||
errorMsg += " Did you intend to call the function?";
|
||||
}
|
||||
else if (exprType->category() == Type::Category::Contract)
|
||||
{
|
||||
for (auto const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr))
|
||||
if (addressMember.name == memberName)
|
||||
TypePointers const& t = funType->returnParameterTypes();
|
||||
|
||||
if (memberName == "value")
|
||||
{
|
||||
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
|
||||
string varName = var ? var->name() : "...";
|
||||
errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
|
||||
break;
|
||||
if (funType->kind() == FunctionType::Kind::Creation)
|
||||
return {
|
||||
8827_error,
|
||||
"Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available."
|
||||
};
|
||||
else if (
|
||||
funType->kind() == FunctionType::Kind::DelegateCall ||
|
||||
funType->kind() == FunctionType::Kind::BareDelegateCall
|
||||
)
|
||||
return { 8477_error, "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting." };
|
||||
else
|
||||
return { 8820_error, "Member \"value\" is only available for payable functions." };
|
||||
}
|
||||
}
|
||||
else if (auto addressType = dynamic_cast<AddressType const*>(exprType))
|
||||
{
|
||||
// Trigger error when using send or transfer with a non-payable fallback function.
|
||||
if (memberName == "send" || memberName == "transfer")
|
||||
{
|
||||
solAssert(
|
||||
addressType->stateMutability() != StateMutability::Payable,
|
||||
"Expected address not-payable as members were not found"
|
||||
);
|
||||
|
||||
errorMsg = "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\".";
|
||||
else if (
|
||||
t.size() == 1 && (
|
||||
t.front()->category() == Type::Category::Struct ||
|
||||
t.front()->category() == Type::Category::Contract
|
||||
)
|
||||
)
|
||||
return { 6005_error, errorMsg + " Did you intend to call the function?" };
|
||||
}
|
||||
}
|
||||
else if (exprType->category() == Type::Category::Contract)
|
||||
{
|
||||
for (MemberList::Member const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr))
|
||||
if (addressMember.name == memberName)
|
||||
{
|
||||
auto const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
|
||||
string varName = var ? var->name() : "...";
|
||||
errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
|
||||
return { 3125_error, errorMsg };
|
||||
}
|
||||
}
|
||||
else if (auto const* addressType = dynamic_cast<AddressType const*>(exprType))
|
||||
{
|
||||
// Trigger error when using send or transfer with a non-payable fallback function.
|
||||
if (memberName == "send" || memberName == "transfer")
|
||||
{
|
||||
solAssert(
|
||||
addressType->stateMutability() != StateMutability::Payable,
|
||||
"Expected address not-payable as members were not found"
|
||||
);
|
||||
|
||||
return { 9862_error, "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\"." };
|
||||
}
|
||||
}
|
||||
|
||||
return { 9582_error, errorMsg };
|
||||
}();
|
||||
|
||||
m_errorReporter.fatalTypeError(
|
||||
4035_error,
|
||||
errorId,
|
||||
_memberAccess.location(),
|
||||
errorMsg
|
||||
description
|
||||
);
|
||||
}
|
||||
else if (possibleMembers.size() > 1)
|
||||
@@ -2875,7 +2924,7 @@ bool TypeChecker::visit(IndexRangeAccess const& _access)
|
||||
if (arrayType->location() != DataLocation::CallData || !arrayType->isDynamicallySized())
|
||||
m_errorReporter.typeError(1227_error, _access.location(), "Index range access is only supported for dynamic calldata arrays.");
|
||||
else if (arrayType->baseType()->isDynamicallyEncoded())
|
||||
m_errorReporter.typeError(1878_error, _access.location(), "Index range access is not supported for arrays with dynamically encoded base types.");
|
||||
m_errorReporter.typeError(2148_error, _access.location(), "Index range access is not supported for arrays with dynamically encoded base types.");
|
||||
_access.annotation().type = TypeProvider::arraySlice(*arrayType);
|
||||
_access.annotation().isLValue = isLValue;
|
||||
_access.annotation().isPure = isPure;
|
||||
@@ -3181,17 +3230,17 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
|
||||
if (_expression.annotation().isLValue)
|
||||
return;
|
||||
|
||||
return m_errorReporter.typeError(1123_error, _expression.location(), [&]() {
|
||||
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||
if (_expression.annotation().isConstant)
|
||||
return "Cannot assign to a constant variable.";
|
||||
return { 6520_error, "Cannot assign to a constant variable." };
|
||||
|
||||
if (auto indexAccess = dynamic_cast<IndexAccess const*>(&_expression))
|
||||
{
|
||||
if (type(indexAccess->baseExpression())->category() == Type::Category::FixedBytes)
|
||||
return "Single bytes in fixed bytes arrays cannot be modified.";
|
||||
return { 4360_error, "Single bytes in fixed bytes arrays cannot be modified." };
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type(indexAccess->baseExpression())))
|
||||
if (arrayType->dataStoredIn(DataLocation::CallData))
|
||||
return "Calldata arrays are read-only.";
|
||||
return { 6182_error, "Calldata arrays are read-only." };
|
||||
}
|
||||
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_expression))
|
||||
@@ -3199,18 +3248,20 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
|
||||
if (auto structType = dynamic_cast<StructType const*>(type(memberAccess->expression())))
|
||||
{
|
||||
if (structType->dataStoredIn(DataLocation::CallData))
|
||||
return "Calldata structs are read-only.";
|
||||
return { 4156_error, "Calldata structs are read-only." };
|
||||
}
|
||||
else if (dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
||||
if (memberAccess->memberName() == "length")
|
||||
return "Member \"length\" is read-only and cannot be used to resize arrays.";
|
||||
return { 7567_error, "Member \"length\" is read-only and cannot be used to resize arrays." };
|
||||
}
|
||||
|
||||
if (auto identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration))
|
||||
if (varDecl->isExternalCallableParameter() && dynamic_cast<ReferenceType const*>(identifier->annotation().type))
|
||||
return "External function arguments of reference type are read-only.";
|
||||
return { 7128_error, "External function arguments of reference type are read-only." };
|
||||
|
||||
return "Expression has to be an lvalue.";
|
||||
}());
|
||||
return { 4247_error, "Expression has to be an lvalue." };
|
||||
}();
|
||||
|
||||
m_errorReporter.typeError(errorId, _expression.location(), description);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user