mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into HEAD
This commit is contained in:
@@ -38,15 +38,45 @@ namespace
|
||||
{
|
||||
|
||||
template <class T, class B>
|
||||
bool hasEqualNameAndParameters(T const& _a, B const& _b)
|
||||
bool hasEqualParameters(T const& _a, B const& _b)
|
||||
{
|
||||
return
|
||||
_a.name() == _b.name() &&
|
||||
FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes(
|
||||
*FunctionType(_b).asExternallyCallableFunction(false)
|
||||
);
|
||||
return FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes(
|
||||
*FunctionType(_b).asExternallyCallableFunction(false)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
map<ASTString, vector<T const*>> filterDeclarations(
|
||||
map<ASTString, vector<Declaration const*>> const& _declarations)
|
||||
{
|
||||
map<ASTString, vector<T const*>> filteredDeclarations;
|
||||
for (auto const& [name, overloads]: _declarations)
|
||||
for (auto const* declaration: overloads)
|
||||
if (auto typedDeclaration = dynamic_cast<T const*>(declaration))
|
||||
filteredDeclarations[name].push_back(typedDeclaration);
|
||||
return filteredDeclarations;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ContractLevelChecker::check(SourceUnit const& _sourceUnit)
|
||||
{
|
||||
bool noErrors = true;
|
||||
findDuplicateDefinitions(
|
||||
filterDeclarations<FunctionDefinition>(*_sourceUnit.annotation().exportedSymbols)
|
||||
);
|
||||
// This check flags duplicate free events when free events become
|
||||
// a Solidity feature
|
||||
findDuplicateDefinitions(
|
||||
filterDeclarations<EventDefinition>(*_sourceUnit.annotation().exportedSymbols)
|
||||
);
|
||||
if (!Error::containsOnlyWarnings(m_errorReporter.errors()))
|
||||
noErrors = false;
|
||||
for (ASTPointer<ASTNode> const& node: _sourceUnit.nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
if (!check(*contract))
|
||||
noErrors = false;
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool ContractLevelChecker::check(ContractDefinition const& _contract)
|
||||
@@ -143,8 +173,21 @@ void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const
|
||||
SecondarySourceLocation ssl;
|
||||
|
||||
for (size_t j = i + 1; j < overloads.size(); ++j)
|
||||
if (hasEqualNameAndParameters(*overloads[i], *overloads[j]))
|
||||
if (hasEqualParameters(*overloads[i], *overloads[j]))
|
||||
{
|
||||
solAssert(
|
||||
(
|
||||
dynamic_cast<ContractDefinition const*>(overloads[i]->scope()) &&
|
||||
dynamic_cast<ContractDefinition const*>(overloads[j]->scope()) &&
|
||||
overloads[i]->name() == overloads[j]->name()
|
||||
) ||
|
||||
(
|
||||
dynamic_cast<SourceUnit const*>(overloads[i]->scope()) &&
|
||||
dynamic_cast<SourceUnit const*>(overloads[j]->scope())
|
||||
),
|
||||
"Override is neither a namesake function/event in contract scope nor "
|
||||
"a free function/event (alias)."
|
||||
);
|
||||
ssl.append("Other declaration is here:", overloads[j]->location());
|
||||
reported.insert(j);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace solidity::frontend
|
||||
|
||||
/**
|
||||
* Component that verifies overloads, abstract contracts, function clashes and others
|
||||
* checks at contract or function level.
|
||||
* checks at file, contract, or function level.
|
||||
*/
|
||||
class ContractLevelChecker
|
||||
{
|
||||
@@ -51,11 +51,14 @@ public:
|
||||
m_errorReporter(_errorReporter)
|
||||
{}
|
||||
|
||||
/// Performs checks on the given source ast.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
bool check(SourceUnit const& _sourceUnit);
|
||||
|
||||
private:
|
||||
/// Performs checks on the given contract.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
bool check(ContractDefinition const& _contract);
|
||||
|
||||
private:
|
||||
/// Checks that two functions defined in this contract with the same name have different
|
||||
/// arguments and that there is at most one constructor.
|
||||
void checkDuplicateFunctions(ContractDefinition const& _contract);
|
||||
|
||||
@@ -109,7 +109,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
else
|
||||
for (Declaration const* declaration: declarations)
|
||||
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||
target, *declaration, alias.alias.get(), &alias.location, true, false, m_errorReporter
|
||||
target, *declaration, alias.alias.get(), &alias.location, false, m_errorReporter
|
||||
))
|
||||
error = true;
|
||||
}
|
||||
@@ -117,10 +117,11 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
for (auto const& nameAndDeclaration: scope->second->declarations())
|
||||
for (auto const& declaration: nameAndDeclaration.second)
|
||||
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||
target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter
|
||||
target, *declaration, &nameAndDeclaration.first, &imp->location(), false, m_errorReporter
|
||||
))
|
||||
error = true;
|
||||
}
|
||||
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
|
||||
return !error;
|
||||
}
|
||||
|
||||
@@ -446,7 +447,6 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
Declaration const& _declaration,
|
||||
string const* _name,
|
||||
SourceLocation const* _errorLocation,
|
||||
bool _warnOnShadow,
|
||||
bool _inactive,
|
||||
ErrorReporter& _errorReporter
|
||||
)
|
||||
@@ -456,7 +456,11 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
|
||||
string name = _name ? *_name : _declaration.name();
|
||||
Declaration const* shadowedDeclaration = nullptr;
|
||||
if (_warnOnShadow && !name.empty() && _container.enclosingContainer())
|
||||
// Do not warn about shadowing for structs and enums because their members are
|
||||
// not accessible without prefixes. Also do not warn about event parameters
|
||||
// because they do not participate in any proper scope.
|
||||
bool warnOnShadow = !_declaration.isStructMember() && !_declaration.isEnumValue() && !_declaration.isEventParameter();
|
||||
if (warnOnShadow && !name.empty() && _container.enclosingContainer())
|
||||
for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true))
|
||||
shadowedDeclaration = decl;
|
||||
|
||||
@@ -533,7 +537,6 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
|
||||
|
||||
void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit)
|
||||
{
|
||||
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
|
||||
ASTVisitor::endVisit(_sourceUnit);
|
||||
}
|
||||
|
||||
@@ -623,23 +626,13 @@ void DeclarationRegistrationHelper::closeCurrentScope()
|
||||
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration)
|
||||
{
|
||||
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
|
||||
|
||||
bool warnAboutShadowing = true;
|
||||
// Do not warn about shadowing for structs and enums because their members are
|
||||
// not accessible without prefixes. Also do not warn about event parameters
|
||||
// because they don't participate in any proper scope.
|
||||
if (
|
||||
dynamic_cast<StructDefinition const*>(m_currentScope) ||
|
||||
dynamic_cast<EnumDefinition const*>(m_currentScope) ||
|
||||
dynamic_cast<EventDefinition const*>(m_currentScope)
|
||||
)
|
||||
warnAboutShadowing = false;
|
||||
solAssert(m_currentScope == _declaration.scope(), "Unexpected current scope.");
|
||||
|
||||
// Register declaration as inactive if we are in block scope.
|
||||
bool inactive =
|
||||
(dynamic_cast<Block const*>(m_currentScope) || dynamic_cast<ForStatement const*>(m_currentScope));
|
||||
|
||||
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter);
|
||||
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, inactive, m_errorReporter);
|
||||
|
||||
solAssert(_declaration.annotation().scope == m_currentScope, "");
|
||||
solAssert(_declaration.annotation().contract == m_currentContract, "");
|
||||
|
||||
@@ -152,7 +152,6 @@ public:
|
||||
Declaration const& _declaration,
|
||||
std::string const* _name,
|
||||
langutil::SourceLocation const* _errorLocation,
|
||||
bool _warnOnShadow,
|
||||
bool _inactive,
|
||||
langutil::ErrorReporter& _errorReporter
|
||||
);
|
||||
|
||||
@@ -2922,6 +2922,9 @@ bool TypeChecker::visit(IndexRangeAccess const& _access)
|
||||
isPure = false;
|
||||
}
|
||||
|
||||
_access.annotation().isLValue = isLValue;
|
||||
_access.annotation().isPure = isPure;
|
||||
|
||||
TypePointer exprType = type(_access.baseExpression());
|
||||
if (exprType->category() == Type::Category::TypeType)
|
||||
{
|
||||
@@ -2936,14 +2939,11 @@ bool TypeChecker::visit(IndexRangeAccess const& _access)
|
||||
else if (!(arrayType = dynamic_cast<ArrayType const*>(exprType)))
|
||||
m_errorReporter.fatalTypeError(4781_error, _access.location(), "Index range access is only possible for arrays and array slices.");
|
||||
|
||||
|
||||
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(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;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+23
-5
@@ -495,6 +495,24 @@ string Scopable::sourceUnitName() const
|
||||
return *sourceUnit().annotation().path;
|
||||
}
|
||||
|
||||
bool Declaration::isEnumValue() const
|
||||
{
|
||||
solAssert(scope(), "");
|
||||
return dynamic_cast<EnumDefinition const*>(scope());
|
||||
}
|
||||
|
||||
bool Declaration::isStructMember() const
|
||||
{
|
||||
solAssert(scope(), "");
|
||||
return dynamic_cast<StructDefinition const*>(scope());
|
||||
}
|
||||
|
||||
bool Declaration::isEventParameter() const
|
||||
{
|
||||
solAssert(scope(), "");
|
||||
return dynamic_cast<EventDefinition const*>(scope());
|
||||
}
|
||||
|
||||
DeclarationAnnotation& Declaration::annotation() const
|
||||
{
|
||||
return initAnnotation<DeclarationAnnotation>();
|
||||
@@ -617,11 +635,6 @@ bool VariableDeclaration::isLibraryFunctionParameter() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VariableDeclaration::isEventParameter() const
|
||||
{
|
||||
return dynamic_cast<EventDefinition const*>(scope()) != nullptr;
|
||||
}
|
||||
|
||||
bool VariableDeclaration::hasReferenceOrMappingType() const
|
||||
{
|
||||
solAssert(typeName().annotation().type, "Can only be called after reference resolution");
|
||||
@@ -629,6 +642,11 @@ bool VariableDeclaration::hasReferenceOrMappingType() const
|
||||
return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type);
|
||||
}
|
||||
|
||||
bool VariableDeclaration::isStateVariable() const
|
||||
{
|
||||
return dynamic_cast<ContractDefinition const*>(scope());
|
||||
}
|
||||
|
||||
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
|
||||
{
|
||||
using Location = VariableDeclaration::Location;
|
||||
|
||||
@@ -258,10 +258,16 @@ public:
|
||||
bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; }
|
||||
virtual bool isVisibleViaContractTypeAccess() const { return false; }
|
||||
|
||||
|
||||
virtual bool isLValue() const { return false; }
|
||||
virtual bool isPartOfExternalInterface() const { return false; }
|
||||
|
||||
/// @returns true if this is a declaration of an enum member.
|
||||
bool isEnumValue() const;
|
||||
/// @returns true if this is a declaration of a struct member.
|
||||
bool isStructMember() const;
|
||||
/// @returns true if this is a declaration of a parameter of an event.
|
||||
bool isEventParameter() const;
|
||||
|
||||
/// @returns the type of expressions referencing this declaration.
|
||||
/// This can only be called once types of variable declarations have already been resolved.
|
||||
virtual TypePointer type() const = 0;
|
||||
@@ -899,7 +905,6 @@ public:
|
||||
ASTPointer<Expression> _value,
|
||||
Visibility _visibility,
|
||||
ASTPointer<StructuredDocumentation> const _documentation = nullptr,
|
||||
bool _isStateVar = false,
|
||||
bool _isIndexed = false,
|
||||
Mutability _mutability = Mutability::Mutable,
|
||||
ASTPointer<OverrideSpecifier> _overrides = nullptr,
|
||||
@@ -909,7 +914,6 @@ public:
|
||||
StructurallyDocumented(std::move(_documentation)),
|
||||
m_typeName(std::move(_type)),
|
||||
m_value(std::move(_value)),
|
||||
m_isStateVariable(_isStateVar),
|
||||
m_isIndexed(_isIndexed),
|
||||
m_mutability(_mutability),
|
||||
m_overrides(std::move(_overrides)),
|
||||
@@ -952,15 +956,11 @@ public:
|
||||
bool isConstructorParameter() const;
|
||||
/// @returns true iff this variable is a parameter(or return parameter of a library function
|
||||
bool isLibraryFunctionParameter() const;
|
||||
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
|
||||
/// in the body of a function or modifier.
|
||||
/// @returns true if this variable is a parameter of an event.
|
||||
bool isEventParameter() const;
|
||||
/// @returns true if the type of the variable is a reference or mapping type, i.e.
|
||||
/// array, struct or mapping. These types can take a data location (and often require it).
|
||||
/// Can only be called after reference resolution.
|
||||
bool hasReferenceOrMappingType() const;
|
||||
bool isStateVariable() const { return m_isStateVariable; }
|
||||
bool isStateVariable() const;
|
||||
bool isIndexed() const { return m_isIndexed; }
|
||||
Mutability mutability() const { return m_mutability; }
|
||||
bool isConstant() const { return m_mutability == Mutability::Constant; }
|
||||
@@ -989,7 +989,6 @@ private:
|
||||
/// Initially assigned value, can be missing. For local variables, this is stored inside
|
||||
/// VariableDeclarationStatement and not here.
|
||||
ASTPointer<Expression> m_value;
|
||||
bool m_isStateVariable = false; ///< Whether or not this is a contract state variable
|
||||
bool m_isIndexed = false; ///< Whether this is an indexed variable (used by events).
|
||||
/// Whether the variable is "constant", "immutable" or non-marked (mutable).
|
||||
Mutability m_mutability = Mutability::Mutable;
|
||||
|
||||
@@ -454,7 +454,6 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
|
||||
nullOrCast<Expression>(member(_node, "value")),
|
||||
visibility(_node),
|
||||
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
|
||||
memberAsBool(_node, "stateVariable"),
|
||||
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
|
||||
mutability,
|
||||
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
|
||||
|
||||
@@ -836,7 +836,7 @@ void BMC::checkCondition(
|
||||
case smtutil::CheckResult::SATISFIABLE:
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << _description << " happens here.";
|
||||
message << "BMC: " << _description << " happens here.";
|
||||
if (_callStack.size())
|
||||
{
|
||||
std::ostringstream modelMessage;
|
||||
@@ -865,13 +865,13 @@ void BMC::checkCondition(
|
||||
case smtutil::CheckResult::UNSATISFIABLE:
|
||||
break;
|
||||
case smtutil::CheckResult::UNKNOWN:
|
||||
m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation);
|
||||
m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation);
|
||||
break;
|
||||
case smtutil::CheckResult::CONFLICTING:
|
||||
m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
break;
|
||||
case smtutil::CheckResult::ERROR:
|
||||
m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver.");
|
||||
m_errorReporter.warning(1823_error, _location, "BMC: Error trying to invoke SMT solver.");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -919,13 +919,13 @@ void BMC::checkBooleanNotConstant(
|
||||
if (positiveResult == smtutil::CheckResult::SATISFIABLE)
|
||||
{
|
||||
solAssert(negatedResult == smtutil::CheckResult::UNSATISFIABLE, "");
|
||||
description = "Condition is always true.";
|
||||
description = "BMC: Condition is always true.";
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(positiveResult == smtutil::CheckResult::UNSATISFIABLE, "");
|
||||
solAssert(negatedResult == smtutil::CheckResult::SATISFIABLE, "");
|
||||
description = "Condition is always false.";
|
||||
description = "BMC: Condition is always false.";
|
||||
}
|
||||
m_errorReporter.warning(
|
||||
6838_error,
|
||||
|
||||
@@ -1103,10 +1103,10 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression c
|
||||
case CheckResult::UNKNOWN:
|
||||
break;
|
||||
case CheckResult::CONFLICTING:
|
||||
m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_outerErrorReporter.warning(1988_error, _location, "CHC: At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
break;
|
||||
case CheckResult::ERROR:
|
||||
m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver.");
|
||||
m_outerErrorReporter.warning(1218_error, _location, "CHC: Error trying to invoke SMT solver.");
|
||||
break;
|
||||
}
|
||||
return {result, cex};
|
||||
@@ -1264,21 +1264,21 @@ void CHC::checkAndReportTarget(
|
||||
m_outerErrorReporter.warning(
|
||||
_errorReporterId,
|
||||
_scope->location(),
|
||||
_satMsg,
|
||||
"CHC: " + _satMsg,
|
||||
SecondarySourceLocation().append("\nCounterexample:\n" + *cex, SourceLocation{})
|
||||
);
|
||||
else
|
||||
m_outerErrorReporter.warning(
|
||||
_errorReporterId,
|
||||
_scope->location(),
|
||||
_satMsg
|
||||
"CHC: " + _satMsg
|
||||
);
|
||||
}
|
||||
else if (!_unknownMsg.empty())
|
||||
m_outerErrorReporter.warning(
|
||||
_errorReporterId,
|
||||
_scope->location(),
|
||||
_unknownMsg
|
||||
"CHC: " + _unknownMsg
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -663,6 +663,9 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
case FunctionType::Kind::Event:
|
||||
// These can be safely ignored.
|
||||
break;
|
||||
case FunctionType::Kind::ObjectCreation:
|
||||
visitObjectCreation(_funCall);
|
||||
return;
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
4588_error,
|
||||
@@ -735,6 +738,25 @@ void SMTEncoder::visitGasLeft(FunctionCall const& _funCall)
|
||||
m_context.addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1));
|
||||
}
|
||||
|
||||
void SMTEncoder::visitObjectCreation(FunctionCall const& _funCall)
|
||||
{
|
||||
auto const& args = _funCall.arguments();
|
||||
solAssert(args.size() >= 1, "");
|
||||
auto argType = args.front()->annotation().type->category();
|
||||
solAssert(argType == Type::Category::Integer || argType == Type::Category::RationalNumber, "");
|
||||
|
||||
smtutil::Expression arraySize = expr(*args.front());
|
||||
setSymbolicUnknownValue(arraySize, TypeProvider::uint256(), m_context);
|
||||
|
||||
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(_funCall));
|
||||
solAssert(symbArray, "");
|
||||
smt::setSymbolicZeroValue(*symbArray, m_context);
|
||||
auto zeroElements = symbArray->elements();
|
||||
symbArray->increaseIndex();
|
||||
m_context.addAssertion(symbArray->length() == arraySize);
|
||||
m_context.addAssertion(symbArray->elements() == zeroElements);
|
||||
}
|
||||
|
||||
void SMTEncoder::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
if (auto decl = identifierToVariable(_identifier))
|
||||
@@ -834,7 +856,20 @@ void SMTEncoder::endVisit(Literal const& _literal)
|
||||
else if (smt::isBool(type))
|
||||
defineExpr(_literal, smtutil::Expression(_literal.token() == Token::TrueLiteral ? true : false));
|
||||
else if (smt::isStringLiteral(type))
|
||||
{
|
||||
createExpr(_literal);
|
||||
|
||||
// Add constraints for the length and values as it is known.
|
||||
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(_literal));
|
||||
solAssert(symbArray, "");
|
||||
|
||||
auto value = _literal.value();
|
||||
m_context.addAssertion(symbArray->length() == value.length());
|
||||
for (size_t i = 0; i < value.length(); i++)
|
||||
m_context.addAssertion(
|
||||
smtutil::Expression::select(symbArray->elements(), i) == size_t(value[i])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
@@ -977,13 +1012,36 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
||||
|
||||
if (_indexAccess.annotation().type->category() == Type::Category::TypeType)
|
||||
return;
|
||||
if (_indexAccess.baseExpression().annotation().type->category() == Type::Category::FixedBytes)
|
||||
if (auto const* type = dynamic_cast<FixedBytesType const*>(_indexAccess.baseExpression().annotation().type))
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7989_error,
|
||||
_indexAccess.location(),
|
||||
"Assertion checker does not yet support index accessing fixed bytes."
|
||||
);
|
||||
smtutil::Expression base = expr(_indexAccess.baseExpression());
|
||||
|
||||
if (type->numBytes() == 1)
|
||||
defineExpr(_indexAccess, base);
|
||||
else
|
||||
{
|
||||
auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_indexAccess.baseExpression().annotation().type);
|
||||
solAssert(!isSigned, "");
|
||||
solAssert(bvSize >= 16, "");
|
||||
solAssert(bvSize % 8 == 0, "");
|
||||
|
||||
smtutil::Expression idx = expr(*_indexAccess.indexExpression());
|
||||
|
||||
auto bvBase = smtutil::Expression::int2bv(base, bvSize);
|
||||
auto bvShl = smtutil::Expression::int2bv(idx * 8, bvSize);
|
||||
auto bvShr = smtutil::Expression::int2bv(bvSize - 8, bvSize);
|
||||
auto result = (bvBase << bvShl) >> bvShr;
|
||||
|
||||
auto anyValue = expr(_indexAccess);
|
||||
m_context.expression(_indexAccess)->increaseIndex();
|
||||
unsigned numBytes = bvSize / 8;
|
||||
auto withBound = smtutil::Expression::ite(
|
||||
idx < numBytes,
|
||||
smtutil::Expression::bv2int(result, false),
|
||||
anyValue
|
||||
);
|
||||
defineExpr(_indexAccess, withBound);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1654,9 +1712,7 @@ void SMTEncoder::assignment(VariableDeclaration const& _variable, Expression con
|
||||
// This is a special case where the SMT sorts are different.
|
||||
// For now we are unaware of other cases where this happens, but if they do appear
|
||||
// we should extract this into an `implicitConversion` function.
|
||||
if (_variable.type()->category() != Type::Category::Array || _value.annotation().type->category() != Type::Category::StringLiteral)
|
||||
assignment(_variable, expr(_value, _variable.type()));
|
||||
// TODO else { store each string literal byte into the array }
|
||||
assignment(_variable, expr(_value, _variable.type()));
|
||||
}
|
||||
|
||||
void SMTEncoder::assignment(VariableDeclaration const& _variable, smtutil::Expression const& _value)
|
||||
|
||||
@@ -137,6 +137,7 @@ protected:
|
||||
void visitAssert(FunctionCall const& _funCall);
|
||||
void visitRequire(FunctionCall const& _funCall);
|
||||
void visitGasLeft(FunctionCall const& _funCall);
|
||||
void visitObjectCreation(FunctionCall const& _funCall);
|
||||
void visitTypeConversion(FunctionCall const& _funCall);
|
||||
void visitFunctionIdentifier(Identifier const& _identifier);
|
||||
|
||||
|
||||
@@ -366,12 +366,10 @@ bool CompilerStack::analyze()
|
||||
// This also calculates whether a contract is abstract, which is needed by the
|
||||
// type checker.
|
||||
ContractLevelChecker contractLevelChecker(m_errorReporter);
|
||||
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
if (!contractLevelChecker.check(*contract))
|
||||
noErrors = false;
|
||||
if (auto sourceAst = source->ast)
|
||||
noErrors = contractLevelChecker.check(*sourceAst);
|
||||
|
||||
// Requires ContractLevelChecker
|
||||
DocStringAnalyser docStringAnalyser(m_errorReporter);
|
||||
|
||||
@@ -800,7 +800,6 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
value,
|
||||
visibility,
|
||||
documentation,
|
||||
_options.isStateVariable,
|
||||
isIndexed,
|
||||
mutability,
|
||||
overrides,
|
||||
|
||||
Reference in New Issue
Block a user