Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable parser to accept transient as data location or identifier #15001

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Expand Up @@ -13,6 +13,7 @@ Bugfixes:

Language Features:
* Introduce a new overload ``require(bool, Error)`` that allows usage of ``require`` functions with custom errors. This feature is available in the ``via-ir`` pipeline only.
* Accept declarations of state variables with ``transient`` data location (parser support only, no code generation yet).


Compiler Features:
Expand Down
1 change: 1 addition & 0 deletions docs/grammar/SolidityLexer.g4
Expand Up @@ -85,6 +85,7 @@ SignedIntegerType:
Storage: 'storage';
String: 'string';
Struct: 'struct';
Transient: 'transient'; // not a real keyword
True: 'true';
Try: 'try';
Type: 'type';
Expand Down
5 changes: 3 additions & 2 deletions docs/grammar/SolidityParser.g4
Expand Up @@ -262,7 +262,7 @@ userDefinedValueTypeDefinition:
* The declaration of a state variable.
*/
stateVariableDeclaration
locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false]
locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false, boolean locationSet = false]
:
type=typeName
(
Expand All @@ -272,6 +272,7 @@ locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean
| {!$constantnessSet}? Constant {$constantnessSet = true;}
| {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;}
| {!$constantnessSet}? Immutable {$constantnessSet = true;}
| {!$locationSet}? Transient {$locationSet = true;}
)*
name=identifier
(Assign initialValue=expression)?
Expand Down Expand Up @@ -419,7 +420,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
/**
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
*/
identifier: Identifier | From | Error | Revert | Global;
identifier: Identifier | From | Error | Revert | Global | Transient;

literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;

Expand Down
27 changes: 25 additions & 2 deletions libsolidity/analysis/DeclarationTypeChecker.cpp
Expand Up @@ -407,6 +407,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
{
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::Transient: return "\"transient\"";
case Location::CallData: return "\"calldata\"";
case Location::Unspecified: return "none";
}
Expand Down Expand Up @@ -456,8 +457,24 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
switch (varLoc)
{
case Location::Unspecified:
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
break;
case Location::Transient:
if (_variable.isConstant() || _variable.immutable())
m_errorReporter.declarationError(
2197_error,
_variable.location(),
"Transient cannot be used as data location for constant or immutable variables."
);
typeLoc = DataLocation::Transient;
nikola-matic marked this conversation as resolved.
Show resolved Hide resolved
break;
default:
solAssert(false);
break;
}
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
Expand All @@ -477,6 +494,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Transient:
solUnimplementedAssert(false, "Transient data location cannot be used in this kind of variable or parameter declaration.");
break;
case Location::Unspecified:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
Expand All @@ -497,6 +517,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
}

if (!type->isValueType())
solUnimplementedAssert(typeLoc != DataLocation::Transient, "Transient data location is only supported for value types.");

_variable.annotation().type = type;
}

Expand Down
4 changes: 3 additions & 1 deletion libsolidity/ast/AST.cpp
Expand Up @@ -816,7 +816,9 @@ std::set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocation
{
using Location = VariableDeclaration::Location;

if (!hasReferenceOrMappingType() || isStateVariable() || isEventOrErrorParameter())
if (isStateVariable())
return std::set<Location>{Location::Unspecified, Location::Transient};
else if (!hasReferenceOrMappingType() || isEventOrErrorParameter())
return std::set<Location>{ Location::Unspecified };
else if (isCallableOrCatchParameter())
{
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/AST.h
Expand Up @@ -1052,7 +1052,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen
class VariableDeclaration: public Declaration, public StructurallyDocumented
{
public:
enum Location { Unspecified, Storage, Memory, CallData };
enum Location { Unspecified, Storage, Transient, Memory, CallData };
enum class Mutability { Mutable, Immutable, Constant };
static std::string mutabilityToString(Mutability _mutability)
{
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/ast/ASTJsonExporter.cpp
Expand Up @@ -1058,6 +1058,8 @@ std::string ASTJsonExporter::location(VariableDeclaration::Location _location)
return "memory";
case VariableDeclaration::Location::CallData:
return "calldata";
case VariableDeclaration::Location::Transient:
return "transient";
}
// To make the compiler happy
return {};
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/ast/ASTJsonImporter.cpp
Expand Up @@ -1190,6 +1190,8 @@ VariableDeclaration::Location ASTJsonImporter::location(Json const& _node)
return VariableDeclaration::Location::Memory;
else if (storageLocStr == "calldata")
return VariableDeclaration::Location::CallData;
else if (storageLocStr == "transient")
return VariableDeclaration::Location::Transient;
else
astAssert(false, "Unknown location declaration");

Expand Down
17 changes: 17 additions & 0 deletions libsolidity/ast/Types.cpp
Expand Up @@ -1544,6 +1544,8 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
return TypeProvider::emptyTuple();
case DataLocation::Storage:
return isPointer() ? nullptr : TypeProvider::emptyTuple();
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
}
return nullptr;
}
Expand Down Expand Up @@ -1571,6 +1573,9 @@ std::string ReferenceType::stringForReferencePart() const
return "calldata";
case DataLocation::Memory:
return "memory";
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
solAssert(false, "");
return "";
Expand All @@ -1584,6 +1589,9 @@ std::string ReferenceType::identifierLocationSuffix() const
case DataLocation::Storage:
id += "_storage";
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
id += "_memory";
break;
Expand Down Expand Up @@ -1748,6 +1756,9 @@ BoolResult ArrayType::validForLocation(DataLocation _loc) const
if (storageSizeUpperBound() >= bigint(1) << 256)
return BoolResult::err("Type too large for storage.");
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
return true;
}
Expand Down Expand Up @@ -1828,6 +1839,9 @@ std::vector<std::tuple<std::string, Type const*>> ArrayType::makeStackItems() co
case DataLocation::Storage:
// byte offset inside storage value is omitted
return {std::make_tuple("slot", TypeProvider::uint256())};
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
solAssert(false, "");
}
Expand Down Expand Up @@ -2568,6 +2582,9 @@ std::vector<std::tuple<std::string, Type const*>> StructType::makeStackItems() c
return {std::make_tuple("mpos", TypeProvider::uint256())};
case DataLocation::Storage:
return {std::make_tuple("slot", TypeProvider::uint256())};
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
solAssert(false, "");
}
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/Types.h
Expand Up @@ -70,7 +70,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato
return rational(_numerator, _denominator);
}

enum class DataLocation { Storage, CallData, Memory };
enum class DataLocation { Storage, Transient, CallData, Memory };


/**
Expand Down
6 changes: 6 additions & 0 deletions libsolidity/codegen/ArrayUtils.cpp
Expand Up @@ -1021,6 +1021,9 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept
if (_arrayType.isByteArrayOrString())
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
}
}
Expand Down Expand Up @@ -1118,6 +1121,9 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
m_context << endTag;
break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
}
}

Expand Down
9 changes: 9 additions & 0 deletions libsolidity/codegen/CompilerUtils.cpp
Expand Up @@ -1024,6 +1024,9 @@ void CompilerUtils::convertType(
"Invalid conversion to storage type."
);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
{
// Copy the array to a free position in memory, unless it is already in memory.
Expand Down Expand Up @@ -1168,6 +1171,9 @@ void CompilerUtils::convertType(
"Invalid conversion to storage type."
);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
// Copy the array to a free position in memory, unless it is already in memory.
switch (typeOnStack.location())
Expand Down Expand Up @@ -1207,6 +1213,9 @@ void CompilerUtils::convertType(
conversionImpl(m_context);
break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::CallData:
{
if (typeOnStack.isDynamicallyEncoded())
Expand Down
6 changes: 6 additions & 0 deletions libsolidity/codegen/ExpressionCompiler.cpp
Expand Up @@ -2047,6 +2047,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
ArrayUtils(m_context).retrieveLength(type);
m_context << Instruction::SWAP1 << Instruction::POP;
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
m_context << Instruction::MLOAD;
break;
Expand Down Expand Up @@ -2193,6 +2196,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
else
setLValueToStorageItem(_indexAccess);
break;
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
ArrayUtils(m_context).accessIndex(arrayType);
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArrayOrString());
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/codegen/YulUtilFunctions.cpp
Expand Up @@ -2563,6 +2563,9 @@ std::string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
templ("advance", toCompactHexWithPrefix(size));
break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::CallData:
{
u256 size = _type.calldataStride();
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Expand Up @@ -2314,6 +2314,9 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)

break;
}
case DataLocation::Transient:
solUnimplementedAssert(false, "Transient data location is only supported for value types.");
break;
case DataLocation::Memory:
{
std::string const indexAccessFunction = m_utils.memoryArrayIndexAccessFunction(arrayType);
Expand Down
12 changes: 12 additions & 0 deletions libsolidity/formal/ModelChecker.cpp
Expand Up @@ -62,6 +62,18 @@ bool ModelChecker::isPragmaPresent(std::vector<std::shared_ptr<SourceUnit>> cons

void ModelChecker::checkRequestedSourcesAndContracts(std::vector<std::shared_ptr<SourceUnit>> const& _sources)
{
if (m_settings.engine.any())
{
struct TransientDataLocationChecker: ASTConstVisitor
{
TransientDataLocationChecker(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
void endVisit(VariableDeclaration const& _var) { solUnimplementedAssert(_var.referenceLocation() != VariableDeclaration::Location::Transient); }
};

for (auto const& source: _sources)
TransientDataLocationChecker checker(*source);
}

std::map<std::string, std::set<std::string>> exist;
for (auto const& source: _sources)
for (auto node: source->nodes())
Expand Down
15 changes: 15 additions & 0 deletions libsolidity/interface/CompilerStack.cpp
Expand Up @@ -1467,6 +1467,11 @@ void CompilerStack::compileContract(
solUnimplementedAssert(!m_eofVersion.has_value(), "Experimental EOF support is only available for via-IR compilation.");
solAssert(m_stackState >= AnalysisSuccessful, "");

solUnimplementedAssert(ranges::all_of(
_contract.stateVariables(),
[](auto const* _stateVar) { return _stateVar->referenceLocation() != VariableDeclaration::Location::Transient; }
));

if (_otherCompilers.count(&_contract))
return;

Expand Down Expand Up @@ -1496,6 +1501,11 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisSuccessful, "");

solUnimplementedAssert(ranges::all_of(
_contract.stateVariables(),
[](auto const* _stateVar) { return _stateVar->referenceLocation() != VariableDeclaration::Location::Transient; }
));

Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
if (!compiledContract.yulIR.empty())
return;
Expand Down Expand Up @@ -1579,6 +1589,11 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisSuccessful, "");

solUnimplementedAssert(ranges::all_of(
_contract.stateVariables(),
[](auto const* _stateVar) { return _stateVar->referenceLocation() != VariableDeclaration::Location::Transient; }
));

if (!_contract.canBeDeployed())
return;

Expand Down
1 change: 1 addition & 0 deletions libsolidity/interface/StorageLayout.cpp
Expand Up @@ -46,6 +46,7 @@ Json StorageLayout::generate(ContractDefinition const& _contractDef)

Json StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset)
{
solUnimplementedAssert(_var.referenceLocation() != VariableDeclaration::Location::Transient);
Json varEntry;
Type const* varType = _var.type();

Expand Down
13 changes: 13 additions & 0 deletions libsolidity/parsing/Parser.cpp
Expand Up @@ -886,6 +886,19 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
}
}
else if (
cameel marked this conversation as resolved.
Show resolved Hide resolved
_options.kind == VarDeclKind::State &&
token == Token::Identifier &&
m_scanner->currentLiteral() == "transient" &&
m_scanner->peekNextToken() != Token::Assign &&
m_scanner->peekNextToken() != Token::Semicolon
)
{
if (location != VariableDeclaration::Location::Unspecified)
parserError(ErrorId{3548}, "Location already specified.");
else
location = VariableDeclaration::Location::Transient;
}
else
break;
nodeFactory.markEndPosition();
Expand Down
8 changes: 8 additions & 0 deletions test/libsolidity/astPropertyTests/transient_data_location.sol
@@ -0,0 +1,8 @@
contract C {
/// TestVarDataLocation: storageLocation
/// TestVarName: name
uint transient transient;
}
// ----
// TestVarDataLocation: transient
// TestVarName: transient
@@ -0,0 +1,4 @@
contract C {
int constant public transient = 0;
}
// ----
@@ -0,0 +1,5 @@
contract C {
int constant public transient x = 0;
}
nikola-matic marked this conversation as resolved.
Show resolved Hide resolved
// ----
// DeclarationError 2197: (17-52): Transient cannot be used as data location for constant or immutable variables.