mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Type checking for Yul.
This commit is contained in:
parent
aa6a2b4706
commit
1f51716227
@ -82,7 +82,6 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
|
||||
vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
|
||||
{
|
||||
expectValidType(_literal.type, _literal.location);
|
||||
|
||||
if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32)
|
||||
typeError(
|
||||
_literal.location,
|
||||
@ -93,6 +92,13 @@ vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
|
||||
else if (_literal.kind == LiteralKind::Boolean)
|
||||
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
|
||||
|
||||
if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type))
|
||||
typeError(
|
||||
_literal.location,
|
||||
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"."
|
||||
);
|
||||
|
||||
|
||||
return {_literal.type};
|
||||
}
|
||||
|
||||
@ -179,7 +185,8 @@ void AsmAnalyzer::operator()(Assignment const& _assignment)
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < numVariables; ++i)
|
||||
checkAssignment(_assignment.variableNames[i]);
|
||||
if (i < types.size())
|
||||
checkAssignment(_assignment.variableNames[i], types[i]);
|
||||
}
|
||||
|
||||
void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
||||
@ -193,6 +200,9 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
||||
yul::IdentifierContext::VariableDeclaration,
|
||||
m_currentScope->insideFunction()
|
||||
);
|
||||
for (auto const& variable: _varDecl.variables)
|
||||
expectValidType(variable.type, variable.location);
|
||||
|
||||
if (_varDecl.value)
|
||||
{
|
||||
vector<YulString> types = std::visit(*this, *_varDecl.value);
|
||||
@ -204,15 +214,25 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
||||
to_string(types.size()) +
|
||||
" values."
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < _varDecl.variables.size(); ++i)
|
||||
{
|
||||
YulString givenType = m_dialect.defaultType;
|
||||
if (i < types.size())
|
||||
givenType = types[i];
|
||||
TypedName const& variable = _varDecl.variables[i];
|
||||
if (variable.type != givenType)
|
||||
typeError(
|
||||
variable.location,
|
||||
"Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (TypedName const& variable: _varDecl.variables)
|
||||
{
|
||||
expectValidType(variable.type, variable.location);
|
||||
m_activeVariables.insert(&std::get<Scope::Variable>(
|
||||
m_currentScope->identifiers.at(variable.name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
|
||||
@ -291,6 +311,11 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||
);
|
||||
}
|
||||
}
|
||||
std::reverse(argTypes.begin(), argTypes.end());
|
||||
|
||||
if (parameterTypes && parameterTypes->size() == argTypes.size())
|
||||
for (size_t i = 0; i < parameterTypes->size(); ++i)
|
||||
expectType((*parameterTypes)[i], argTypes[i], locationOf(_funCall.arguments[i]));
|
||||
|
||||
if (m_success)
|
||||
{
|
||||
@ -306,7 +331,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||
|
||||
void AsmAnalyzer::operator()(If const& _if)
|
||||
{
|
||||
expectExpression(*_if.condition);
|
||||
expectBoolExpression(*_if.condition);
|
||||
|
||||
(*this)(_if.body);
|
||||
}
|
||||
@ -315,32 +340,15 @@ void AsmAnalyzer::operator()(Switch const& _switch)
|
||||
{
|
||||
yulAssert(_switch.expression, "");
|
||||
|
||||
expectExpression(*_switch.expression);
|
||||
|
||||
YulString caseType;
|
||||
bool mismatchingTypes = false;
|
||||
for (auto const& _case: _switch.cases)
|
||||
if (_case.value)
|
||||
{
|
||||
if (caseType.empty())
|
||||
caseType = _case.value->type;
|
||||
else if (caseType != _case.value->type)
|
||||
{
|
||||
mismatchingTypes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mismatchingTypes)
|
||||
m_errorReporter.typeError(
|
||||
_switch.location,
|
||||
"Switch cases have non-matching types."
|
||||
);
|
||||
YulString valueType = expectExpression(*_switch.expression);
|
||||
|
||||
set<u256> cases;
|
||||
for (auto const& _case: _switch.cases)
|
||||
{
|
||||
if (_case.value)
|
||||
{
|
||||
expectType(valueType, _case.value->type, _case.value->location);
|
||||
|
||||
// We cannot use "expectExpression" here because *_case.value is not an
|
||||
// Expression and would be converted to an Expression otherwise.
|
||||
(*this)(*_case.value);
|
||||
@ -366,8 +374,7 @@ void AsmAnalyzer::operator()(ForLoop const& _for)
|
||||
// condition, the body and the post part inside.
|
||||
m_currentScope = &scope(&_for.pre);
|
||||
|
||||
expectExpression(*_for.condition);
|
||||
|
||||
expectBoolExpression(*_for.condition);
|
||||
// backup outer for-loop & create new state
|
||||
auto outerForLoop = m_currentForLoop;
|
||||
m_currentForLoop = &_for;
|
||||
@ -403,10 +410,24 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr)
|
||||
return types.empty() ? m_dialect.defaultType : types.front();
|
||||
}
|
||||
|
||||
void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
||||
void AsmAnalyzer::expectBoolExpression(Expression const& _expr)
|
||||
{
|
||||
YulString type = expectExpression(_expr);
|
||||
if (type != m_dialect.boolType)
|
||||
typeError(locationOf(_expr),
|
||||
"Expected a value of boolean type \"" +
|
||||
m_dialect.boolType.str() +
|
||||
"\" but got \"" +
|
||||
type.str() +
|
||||
"\""
|
||||
);
|
||||
}
|
||||
|
||||
void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueType)
|
||||
{
|
||||
yulAssert(!_variable.name.empty(), "");
|
||||
size_t numErrorsBefore = m_errorReporter.errors().size();
|
||||
YulString const* variableType = nullptr;
|
||||
bool found = false;
|
||||
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
||||
{
|
||||
@ -418,6 +439,8 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
||||
_variable.location,
|
||||
"Variable " + _variable.name.str() + " used before it was declared."
|
||||
);
|
||||
else
|
||||
variableType = &std::get<Scope::Variable>(*var).type;
|
||||
found = true;
|
||||
}
|
||||
else if (m_resolver)
|
||||
@ -427,6 +450,7 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
||||
if (variableSize != size_t(-1))
|
||||
{
|
||||
found = true;
|
||||
variableType = &m_dialect.defaultType;
|
||||
yulAssert(variableSize == 1, "Invalid stack size of external reference.");
|
||||
}
|
||||
}
|
||||
@ -438,6 +462,17 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
||||
if (numErrorsBefore == m_errorReporter.errors().size())
|
||||
declarationError(_variable.location, "Variable not found or variable not lvalue.");
|
||||
}
|
||||
if (variableType && *variableType != _valueType)
|
||||
typeError(_variable.location,
|
||||
"Assigning a value of type \"" +
|
||||
_valueType.str() +
|
||||
"\" to a variable of type \"" +
|
||||
variableType->str() +
|
||||
"\"."
|
||||
);
|
||||
|
||||
if (m_success)
|
||||
yulAssert(variableType, "");
|
||||
}
|
||||
|
||||
Scope& AsmAnalyzer::scope(Block const* _block)
|
||||
@ -447,15 +482,28 @@ Scope& AsmAnalyzer::scope(Block const* _block)
|
||||
yulAssert(scopePtr, "Scope requested but not present.");
|
||||
return *scopePtr;
|
||||
}
|
||||
|
||||
void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location)
|
||||
{
|
||||
if (!_type.empty() && !m_dialect.types.count(_type))
|
||||
m_errorReporter.typeError(
|
||||
if (!m_dialect.types.count(_type))
|
||||
typeError(
|
||||
_location,
|
||||
"\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)."
|
||||
);
|
||||
}
|
||||
|
||||
void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, SourceLocation const& _location)
|
||||
{
|
||||
if (_expectedType != _givenType)
|
||||
typeError(_location,
|
||||
"Expected a value of type \"" +
|
||||
_expectedType.str() +
|
||||
"\" but got \"" +
|
||||
_givenType.str() +
|
||||
"\""
|
||||
);
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
|
||||
{
|
||||
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
|
||||
|
@ -96,13 +96,18 @@ private:
|
||||
/// Visits the expression, expects that it evaluates to exactly one value and
|
||||
/// returns the type. Reports errors on errors and returns the default type.
|
||||
YulString expectExpression(Expression const& _expr);
|
||||
/// Vists the expression and expects it to return a single boolean value.
|
||||
/// Reports an error otherwise.
|
||||
void expectBoolExpression(Expression const& _expr);
|
||||
bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location);
|
||||
|
||||
/// Verifies that a variable to be assigned to exists and can be assigned to.
|
||||
void checkAssignment(Identifier const& _variable);
|
||||
/// Verifies that a variable to be assigned to exists, can be assigned to
|
||||
/// and has the same type as the value.
|
||||
void checkAssignment(Identifier const& _variable, YulString _valueType);
|
||||
|
||||
Scope& scope(Block const* _block);
|
||||
void expectValidType(YulString _type, langutil::SourceLocation const& _location);
|
||||
void expectType(YulString _expectedType, YulString _givenType, langutil::SourceLocation const& _location);
|
||||
bool warnOnInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location);
|
||||
bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
|
||||
|
||||
|
@ -19,10 +19,19 @@
|
||||
*/
|
||||
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
using namespace solidity::yul;
|
||||
using namespace std;
|
||||
|
||||
bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const
|
||||
{
|
||||
if (_kind == LiteralKind::Boolean)
|
||||
return _type == boolType;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
Dialect const& Dialect::yulDeprecated()
|
||||
{
|
||||
static unique_ptr<Dialect> dialect;
|
||||
|
@ -33,6 +33,7 @@ namespace solidity::yul
|
||||
|
||||
class YulString;
|
||||
using Type = YulString;
|
||||
enum class LiteralKind;
|
||||
|
||||
struct BuiltinFunction
|
||||
{
|
||||
@ -52,7 +53,7 @@ struct Dialect: boost::noncopyable
|
||||
YulString defaultType;
|
||||
/// Type used for the literals "true" and "false".
|
||||
YulString boolType;
|
||||
std::set<YulString> types;
|
||||
std::set<YulString> types = {{}};
|
||||
|
||||
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
|
||||
virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }
|
||||
@ -61,6 +62,10 @@ struct Dialect: boost::noncopyable
|
||||
virtual BuiltinFunction const* equalityFunction() const { return nullptr; }
|
||||
virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; }
|
||||
|
||||
/// Check whether the given type is legal for the given literal value.
|
||||
/// Should only be called if the type exists in the dialect at all.
|
||||
virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const;
|
||||
|
||||
virtual std::set<YulString> fixedFunctionNames() const { return {}; }
|
||||
|
||||
Dialect() = default;
|
||||
|
Loading…
Reference in New Issue
Block a user