Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth
2020-11-18 20:05:02 +01:00
89 changed files with 2313 additions and 1511 deletions
+9 -11
View File
@@ -215,22 +215,21 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
void ReferencesResolver::operator()(yul::Identifier const& _identifier)
{
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), ".slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), ".offset");
static set<string> suffixes{"slot", "offset", "length"};
string suffix;
for (string const& s: suffixes)
if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
suffix = s;
// Could also use `pathFromCurrentScope`, split by '.'
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
if (isSlot || isOffset)
if (!suffix.empty())
{
// special mode to access storage variables
if (!declarations.empty())
// the special identifier exists itself, we should not allow that.
return;
string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - (
isSlot ?
string(".slot").size() :
string(".offset").size()
));
string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1);
solAssert(!realName.empty(), "Empty name.");
declarations = m_resolver.nameFromCurrentScope(realName);
if (!declarations.empty())
@@ -255,7 +254,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
m_errorReporter.declarationError(
9467_error,
_identifier.location,
"Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables."
"Identifier not found. Use \".slot\" and \".offset\" to access storage variables."
);
return;
}
@@ -270,8 +269,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return;
}
m_yulAnnotation->externalReferences[&_identifier].isSlot = isSlot;
m_yulAnnotation->externalReferences[&_identifier].isOffset = isOffset;
m_yulAnnotation->externalReferences[&_identifier].suffix = move(suffix);
m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
}
+51 -23
View File
@@ -755,7 +755,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second;
Declaration const* declaration = identifierInfo.declaration;
solAssert(!!declaration, "");
bool requiresStorage = identifierInfo.isSlot || identifierInfo.isOffset;
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
solAssert(var->type(), "Expected variable type!");
@@ -778,7 +777,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to.");
return false;
}
else if (requiresStorage)
else if (!identifierInfo.suffix.empty())
{
m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables.");
return false;
@@ -804,51 +803,80 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
solAssert(!dynamic_cast<FixedPointType const*>(var->type()), "FixedPointType not implemented.");
if (requiresStorage)
if (!identifierInfo.suffix.empty())
{
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
string const& suffix = identifierInfo.suffix;
solAssert((set<string>{"offset", "slot", "length"}).count(suffix), "");
if (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables.");
return false;
if (suffix != "slot" && suffix != "offset")
{
m_errorReporter.typeError(4656_error, _identifier.location, "State variables only support \".slot\" and \".offset\".");
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
if (var->isStateVariable())
{
m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\".");
return false;
}
else if (suffix != "slot")
{
m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to.");
return false;
}
}
}
else if (_context == yul::IdentifierContext::LValue)
else if (
auto const* arrayType = dynamic_cast<ArrayType const*>(var->type());
arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData)
)
{
if (var->isStateVariable())
if (suffix != "offset" && suffix != "length")
{
m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\".");
m_errorReporter.typeError(1536_error, _identifier.location, "Calldata variables only support \".offset\" and \".length\".");
return false;
}
else if (identifierInfo.isOffset)
{
m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to.");
return false;
}
else
solAssert(identifierInfo.isSlot, "");
}
else
{
m_errorReporter.typeError(3622_error, _identifier.location, "The suffix \"." + suffix + "\" is not supported by this variable or type.");
return false;
}
}
else if (!var->isConstant() && var->isStateVariable())
{
m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the .slot and .offset suffixes.");
m_errorReporter.typeError(
1408_error,
_identifier.location,
"Only local variables are supported. To access storage variables, use the \".slot\" and \".offset\" suffixes."
);
return false;
}
else if (var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the .slot or .offset suffix to access storage reference variables.");
m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables.");
return false;
}
else if (var->type()->sizeOnStack() != 1)
{
if (var->type()->dataStoredIn(DataLocation::CallData))
m_errorReporter.typeError(2370_error, _identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes.");
if (
auto const* arrayType = dynamic_cast<ArrayType const*>(var->type());
arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData)
)
m_errorReporter.typeError(1397_error, _identifier.location, "Call data elements cannot be accessed directly. Use \".offset\" and \".length\" to access the calldata offset and length of this array and then use \"calldatacopy\".");
else
{
solAssert(!var->type()->dataStoredIn(DataLocation::CallData), "");
m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported.");
}
return false;
}
}
else if (requiresStorage)
else if (!identifierInfo.suffix.empty())
{
m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables.");
m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables.");
return false;
}
else if (_context == yul::IdentifierContext::LValue)
@@ -3277,7 +3305,7 @@ void TypeChecker::endVisit(Literal const& _literal)
_literal.location(),
msg +
" If this is not used as an address, please prepend '00'. " +
"For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals"
"For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals"
);
}
+2 -2
View File
@@ -198,8 +198,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation
struct ExternalIdentifierInfo
{
Declaration const* declaration = nullptr;
bool isSlot = false; ///< Whether the storage slot of a variable is queried.
bool isOffset = false; ///< Whether the intra-slot offset of a storage variable is queried.
/// Suffix used, one of "slot", "offset", "length" or empty.
std::string suffix;
size_t valueSize = size_t(-1);
};
+4 -2
View File
@@ -224,8 +224,10 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie
Json::Value tuple(Json::objectValue);
tuple["src"] = sourceLocationToString(_info.first->location);
tuple["declaration"] = idOrNull(_info.second.declaration);
tuple["isSlot"] = Json::Value(_info.second.isSlot);
tuple["isOffset"] = Json::Value(_info.second.isOffset);
tuple["isSlot"] = Json::Value(_info.second.suffix == "slot");
tuple["isOffset"] = Json::Value(_info.second.suffix == "offset");
if (!_info.second.suffix.empty())
tuple["suffix"] = Json::Value(_info.second.suffix);
tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize));
return tuple;
}
+66 -30
View File
@@ -756,9 +756,9 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
else if (m_context.isStateVariable(decl))
{
auto const& location = m_context.storageLocationOfVariable(*decl);
if (ref->second.isSlot)
if (ref->second.suffix == "slot")
m_context << location.first;
else if (ref->second.isOffset)
else if (ref->second.suffix == "offset")
m_context << u256(location.second);
else
solAssert(false, "");
@@ -766,26 +766,44 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
else if (m_context.isLocalVariable(decl))
{
unsigned stackDiff = static_cast<unsigned>(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable);
if (ref->second.isSlot || ref->second.isOffset)
if (!ref->second.suffix.empty())
{
solAssert(variable->type()->dataStoredIn(DataLocation::Storage), "");
unsigned size = variable->type()->sizeOnStack();
if (size == 2)
string const& suffix = ref->second.suffix;
if (variable->type()->dataStoredIn(DataLocation::Storage))
{
// slot plus offset
if (ref->second.isOffset)
solAssert(suffix == "offset" || suffix == "slot", "");
unsigned size = variable->type()->sizeOnStack();
if (size == 2)
{
// slot plus offset
if (suffix == "offset")
stackDiff--;
}
else
{
solAssert(size == 1, "");
// only slot, offset is zero
if (suffix == "offset")
{
_assembly.appendConstant(u256(0));
return;
}
}
}
else if (variable->type()->dataStoredIn(DataLocation::CallData))
{
auto const* arrayType = dynamic_cast<ArrayType const*>(variable->type());
solAssert(
arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData),
""
);
solAssert(suffix == "offset" || suffix == "length", "");
solAssert(variable->type()->sizeOnStack() == 2, "");
if (suffix == "length")
stackDiff--;
}
else
{
solAssert(size == 1, "");
// only slot, offset is zero
if (ref->second.isOffset)
{
_assembly.appendConstant(u256(0));
return;
}
}
solAssert(false, "");
}
else
solAssert(variable->type()->sizeOnStack() == 1, "");
@@ -795,7 +813,6 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
solAssert(variable->type()->sizeOnStack() == 1, "");
_assembly.appendInstruction(dupInstruction(stackDiff));
}
else
@@ -803,7 +820,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
}
else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
{
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
solAssert(ref->second.suffix.empty(), "");
solAssert(contract->isLibrary(), "");
_assembly.appendLinkerSymbol(contract->fullyQualifiedName());
}
@@ -814,20 +831,39 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
else
{
// lvalue context
solAssert(!ref->second.isOffset, "");
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
solAssert(
!!variable && m_context.isLocalVariable(variable),
"Can only assign to stack variables in inline assembly."
);
solAssert(variable->type()->sizeOnStack() == 1, "");
unsigned stackDiff = static_cast<unsigned>(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable) - 1;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
string const& suffix = ref->second.suffix;
if (variable->type()->dataStoredIn(DataLocation::Storage))
{
solAssert(
!!variable && m_context.isLocalVariable(variable),
"Can only assign to stack variables in inline assembly."
);
solAssert(variable->type()->sizeOnStack() == 1, "");
solAssert(suffix == "slot", "");
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
);
}
else if (variable->type()->dataStoredIn(DataLocation::CallData))
{
auto const* arrayType = dynamic_cast<ArrayType const*>(variable->type());
solAssert(
arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData),
""
);
solAssert(suffix == "offset" || suffix == "length", "");
solAssert(variable->type()->sizeOnStack() == 2, "");
if (suffix == "length")
stackDiff--;
}
else
solAssert(suffix.empty(), "");
_assembly.appendInstruction(swapInstruction(stackDiff));
_assembly.appendInstruction(Instruction::POP);
}
+13
View File
@@ -3157,6 +3157,19 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
solUnimplementedAssert(false, "Tuple conversion not implemented.");
break;
}
case Type::Category::TypeType:
{
TypeType const& typeType = dynamic_cast<decltype(typeType)>(_from);
if (
auto const* contractType = dynamic_cast<ContractType const*>(typeType.actualType());
contractType->contractDefinition().isLibrary() &&
_to == *TypeProvider::address()
)
body = "converted := value";
else
solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
break;
}
default:
solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
}
@@ -30,6 +30,7 @@
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ReturnInfo.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libevmasm/GasMeter.h>
@@ -68,43 +69,12 @@ struct CopyTranslate: public yul::ASTCopier
yul::Expression operator()(yul::Identifier const& _identifier) override
{
// The operator() function is only called in lvalue context. In rvalue context,
// only translate(yul::Identifier) is called.
if (m_references.count(&_identifier))
{
auto const& reference = m_references.at(&_identifier);
auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
solUnimplementedAssert(varDecl, "");
if (reference.isOffset || reference.isSlot)
{
solAssert(reference.isOffset != reference.isSlot, "");
string value;
if (varDecl->isStateVariable())
value =
reference.isSlot ?
m_context.storageLocationOfStateVariable(*varDecl).first.str() :
to_string(m_context.storageLocationOfStateVariable(*varDecl).second);
else
{
solAssert(varDecl->isLocalVariable(), "");
if (reference.isSlot)
value = IRVariable{*varDecl}.part("slot").name();
else if (varDecl->type()->isValueType())
value = IRVariable{*varDecl}.part("offset").name();
else
{
solAssert(!IRVariable{*varDecl}.hasPart("offset"), "");
value = "0";
}
}
if (isdigit(value.front()))
return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}};
else
return yul::Identifier{_identifier.location, yul::YulString{value}};
}
}
return ASTCopier::operator()(_identifier);
return translateReference(_identifier);
else
return ASTCopier::operator()(_identifier);
}
yul::YulString translateIdentifier(yul::YulString _name) override
@@ -124,24 +94,114 @@ struct CopyTranslate: public yul::ASTCopier
if (!m_references.count(&_identifier))
return ASTCopier::translate(_identifier);
auto const& reference = m_references.at(&_identifier);
auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
solUnimplementedAssert(varDecl, "");
solAssert(
reference.isOffset == false && reference.isSlot == false,
"Should not be called for offset/slot"
);
auto const& var = m_context.localVariable(*varDecl);
solAssert(var.type().sizeOnStack() == 1, "");
return yul::Identifier{
_identifier.location,
yul::YulString{var.commaSeparatedList()}
};
yul::Expression translated = translateReference(_identifier);
solAssert(holds_alternative<yul::Identifier>(translated), "");
return get<yul::Identifier>(std::move(translated));
}
private:
/// Translates a reference to a local variable, potentially including
/// a suffix. Might return a literal, which causes this to be invalid in
/// lvalue-context.
yul::Expression translateReference(yul::Identifier const& _identifier)
{
auto const& reference = m_references.at(&_identifier);
auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
solUnimplementedAssert(varDecl, "");
string const& suffix = reference.suffix;
if (suffix.empty() && !varDecl->isStateVariable())
{
auto const& var = m_context.localVariable(*varDecl);
solAssert(var.type().sizeOnStack() == 1, "");
return yul::Identifier{
_identifier.location,
yul::YulString{var.commaSeparatedList()}
};
}
string value;
if (varDecl->isConstant())
{
VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl);
solAssert(variable, "");
if (variable->value()->annotation().type->category() == Type::Category::RationalNumber)
{
u256 intValue = dynamic_cast<RationalNumberType const&>(*variable->value()->annotation().type).literalValue(nullptr);
if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type()))
intValue <<= 256 - 8 * bytesType->numBytes();
else
solAssert(variable->type()->category() == Type::Category::Integer, "");
value = intValue.str();
}
else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get()))
{
TypePointer type = literal->annotation().type;
switch (type->category())
{
case Type::Category::Bool:
case Type::Category::Address:
solAssert(type->category() == variable->annotation().type->category(), "");
value = toCompactHexWithPrefix(type->literalValue(literal));
break;
case Type::Category::StringLiteral:
{
auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type);
solAssert(variable->type()->category() == Type::Category::FixedBytes, "");
unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes();
solAssert(stringLiteral.value().size() <= numBytes, "");
value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft)));
break;
}
default:
solAssert(false, "");
}
}
else
solAssert(false, "Invalid constant in inline assembly.");
}
else if (varDecl->isStateVariable())
{
if (suffix == "slot")
value = m_context.storageLocationOfStateVariable(*varDecl).first.str();
else if (suffix == "offset")
value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second);
else
solAssert(false, "");
}
else if (varDecl->type()->dataStoredIn(DataLocation::Storage))
{
solAssert(suffix == "slot" || suffix == "offset", "");
solAssert(varDecl->isLocalVariable(), "");
if (suffix == "slot")
value = IRVariable{*varDecl}.part("slot").name();
else if (varDecl->type()->isValueType())
value = IRVariable{*varDecl}.part("offset").name();
else
{
solAssert(!IRVariable{*varDecl}.hasPart("offset"), "");
value = "0";
}
}
else if (varDecl->type()->dataStoredIn(DataLocation::CallData))
{
solAssert(suffix == "offset" || suffix == "length", "");
value = IRVariable{*varDecl}.part(suffix).name();
}
else
solAssert(false, "");
if (isdigit(value.front()))
return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}};
else
return yul::Identifier{_identifier.location, yul::YulString{value}};
}
yul::Dialect const& m_dialect;
IRGenerationContext& m_context;
ExternalRefsMap const& m_references;
@@ -857,10 +917,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{
solAssert(!functionType->bound(), "");
if (auto contractType = dynamic_cast<ContractType const*>(expressionType->actualType()))
solUnimplementedAssert(
!contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal,
"Only internal function calls implemented for libraries"
);
if (contractType->contractDefinition().isLibrary())
solAssert(functionType->kind() == FunctionType::Kind::Internal || functionType->kind() == FunctionType::Kind::DelegateCall, "");
}
}
else
@@ -2143,9 +2201,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
}
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
handleVariableReference(*varDecl, _identifier);
else if (dynamic_cast<ContractDefinition const*>(declaration))
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(declaration))
{
// no-op
if (contract->isLibrary())
define(IRVariable(_identifier).part("address")) << linkerSymbol(*contract) << "\n";
}
else if (dynamic_cast<EventDefinition const*>(declaration))
{
@@ -2965,3 +3024,9 @@ void IRGeneratorForStatements::setLocation(ASTNode const& _node)
{
m_currentLocation = _node.location();
}
string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const
{
solAssert(_library.isLibrary(), "");
return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")";
}
@@ -186,6 +186,8 @@ private:
void setLocation(ASTNode const& _node);
std::string linkerSymbol(ContractDefinition const& _library) const;
std::ostringstream m_code;
IRGenerationContext& m_context;
YulUtilFunctions& m_utils;
+1 -1
View File
@@ -16,7 +16,7 @@
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Utilities to handle the Contract ABI (https://solidity.readthedocs.io/en/develop/abi-spec.html)
* Utilities to handle the Contract ABI (https://docs.soliditylang.org/en/develop/abi-spec.html)
*/
#include <libsolidity/interface/ABI.h>
+1 -1
View File
@@ -16,7 +16,7 @@
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Utilities to handle the Contract ABI (https://solidity.readthedocs.io/en/develop/abi-spec.html)
* Utilities to handle the Contract ABI (https://docs.soliditylang.org/en/develop/abi-spec.html)
*/
#pragma once