Skip to content

Commit c0225a0

Browse files
authored
Merge pull request #16229 from argotorg/introduce-experimental-flag
Introduce experimental flag/option
2 parents 0409c5c + c61a2ae commit c0225a0

182 files changed

Lines changed: 1610 additions & 230 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Language Features:
44

55
Compiler Features:
6+
* Commandline Interface: Introduce `--experimental` flag required for enabling the experimental mode.
7+
* General: Restrict the existing experimental features (`generic-solidity`, `lsp`, `ethdebug`, `eof`, `evm`, `ast-import`, `evmasm-import`, `ir-ast`, `ssa-cfg`) to experimental mode.
8+
* Metadata: Store the state of the experimental mode in JSON and CBOR metadata. In CBOR this broadens the meaning of the existing `experimental` field, which used to indicate only the presence of certain experimental pragmas in the source.
9+
* Standard JSON Interface: Introduce `settings.experimental` setting required for enabling the experimental mode.
610

711
Bugfixes:
812

docs/metadata.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ explanatory purposes.
136136
"compilationTarget": {
137137
"myDirectory/myFile.sol": "MyContract"
138138
},
139+
// Optional (false if omitted): Indicates whether experimental mode has been enabled.
140+
// Always matches the value of the `experimental` flag in CBOR metadata.
141+
// Note that experimental mode being enabled does not necessarily mean that any
142+
// experimental features were actually used, or if they were, that those features
143+
// affected the bytecode.
144+
"experimental": true,
139145
// Required for Solidity: Addresses for libraries used.
140146
// Note that metadata has a different format for "libraries" field than the standard JSON input.
141147
// metadata format = { "MyLib.sol:MyLib": "0x123123..." }
@@ -197,12 +203,14 @@ Below are all the possible fields:
197203
.. code-block:: javascript
198204
199205
{
206+
// Present if "bytecodeHash" was "ipfs" in compiler settings
200207
"ipfs": "<metadata hash>",
201-
// If "bytecodeHash" was "bzzr1" in compiler settings not "ipfs" but "bzzr1"
208+
// Present if "bytecodeHash" was "bzzr1" in compiler settings
202209
"bzzr1": "<metadata hash>",
203210
// Previous versions were using "bzzr0" instead of "bzzr1"
204211
"bzzr0": "<metadata hash>",
205-
// If any experimental features that affect code generation are used
212+
// Present if experimental mode has been enabled either via "--experimental" flag or
213+
// "settings.experimental" option in Standard JSON
206214
"experimental": true,
207215
"solc": "<compiler version>"
208216
}

docs/using-the-compiler.rst

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ Input Description
281281
"stopAfter": "parsing",
282282
// Optional: List of remappings
283283
"remappings": [ ":g=/dir" ],
284+
// Optional: Experimental mode toggle (Default: false)
285+
// Makes it possible to use experimental features (but does not enable any such feature by itself).
286+
// The use of this mode is recorded in contract metadata.
287+
"experimental": true,
284288
// Optional: Optimizer settings
285289
"optimizer": {
286290
// Turn on the optimizer. Optional. Default: false.
@@ -383,8 +387,11 @@ Input Description
383387
// - `<end>` is the index of the first byte after that location.
384388
// - `snippet`: A single-line code snippet from the location indicated by `@src`.
385389
// The snippet is quoted and follows the corresponding `@src` annotation.
386-
// - `*`: Wildcard value that can be used to request everything.
387-
"debugInfo": ["location", "snippet"]
390+
// - `ast-id`: Annotations of the form `@ast-id <id>` over elements that can be mapped back to a definition in the original Solidity file.
391+
// `<id>` is a node ID in the Solidity AST ('ast' output).
392+
// - `ethdebug`: Ethdebug annotations (experimental).
393+
// - `*`: Wildcard value that can be used to request all non-experimental components.
394+
"debugInfo": ["location", "snippet", "ast-id", "ethdebug"]
388395
},
389396
// Metadata settings (optional)
390397
"metadata": {
@@ -435,13 +442,15 @@ Input Description
435442
// userdoc - User documentation (natspec)
436443
// metadata - Metadata
437444
// ir - Yul intermediate representation of the code before optimization
438-
// irAst - AST of Yul intermediate representation of the code before optimization
445+
// irAst - AST of Yul intermediate representation of the code before optimization (experimental)
439446
// irOptimized - Intermediate representation after optimization
440-
// irOptimizedAst - AST of intermediate representation after optimization
441-
// storageLayout - Slots, offsets and types of the contract's state variables in storage.
442-
// transientStorageLayout - Slots, offsets and types of the contract's state variables in transient storage.
447+
// irOptimizedAst - AST of intermediate representation after optimization (experimental)
448+
// storageLayout - Slots, offsets and types of the contract's state variables in storage
449+
// transientStorageLayout - Slots, offsets and types of the contract's state variables in transient storage
443450
// evm.assembly - New assembly format
444451
// evm.legacyAssembly - Old-style assembly format in JSON
452+
// evm.bytecode.ethdebug - Debug information in ethdebug format (ethdebug/format/program schema). Can only be requested when compiling via IR. (experimental)
453+
// evm.deployedBytecode.ethdebug - Like evm.bytecode.ethdebug, but for the runtime part of the contract (experimental)
445454
// evm.bytecode.functionDebugData - Debugging information at function level
446455
// evm.bytecode.object - Bytecode object
447456
// evm.bytecode.opcodes - Opcodes list
@@ -452,6 +461,7 @@ Input Description
452461
// evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables
453462
// evm.methodIdentifiers - The list of function hashes
454463
// evm.gasEstimates - Function gas estimates
464+
// yulCFGJson - Control Flow Graph (CFG) of the Single Static Assignment (SSA) form of the contract (experimental)
455465
//
456466
// Note that using `evm`, `evm.bytecode`, etc. will select every
457467
// target part of that output. Additionally, `*` can be used as a wildcard to request everything.
@@ -604,6 +614,8 @@ Output Description
604614
"legacyAssembly": {},
605615
// Bytecode and related details.
606616
"bytecode": {
617+
// Ethdebug output (experimental)
618+
"ethdebug": {/* ... */},
607619
// Debugging data at the level of functions.
608620
"functionDebugData": {
609621
// Now follows a set of functions including compiler-internal and
@@ -646,6 +658,8 @@ Output Description
646658
}
647659
},
648660
"deployedBytecode": {
661+
// Ethdebug output (experimental)
662+
"ethdebug": {/* ... */},
649663
/* ..., */ // The same layout as above.
650664
"immutableReferences": {
651665
// There are two references to the immutable with AST ID 3, both 32 bytes long. One is
@@ -670,11 +684,15 @@ Output Description
670684
"internal": {
671685
"heavyLifting()": "infinite"
672686
}
673-
}
687+
},
688+
// Yul CFG representation of the SSA form (experimental)
689+
"yulCFGJson": {/* ... */}
674690
}
675691
}
676692
}
677-
}
693+
},
694+
// Global Ethdebug output (experimental)
695+
"ethdebug": {/* ... */ }
678696
}
679697
680698
@@ -696,3 +714,57 @@ Error Types
696714
13. ``YulException``: Error during Yul code generation - this should be reported as an issue.
697715
14. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible.
698716
15. ``Info``: Information that the compiler thinks the user might find useful, but is not dangerous and does not necessarily need to be addressed.
717+
718+
.. index:: ! Experimental mode, ! --experimental
719+
.. _experimental-mode:
720+
721+
Experimental Mode
722+
*****************
723+
724+
Some language and compiler features included in stable releases are not themselves considered stable.
725+
They are sparsely documented, if at all, often not adequately tested, and thus not yet intended for production use.
726+
In many cases it is possible to develop a big feature incrementally, with each iteration being already stable.
727+
Sometimes, however, it is preferable to start with a prototype and stabilize it over multiple releases, while receiving feedback from users.
728+
To prevent accidental use, such features can be only accessed by enabling the experimental mode.
729+
730+
There are no backwards compatibility guarantees for experimental features.
731+
They are subject to change in breaking ways in non-breaking releases of the compiler.
732+
Only major changes affecting them are recorded in the changelog.
733+
734+
To enable the experimental mode, use the ``--experimental`` flag on the command line,
735+
or the analogous ``settings.experimental`` boolean setting in the Standard JSON input.
736+
737+
Note that the use of this mode is recorded in the metadata:
738+
739+
- ``experimental`` flag in CBOR metadata is set to ``true``,
740+
- ``settings.experimental`` in JSON metadata is set to ``true``,
741+
742+
.. note::
743+
Prior to version 0.8.34, most of the experimental features were usable without any extra safeguards.
744+
Some were gated behind ``pragma experimental``, but this was not done consistently.
745+
The information about them was also only recorded in CBOR metadata and even then not always.
746+
The main goal of the experimental mode is to systematize this and make users fully aware when relying on features which are unfinished or not production-ready.
747+
748+
The table below details all currently available experimental features.
749+
750+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
751+
| Feature | ID | Affects bytecode | Flag/pragma |
752+
+=======================+==========================+==================+===================================================================+
753+
| AST import | ``ast-import`` | yes | ``--import-ast`` |
754+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
755+
| LSP | ``lsp`` | no | ``--lsp`` |
756+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
757+
| EVM Assembly import | ``evmasm-import`` | yes | ``--import-asm-json`` |
758+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
759+
| Generic Solidity | ``generic-solidity`` | yes | ``pragma experimental solidity`` |
760+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
761+
| IR AST | ``ir-ast`` | no | ``--ir-ast-json``, ``--ir-optimized-ast-json`` |
762+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
763+
| EOF | ``eof`` | yes | ``--experimental-eof-version`` |
764+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
765+
| Non-mainnet EVMs | ``evm`` | yes | ``--evm-version <version name>`` |
766+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
767+
| Ethdebug | ``ethdebug`` | no | ``--ethdebug``, ``--ethdebug-runtime``, ``--debug-info ethdebug`` |
768+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+
769+
| Yul SSA CFG exporter | ``ssa-cfg`` | no | ``--yul-cfg-json`` |
770+
+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+

libsolidity/analysis/SyntaxChecker.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
110110
auto feature = ExperimentalFeatureNames.at(literal);
111111
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
112112
if (!ExperimentalFeatureWithoutWarning.count(feature))
113-
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
113+
{
114+
if (!m_experimental)
115+
m_errorReporter.syntaxError(
116+
2816_error,
117+
_pragma.location(),
118+
"Experimental pragmas can only be used if experimental mode is enabled. To enable experimental mode, use the --experimental flag."
119+
);
120+
else
121+
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
122+
}
114123

115124
if (feature == ExperimentalFeature::ABIEncoderV2)
116125
{

libsolidity/analysis/SyntaxChecker.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@ class SyntaxChecker: private ASTConstVisitor
4545
{
4646
public:
4747
/// @param _errorReporter provides the error logging functionality.
48-
SyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer):
48+
/// @param _useYulOptimizer indicates whether Yul optimizer is enabled.
49+
/// @param _experimental indicates whether the experimental mode is enabled.
50+
SyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer, bool _experimental):
4951
m_errorReporter(_errorReporter),
50-
m_useYulOptimizer(_useYulOptimizer)
52+
m_useYulOptimizer(_useYulOptimizer),
53+
m_experimental(_experimental)
5154
{}
5255

5356
bool checkSyntax(ASTNode const& _astRoot);
@@ -112,6 +115,9 @@ class SyntaxChecker: private ASTConstVisitor
112115
/// Flag that indicates whether we are inside an unchecked block.
113116
bool m_uncheckedArithmetic = false;
114117

118+
/// Flag that indicates whether the experimental mode is enabled.
119+
bool m_experimental = false;
120+
115121
int m_inLoopDepth = 0;
116122
std::optional<ContractKind> m_currentContractKind;
117123

libsolidity/interface/CompilerStack.cpp

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ void CompilerStack::setViaIR(bool _viaIR)
229229
m_viaIR = _viaIR;
230230
}
231231

232+
void CompilerStack::setExperimental(bool _experimental)
233+
{
234+
solAssert(m_stackState < ParsedAndImported, "Must set experimental before parsing.");
235+
m_experimental = _experimental;
236+
}
237+
232238
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
233239
{
234240
solAssert(m_stackState < ParsedAndImported, "Must set EVM version before parsing.");
@@ -450,6 +456,19 @@ void CompilerStack::importASTs(std::map<std::string, Json> const& _sources)
450456
storeContractDefinitions();
451457
}
452458

459+
namespace
460+
{
461+
462+
bool onlySafeExperimentalFeaturesActivated(std::set<ExperimentalFeature> const& _features)
463+
{
464+
for (auto const feature: _features)
465+
if (!ExperimentalFeatureWithoutWarning.contains(feature))
466+
return false;
467+
return true;
468+
}
469+
470+
}
471+
453472
bool CompilerStack::analyze()
454473
{
455474
solAssert(m_stackState == ParsedAndImported, "Must call analyze only after parsing was successful.");
@@ -467,7 +486,7 @@ bool CompilerStack::analyze()
467486
{
468487
bool experimentalSolidity = isExperimentalSolidity();
469488

470-
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser);
489+
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser, m_experimental);
471490
for (Source const* source: m_sourceOrder)
472491
if (source->ast && !syntaxChecker.checkSyntax(*source->ast))
473492
noErrors = false;
@@ -527,6 +546,10 @@ bool CompilerStack::analyze()
527546
if (!noErrors)
528547
return false;
529548

549+
for (Source const* source: m_sourceOrder)
550+
if (source->ast && !m_experimental)
551+
solAssert(onlySafeExperimentalFeaturesActivated(source->ast->annotation().experimentalFeatures));
552+
530553
m_stackState = AnalysisSuccessful;
531554
return true;
532555
}
@@ -1446,17 +1469,6 @@ void CompilerStack::annotateInternalFunctionIDs()
14461469
}
14471470
}
14481471

1449-
namespace
1450-
{
1451-
bool onlySafeExperimentalFeaturesActivated(std::set<ExperimentalFeature> const& features)
1452-
{
1453-
for (auto const feature: features)
1454-
if (!ExperimentalFeatureWithoutWarning.count(feature))
1455-
return false;
1456-
return true;
1457-
}
1458-
}
1459-
14601472
void CompilerStack::assembleYul(
14611473
ContractDefinition const& _contract,
14621474
std::shared_ptr<evmasm::Assembly> _assembly,
@@ -1816,6 +1828,8 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR
18161828
if (_forIR)
18171829
meta["settings"]["viaIR"] = _forIR;
18181830
meta["settings"]["evmVersion"] = m_evmVersion.name();
1831+
if (m_experimental)
1832+
meta["settings"]["experimental"] = m_experimental;
18191833
if (m_eofVersion.has_value())
18201834
meta["settings"]["eofVersion"] = *m_eofVersion;
18211835
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
@@ -1927,10 +1941,14 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR)
19271941
if (m_metadataFormat == MetadataFormat::NoMetadata)
19281942
return bytes{};
19291943

1930-
bool const experimentalMode = !onlySafeExperimentalFeaturesActivated(
1944+
bool const usesExperimentalSyntax = !_contract.contract->sourceUnit().annotation().experimentalFeatures.empty();
1945+
bool const onlySafeExperimentalFeatures = onlySafeExperimentalFeaturesActivated(
19311946
_contract.contract->sourceUnit().annotation().experimentalFeatures
19321947
);
19331948

1949+
if (m_eofVersion.has_value() || (usesExperimentalSyntax && !onlySafeExperimentalFeatures))
1950+
solAssert(m_experimental, "Experimental mode not enabled");
1951+
19341952
std::string meta = (_forIR == m_viaIR ? metadata(_contract) : createMetadata(_contract, _forIR));
19351953

19361954
MetadataCBOREncoder encoder;
@@ -1942,7 +1960,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR)
19421960
else
19431961
solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash");
19441962

1945-
if (experimentalMode || m_eofVersion.has_value())
1963+
if (m_experimental)
19461964
encoder.pushBool("experimental", true);
19471965
if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag)
19481966
encoder.pushBytes("solc", VersionCompactBytes);

libsolidity/interface/CompilerStack.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
223223
/// Must be set before parsing.
224224
void setViaIR(bool _viaIR);
225225

226+
/// Sets the experimental toggle to allow usage of experimental features.
227+
void setExperimental(bool _experimental);
228+
226229
/// Set the EVM version used before running compile.
227230
/// When called without an argument it will revert to the default version.
228231
/// Must be set before parsing.
@@ -607,6 +610,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
607610
RevertStrings m_revertStrings = RevertStrings::Default;
608611
State m_stopAfter = State::CompilationSuccessful;
609612
bool m_viaIR = false;
613+
bool m_experimental = false;
610614
langutil::EVMVersion m_evmVersion;
611615
std::optional<uint8_t> m_eofVersion;
612616
ModelCheckerSettings m_modelCheckerSettings;

0 commit comments

Comments
 (0)