Browse Source

Merge pull request #1197 from seanyoung/sol-lamports-units

Allow sol and lamports to be used as units
Sean Young 2 years ago
parent
commit
09ea698be9

+ 6 - 0
CHANGELOG.md

@@ -2,6 +2,12 @@
 All notable changes to [Solang](https://github.com/hyperledger/solang/)
 will be documented here.
 
+## Unreleased
+
+### Added
+- The Solana units `sol` and `lamports` are now supported, e.g. `10 sol` and `100 lamports`.
+   [seanyoung](https://github.com/seanyoung)
+
 ## v0.2.2 Alexandria
 
 ### Added

+ 11 - 13
README.md

@@ -155,16 +155,17 @@ Here is a brief description of what we envision for the next versions.
 
 ### V0.3
 
-| Milestone                                  | Status      |
-|--------------------------------------------|-------------|
-| Call Solana's Rust contracts from Solidity | Completed   |
-| Improvements in overflow checking          | Completed   |
-| Support Solana's Program Derived Addresses | Completed   |
-| Call Solidity from Solana's Rust contracts | Not started |
-| Improve developer experience for Substrate | In progress |
-| Tooling for calls between ink! <> solidity | In progress |
-| Support chain extensions for Substrate     | Not started |
-| Provide CLI for node interactions          | Not started |
+| Milestone                                    | Status      |
+|----------------------------------------------|-------------|
+| Specify values as "1 sol" and "1e9 lamports" | Completed   |
+| Call Solana's Rust contracts from Solidity   | Completed   |
+| Improvements in overflow checking            | Completed   |
+| Support Solana's Program Derived Addresses   | Completed   |
+| Call Solidity from Solana's Rust contracts   | Not started |
+| Improve developer experience for Substrate   | In progress |
+| Tooling for calls between ink! <> solidity   | In progress |
+| Support chain extensions for Substrate       | Not started |
+| Provide CLI for node interactions            | Not started |
 
 
 ### V0.4
@@ -172,13 +173,10 @@ Here is a brief description of what we envision for the next versions.
 | Milestone                                          | Status      |
 |----------------------------------------------------|-------------|
 | Improve management over optimization passes        | Not started |
-| Specify values as "1 sol" and "1e9 lamports"       | In progress |
 | Adopt single static assignment for code generation | Not started |
 | Support openzeppelin on Substrate target           | Not started |
 | Provide Solidity -> Substrate porting guide        | Not started |
 
-
-
 ## License
 
 [Apache 2.0](LICENSE)

+ 18 - 15
docs/language/expressions.rst

@@ -160,26 +160,29 @@ code for both, so those fields will evaluate to the same value.
     then the contract code would need to contain itself as a constant array, which would
     result in an contract of infinite size.
 
-Ether and time units
-____________________
+Ether, Sol, and time units
+__________________________
 
 Any decimal numeric literal constant can have a unit denomination. For example
 ``10 minutes`` will evaluate to 600, i.e. the constant will be multiplied by the
 multiplier listed below. The following units are available:
 
-=========== =========================
-Unit        Multiplier
-
-``seconds`` 1
-``minutes`` 60
-``hours``   3600
-``days``    86400
-``weeks``   604800
-``wei``     1
-``ether``   1_000_000_000_000_000_000
-=========== =========================
-
-Note that ``ether``, ``wei`` and the other Ethereum currency denominations are available when not
+============ =========================
+Unit         Multiplier
+
+``seconds``  1
+``minutes``  60
+``hours``    3600
+``days``     86400
+``weeks``    604800
+``lamports`` 1
+``sol``      1_000_000_000
+``wei``      1
+``gwei``     1_000_000_000
+``ether``    1_000_000_000_000_000_000
+============ =========================
+
+Note that the Ethereum currency denominations ``ether``, ``gwei``, and ``wei`` are available when not
 compiling for Ethereum, but they will produce warnings.
 
 Casting

+ 2 - 0
solang-parser/README.md

@@ -4,6 +4,8 @@
 This crate is part of [Hyperledger Solang](https://solang.readthedocs.io/). It contains the
 parser for Solidity, including the dialects used by Solang for Solana and Substrate.
 
+This parser is compatible with Ethereum Solidity v0.8.18.
+
 ```rust
 use solang_parser::{pt::{SourceUnitPart, ContractPart}, parse};
 

+ 0 - 25
solang-parser/src/lexer.rs

@@ -151,15 +151,6 @@ pub enum Token<'input> {
     Receive,
     Fallback,
 
-    Seconds,
-    Minutes,
-    Hours,
-    Days,
-    Weeks,
-    Gwei,
-    Wei,
-    Ether,
-
     This,
     As,
     Is,
@@ -298,14 +289,6 @@ impl<'input> fmt::Display for Token<'input> {
             Token::Catch => write!(f, "catch"),
             Token::Receive => write!(f, "receive"),
             Token::Fallback => write!(f, "fallback"),
-            Token::Seconds => write!(f, "seconds"),
-            Token::Minutes => write!(f, "minutes"),
-            Token::Hours => write!(f, "hours"),
-            Token::Days => write!(f, "days"),
-            Token::Weeks => write!(f, "weeks"),
-            Token::Gwei => write!(f, "gwei"),
-            Token::Wei => write!(f, "wei"),
-            Token::Ether => write!(f, "ether"),
             Token::This => write!(f, "this"),
             Token::As => write!(f, "as"),
             Token::Is => write!(f, "is"),
@@ -543,14 +526,6 @@ static KEYWORDS: phf::Map<&'static str, Token> = phf_map! {
     "catch" => Token::Catch,
     "receive" => Token::Receive,
     "fallback" => Token::Fallback,
-    "seconds" => Token::Seconds,
-    "minutes" => Token::Minutes,
-    "hours" => Token::Hours,
-    "days" => Token::Days,
-    "weeks" => Token::Weeks,
-    "wei" => Token::Wei,
-    "gwei" => Token::Gwei,
-    "ether" => Token::Ether,
     "this" => Token::This,
     "as" => Token::As,
     "is" => Token::Is,

+ 4 - 19
solang-parser/src/pt.rs

@@ -459,19 +459,6 @@ pub struct NamedArgument {
     pub expr: Expression,
 }
 
-#[derive(Debug, PartialEq, Eq, Clone)]
-#[cfg_attr(feature = "pt-serde", derive(Serialize, Deserialize))]
-pub enum Unit {
-    Seconds(Loc),
-    Minutes(Loc),
-    Hours(Loc),
-    Days(Loc),
-    Weeks(Loc),
-    Wei(Loc),
-    Gwei(Loc),
-    Ether(Loc),
-}
-
 #[derive(Debug, PartialEq, Eq, Clone)]
 #[cfg_attr(feature = "pt-serde", derive(Serialize, Deserialize))]
 pub enum Expression {
@@ -529,9 +516,9 @@ pub enum Expression {
     AssignDivide(Loc, Box<Expression>, Box<Expression>),
     AssignModulo(Loc, Box<Expression>, Box<Expression>),
     BoolLiteral(Loc, bool),
-    NumberLiteral(Loc, String, String),
-    RationalNumberLiteral(Loc, String, String, String),
-    HexNumberLiteral(Loc, String),
+    NumberLiteral(Loc, String, String, Option<Identifier>),
+    RationalNumberLiteral(Loc, String, String, String, Option<Identifier>),
+    HexNumberLiteral(Loc, String, Option<Identifier>),
     StringLiteral(Vec<StringLiteral>),
     Type(Loc, Type),
     HexLiteral(Vec<HexLiteral>),
@@ -539,7 +526,6 @@ pub enum Expression {
     Variable(Identifier),
     List(Loc, ParameterList),
     ArrayLiteral(Loc, Vec<Expression>),
-    Unit(Loc, Box<Expression>, Unit),
     This(Loc),
 }
 
@@ -597,11 +583,10 @@ impl CodeLocation for Expression {
             | Expression::BoolLiteral(loc, _)
             | Expression::NumberLiteral(loc, ..)
             | Expression::RationalNumberLiteral(loc, ..)
-            | Expression::HexNumberLiteral(loc, _)
+            | Expression::HexNumberLiteral(loc, ..)
             | Expression::ArrayLiteral(loc, _)
             | Expression::List(loc, _)
             | Expression::Type(loc, _)
-            | Expression::Unit(loc, ..)
             | Expression::This(loc)
             | Expression::Variable(Identifier { loc, .. })
             | Expression::AddressLiteral(loc, _) => *loc,

+ 61 - 55
solang-parser/src/solidity.lalrpop

@@ -85,7 +85,7 @@ NoFunctionType: Type = {
     Bytes => Type::Bytes(<>),
     // prior to 0.8.0 `byte` used to be an alias for `bytes1`
     "byte" => Type::Bytes(1),
-    <l:@L> "mapping" "(" <k:AnyType> <key_name:SolIdentifier?> "=>" <v:AnyType> <value_name:SolIdentifier?> ")" <r:@R> => {
+    <l:@L> "mapping" "(" <k:Precedence0> <key_name:SolIdentifier?> "=>" <v:Precedence0> <value_name:SolIdentifier?> ")" <r:@R> => {
         Type::Mapping {
             loc: Loc::File(file_no, l, r),
             key: Box::new(k),
@@ -162,7 +162,7 @@ SolIdentifierPath: IdentifierPath = {
 }
 
 VariableDeclaration: VariableDeclaration = {
-    <l:@L> <ty:AnyType> <storage:StorageLocation?> <name:SolIdentifierOrError> <r:@R> => VariableDeclaration {
+    <l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:SolIdentifierOrError> <r:@R> => VariableDeclaration {
         loc: Loc::File(file_no, l, r), ty, storage, name
     },
 }
@@ -216,13 +216,13 @@ ContractDefinition: Box<ContractDefinition> = {
 }
 
 EventParameter: EventParameter = {
-    <l:@L> <ty:AnyType> <i:"indexed"?> <name:SolIdentifier?> <r:@R> => EventParameter{
+    <l:@L> <ty:Precedence0> <i:"indexed"?> <name:SolIdentifier?> <r:@R> => EventParameter{
         loc: Loc::File(file_no, l, r), ty, indexed: i.is_some(), name
     }
 }
 
 ErrorParameter: ErrorParameter = {
-    <l:@L> <ty:AnyType> <name:SolIdentifier?> <r:@R> => ErrorParameter{
+    <l:@L> <ty:Precedence0> <name:SolIdentifier?> <r:@R> => ErrorParameter{
         loc: Loc::File(file_no, l, r), ty, name
     }
 }
@@ -274,7 +274,7 @@ VariableDefinition: Box<VariableDefinition> = {
 }
 
 TypeDefinition: Box<TypeDefinition> = {
-    <l:@L> "type" <name:SolIdentifier> "is" <ty:AnyType> <r:@R> ";" => {
+    <l:@L> "type" <name:SolIdentifier> "is" <ty:Precedence0> <r:@R> ";" => {
         Box::new(TypeDefinition{
             loc: Loc::File(file_no, l, r), name, ty
         })
@@ -401,7 +401,28 @@ Precedence2: Expression = {
     <a:@L> "+" <e:Precedence2> <b:@R>  => Expression::UnaryPlus(Loc::File(file_no, a, b), Box::new(e)),
     <a:@L> "-" <e:Precedence2> <b:@R> => Expression::UnaryMinus(Loc::File(file_no, a, b), Box::new(e)),
     <l:@L> "revert" <r:@R> => Expression::Variable(Identifier{loc: Loc::File(file_no, l, r), name: "revert".to_string()}),
-    Precedence0,
+
+    Precedence1
+}
+
+Precedence1: Expression = {
+    <l:@L> <n:number> <unit:SolNoRevertIdentifier?> <r:@R> => {
+        let integer: String = n.0.chars().filter(|v| *v != '_').collect();
+        let exp: String = n.1.chars().filter(|v| *v != '_').collect();
+
+        Expression::NumberLiteral(Loc::File(file_no, l, r), integer, exp, unit)
+    },
+    <l:@L> <n:rational> <unit:SolNoRevertIdentifier?> <r:@R> => {
+        let integer: String = n.0.chars().filter(|v| *v != '_').collect();
+        let fraction: String = n.1.chars().filter(|v| *v != '_').collect();
+        let exp: String = n.2.chars().filter(|v| *v != '_').collect();
+
+        Expression::RationalNumberLiteral(Loc::File(file_no, l, r), integer, fraction, exp, unit)
+    },
+    <l:@L> <n:hexnumber> <unit:SolNoRevertIdentifier?> <r:@R> => {
+        Expression::HexNumberLiteral(Loc::File(file_no, l, r), n.to_owned(), unit)
+    },
+    Precedence0
 }
 
 NamedArgument: NamedArgument = {
@@ -432,10 +453,6 @@ FunctionCallPrecedence: Expression = {
     <l:@L> "type" <r:@R> => Expression::Variable(Identifier{loc: Loc::File(file_no, l, r), name: "type".to_string()}),
 }
 
-AnyType: Expression = {
-    Precedence0
-}
-
 Precedence0: Expression = {
     NoFunctionTyPrecedence0,
     FunctionTyPrecedence0
@@ -446,14 +463,14 @@ FunctionTyPrecedence0: Expression = {
 }
 
 NoFunctionTyPrecedence0: Expression = {
-    <a:@L> <e:Precedence0> "++" <b:@R> => Expression::PostIncrement(Loc::File(file_no, a, b), Box::new(e)),
-    <a:@L> <e:Precedence0> "--" <b:@R> => Expression::PostDecrement(Loc::File(file_no, a, b), Box::new(e)),
+    <a:@L> <e:Precedence1> "++" <b:@R> => Expression::PostIncrement(Loc::File(file_no, a, b), Box::new(e)),
+    <a:@L> <e:Precedence1> "--" <b:@R> => Expression::PostDecrement(Loc::File(file_no, a, b), Box::new(e)),
     <FunctionCall> => <>,
-    <a:@L> <e:Precedence0> "[" <i:Expression?> "]" <b:@R> => Expression::ArraySubscript(Loc::File(file_no, a, b), Box::new(e), i.map(Box::new)),
-    <a:@L> <e:Precedence0> "[" <l:Expression?> ":" <r:Expression?> "]" <b:@R> => Expression::ArraySlice(Loc::File(file_no, a, b), Box::new(e), l.map(Box::new), r.map(Box::new)),
-    <a:@L> <e:Precedence0> "." <i:SolIdentifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e), i),
+    <a:@L> <e:Precedence1> "[" <i:Expression?> "]" <b:@R> => Expression::ArraySubscript(Loc::File(file_no, a, b), Box::new(e), i.map(Box::new)),
+    <a:@L> <e:Precedence1> "[" <l:Expression?> ":" <r:Expression?> "]" <b:@R> => Expression::ArraySlice(Loc::File(file_no, a, b), Box::new(e), l.map(Box::new), r.map(Box::new)),
+    <a:@L> <e:Precedence1> "." <i:SolIdentifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e), i),
     // Solidity has ".address" members on external function types. Address is a keyword, so special casing needed
-    <a:@L> <e:Precedence0> "." <al:@L> "address" <b:@R> => {
+    <a:@L> <e:Precedence1> "." <al:@L> "address" <b:@R> => {
         Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e),
             Identifier { loc: Loc::File(file_no, al, b), name: "address".to_string() })
     },
@@ -462,8 +479,7 @@ NoFunctionTyPrecedence0: Expression = {
         Expression::ArrayLiteral(Loc::File(file_no, a, b), v)
     },
     <SolNoRevertIdentifier> => Expression::Variable(<>),
-    <l:@L> <e:Precedence0> <u:Unit> <r:@R> => Expression::Unit(Loc::File(file_no, l, r), Box::new(e), u),
-    <l:@L> <a:ParameterList> <r:@R> => {
+    <l:@L> <a:NamedParameterList> <r:@R> => {
         if a.len() == 1 {
             if let Some(Parameter{ ty, storage: None, name: None, .. }) = &a[0].1 {
                 // this means "(" Expression ")"
@@ -488,33 +504,6 @@ LiteralExpression: Expression = {
 
         Expression::AddressLiteral(Loc::File(file_no, l, r), a.chars().skip(8).filter(|c|  *c != '"' && *c != '\'').collect())
     },
-    <l:@L> <n:number> <r:@R> => {
-        let integer: String = n.0.chars().filter(|v| *v != '_').collect();
-        let exp: String = n.1.chars().filter(|v| *v != '_').collect();
-
-        Expression::NumberLiteral(Loc::File(file_no, l, r), integer, exp)
-    },
-    <l:@L> <n:rational> <r:@R> => {
-        let integer: String = n.0.chars().filter(|v| *v != '_').collect();
-        let fraction: String = n.1.chars().filter(|v| *v != '_').collect();
-        let exp: String = n.2.chars().filter(|v| *v != '_').collect();
-
-        Expression::RationalNumberLiteral(Loc::File(file_no, l, r), integer, fraction, exp)
-    },
-    <l:@L> <n:hexnumber> <r:@R> => {
-        Expression::HexNumberLiteral(Loc::File(file_no, l, r), n.to_owned())
-    }
-}
-
-Unit: Unit = {
-    <@L> "seconds" <@R> => Unit::Seconds(Loc::File(file_no, <>)),
-    <@L> "minutes" <@R> => Unit::Minutes(Loc::File(file_no, <>)),
-    <@L> "hours" <@R> => Unit::Hours(Loc::File(file_no, <>)),
-    <@L> "days" <@R> => Unit::Days(Loc::File(file_no, <>)),
-    <@L> "weeks" <@R> => Unit::Weeks(Loc::File(file_no, <>)),
-    <@L> "wei" <@R> => Unit::Wei(Loc::File(file_no, <>)),
-    <@L> "gwei" <@R> => Unit::Gwei(Loc::File(file_no, <>)),
-    <@L> "ether" <@R> => Unit::Ether(Loc::File(file_no, <>)),
 }
 
 StringLiteral: StringLiteral = {
@@ -537,7 +526,7 @@ HexLiteral: HexLiteral = {
 // and as an added bonus we can generate error messages about missing parameters/returns
 // to functions
 Parameter: Parameter = {
-    <l:@L> <ty:Expression> <storage:StorageLocation?> <name:SolIdentifier?> <r:@R> => {
+    <l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:SolIdentifier?> <r:@R> => {
         let loc = Loc::File(file_no, l, r);
         Parameter{loc, ty, storage, name}
     }
@@ -557,6 +546,31 @@ ParameterList: Vec<(Loc, Option<Parameter>)> = {
     }
 }
 
+NamedParameter: Parameter = {
+    <l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:SolIdentifier> <r:@R> => {
+        let loc = Loc::File(file_no, l, r);
+        Parameter{ loc, ty, storage, name: Some(name) }
+    },
+    <l:@L> <ty:Expression> <r:@R> => {
+        let loc = Loc::File(file_no, l, r);
+        Parameter{ loc, ty, storage: None, name: None }
+    }
+}
+
+OptNamedParameter: (Loc, Option<Parameter>) = {
+    <l:@L> <p:NamedParameter?> <r:@R> => (Loc::File(file_no, l, r), p),
+}
+
+NamedParameterList: Vec<(Loc, Option<Parameter>)> = {
+    "(" ")" => Vec::new(),
+    "(" <l:@L> <p:NamedParameter> <r:@R> ")" => vec!((Loc::File(file_no, l, r), Some(p))),
+    "(" <CommaTwo<OptNamedParameter>> ")" => <>,
+    "(" <false_token:!> ")"=> {
+        parser_errors.push(<>);
+        Vec::new()
+    }
+}
+
 Mutability: Mutability = {
     <l:@L> "pure" <r:@R> => Mutability::Pure(Loc::File(file_no, l, r)),
     <l:@L> "constant" <r:@R> => Mutability::Constant(Loc::File(file_no, l, r)),
@@ -686,7 +700,7 @@ FunctionDefinition: Box<FunctionDefinition> = {
 }
 
 Using: Box<Using> = {
-    <l:@L> "using" <list:UsingList> "for" <ty:AnyType> <global:SolIdentifier?> <r:@R> ";" => Box::new(Using {
+    <l:@L> "using" <list:UsingList> "for" <ty:Precedence0> <global:SolIdentifier?> <r:@R> ";" => Box::new(Using {
         loc: Loc::File(file_no, l, r),
         list,
         ty: Some(ty),
@@ -1140,14 +1154,6 @@ extern {
         "catch" => Token::Catch,
         "receive" => Token::Receive,
         "fallback" => Token::Fallback,
-        "seconds" => Token::Seconds,
-        "minutes" => Token::Minutes,
-        "hours" => Token::Hours,
-        "days" => Token::Days,
-        "weeks" => Token::Weeks,
-        "wei" => Token::Wei,
-        "gwei" => Token::Gwei,
-        "ether" => Token::Ether,
         "this" => Token::This,
         "as" => Token::As,
         "is" => Token::Is,

+ 9 - 14
solang-parser/src/test.rs

@@ -47,21 +47,13 @@ contract 9c {
                 Diagnostic { loc: File(0, 48, 49), level: Error, ty: ParserError, message: r#"unrecognised token ';', expected string"#.to_string(), notes: vec![] },
                 Diagnostic { loc: File(0, 62, 65), level: Error, ty: ParserError, message: r#"unrecognised token 'for', expected "(", ";", "=""#.to_string(), notes: vec![] },
                 Diagnostic { loc: File(0, 78, 79), level: Error, ty: ParserError, message: r#"unrecognised token '9', expected "case", "default", "leave", "revert", "switch", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 95, 96), level: Error, ty: ParserError, message: r#"unrecognised token '0', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 116, 123), level: Error, ty: ParserError, message: r#"unrecognised token 'uint256', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 164, 166), level: Error, ty: ParserError, message: r#"unrecognised token 'id', expected ";""#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 179, 180), level: Error, ty: ParserError, message: r#"unrecognised token '1', expected "case", "default", "leave", "revert", "switch", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 196, 197), level: Error, ty: ParserError, message: r#"unrecognised token '0', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 238, 242), level: Error, ty: ParserError, message: r#"unrecognised token 'uint256', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 292, 293), level: Error, ty: ParserError, message: r#"unrecognised token '0', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 334, 338), level: Error, ty: ParserError, message: r#"unrecognised token 'uint256', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 403, 404), level: Error, ty: ParserError, message: r#"unrecognised token '3', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
+                Diagnostic { loc: File(0, 95, 96), level: Error, ty: ParserError, message: "unrecognised token '0', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \"(\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \"=>\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"calldata\", \"case\", \"constant\", \"default\", \"external\", \"immutable\", \"indexed\", \"internal\", \"leave\", \"memory\", \"override\", \"private\", \"public\", \"revert\", \"storage\", \"switch\", \"{\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
+                Diagnostic { loc: File(0, 116, 123), level: Error, ty: ParserError, message: "unrecognised token 'uint256', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"case\", \"default\", \"leave\", \"switch\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
+                Diagnostic { loc: File(0, 403, 404), level: Error, ty: ParserError, message: "unrecognised token '3', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \"(\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \"=>\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"calldata\", \"case\", \"constant\", \"default\", \"external\", \"immutable\", \"indexed\", \"internal\", \"leave\", \"memory\", \"override\", \"private\", \"public\", \"revert\", \"storage\", \"switch\", \"{\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
                 Diagnostic { loc: File(0, 441, 442), level: Error, ty: ParserError, message: r#"unrecognised token '4', expected "(", "case", "default", "leave", "revert", "switch", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 460, 461), level: Error, ty: ParserError, message: r#"unrecognised token '!', expected "!=", "%", "%=", "&", "&&", "&=", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "payable", "private", "public", "pure", "return", "returns", "revert", "seconds", "storage", "switch", "view", "virtual", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 482, 483), level: Error, ty: ParserError, message: r#"unrecognised token '3', expected "!=", "%", "%=", "&", "&&", "&=", "(", ")", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "/", "/=", ":", ";", "<", "<<", "<<=", "<=", "=", "==", "=>", ">", ">=", ">>", ">>=", "?", "[", "]", "^", "^=", "calldata", "case", "constant", "days", "default", "ether", "external", "gwei", "hours", "immutable", "indexed", "internal", "leave", "memory", "minutes", "override", "private", "public", "revert", "seconds", "storage", "switch", "weeks", "wei", "{", "|", "|=", "||", "}", identifier"#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 495, 502), level: Error, ty: ParserError, message: r#"unrecognised token 'sesa_id', expected ")", ";""#.to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 518, 522), level: Error, ty: ParserError, message: "unrecognised token 'uint256', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \"(\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \"=>\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"calldata\", \"case\", \"constant\", \"days\", \"default\", \"ether\", \"external\", \"gwei\", \"hours\", \"immutable\", \"indexed\", \"internal\", \"leave\", \"memory\", \"minutes\", \"override\", \"private\", \"public\", \"revert\", \"seconds\", \"storage\", \"switch\", \"weeks\", \"wei\", \"{\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
-                Diagnostic { loc: File(0, 530, 531), level: Error, ty: ParserError, message: "unrecognised token 'b', expected \")\", \";\"".to_string(), notes: vec![] },
+                Diagnostic { loc: File(0, 460, 461), level: Error, ty: ParserError, message: "unrecognised token '!', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \"=>\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"calldata\", \"case\", \"constant\", \"default\", \"external\", \"immutable\", \"indexed\", \"internal\", \"leave\", \"memory\", \"override\", \"payable\", \"private\", \"public\", \"pure\", \"return\", \"returns\", \"revert\", \"storage\", \"switch\", \"view\", \"virtual\", \"{\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
+                Diagnostic { loc: File(0, 482, 483), level: Error, ty: ParserError, message: "unrecognised token '3', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \"(\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \"=>\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"calldata\", \"case\", \"constant\", \"default\", \"external\", \"immutable\", \"indexed\", \"internal\", \"leave\", \"memory\", \"override\", \"private\", \"public\", \"revert\", \"storage\", \"switch\", \"{\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
+                Diagnostic { loc: File(0, 518, 522), level: Error, ty: ParserError, message: "unrecognised token 'uint256', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"case\", \"default\", \"leave\", \"switch\", \"|\", \"|=\", \"||\", \"}\", identifier".to_string(), notes: vec![] },
                 Diagnostic { loc: File(0, 555, 556), level: Error, ty: ParserError, message: "unrecognised token '}', expected \"!\", \"(\", \"+\", \"++\", \"-\", \"--\", \"[\", \"address\", \"assembly\", \"bool\", \"break\", \"byte\", \"bytes\", \"case\", \"continue\", \"default\", \"delete\", \"do\", \"emit\", \"false\", \"for\", \"function\", \"if\", \"leave\", \"mapping\", \"new\", \"payable\", \"return\", \"revert\", \"string\", \"switch\", \"this\", \"true\", \"try\", \"type\", \"unchecked\", \"while\", \"{\", \"~\", Bytes, Int, Uint, address, hexnumber, hexstring, identifier, number, rational, string".to_string(), notes: vec![] },
                 Diagnostic { loc: File(0, 557, 558), level: Error, ty: ParserError, message: "unrecognised token '}', expected \"(\", \";\", \"@\", \"[\", \"abstract\", \"address\", \"bool\", \"byte\", \"bytes\", \"case\", \"contract\", \"default\", \"enum\", \"event\", \"false\", \"function\", \"import\", \"interface\", \"leave\", \"library\", \"mapping\", \"payable\", \"pragma\", \"string\", \"struct\", \"switch\", \"this\", \"true\", \"type\", \"using\", Bytes, Int, Uint, address, hexnumber, hexstring, identifier, number, rational, string".to_string(), notes: vec![] }
             ]
@@ -219,11 +211,13 @@ fn parse_test() {
                                 Loc::File(0, 765, 766),
                                 "1".to_string(),
                                 "".to_string(),
+                                None,
                             ),
                             Expression::NumberLiteral(
                                 Loc::File(0, 768, 769),
                                 "1".to_string(),
                                 "".to_string(),
+                                None,
                             ),
                         ],
                     ),
@@ -261,6 +255,7 @@ fn parse_test() {
                                             Loc::File(0, 830, 831),
                                             "2".to_string(),
                                             "".to_string(),
+                                            None,
                                         )),
                                     )],
                                 ),

+ 1 - 1
solang-parser/testdata/solidity

@@ -1 +1 @@
-Subproject commit d55b84ff63b1707f974f38c4d84088adb995b46a
+Subproject commit 87f61d960cceab32489350726a99c050e6f92c61

+ 47 - 0
src/sema/expression/literals.rs

@@ -518,6 +518,53 @@ pub(super) fn struct_literal(
     }
 }
 
+pub(crate) fn unit_literal(
+    loc: &pt::Loc,
+    unit: &Option<pt::Identifier>,
+    ns: &mut Namespace,
+    diagnostics: &mut Diagnostics,
+) -> BigInt {
+    if let Some(unit) = unit {
+        match unit.name.as_str() {
+            "wei" | "gwei" | "ether" if ns.target != crate::Target::EVM => {
+                diagnostics.push(Diagnostic::warning(
+                    *loc,
+                    format!("ethereum currency unit used while targeting {}", ns.target),
+                ));
+            }
+            "sol" | "lamports" if ns.target != crate::Target::Solana => {
+                diagnostics.push(Diagnostic::warning(
+                    *loc,
+                    format!("solana currency unit used while targeting {}", ns.target),
+                ));
+            }
+            _ => (),
+        }
+
+        match unit.name.as_str() {
+            "seconds" => BigInt::from(1),
+            "minutes" => BigInt::from(60),
+            "hours" => BigInt::from(60 * 60),
+            "days" => BigInt::from(60 * 60 * 24),
+            "weeks" => BigInt::from(60 * 60 * 24 * 7),
+            "wei" => BigInt::from(1),
+            "gwei" => BigInt::from(10).pow(9u32),
+            "ether" => BigInt::from(10).pow(18u32),
+            "sol" => BigInt::from(10).pow(9u32),
+            "lamports" => BigInt::from(1),
+            _ => {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    format!("unknown unit '{}'", unit.name),
+                ));
+                BigInt::from(1)
+            }
+        }
+    } else {
+        BigInt::from(1)
+    }
+}
+
 /// Resolve a struct literal with named fields
 pub(super) fn named_struct_literal(
     loc: &pt::Loc,

+ 35 - 82
src/sema/expression/resolve_expression.rs

@@ -15,7 +15,7 @@ use crate::sema::expression::integers::{
 };
 use crate::sema::expression::literals::{
     address_literal, array_literal, hex_literal, hex_number_literal, number_literal,
-    rational_number_literal, string_literal,
+    rational_number_literal, string_literal, unit_literal,
 };
 use crate::sema::expression::member_access::member_access;
 use crate::sema::expression::subscript::array_subscript;
@@ -27,7 +27,7 @@ use crate::sema::unused_variable::{
 };
 use crate::sema::Recurse;
 use num_bigint::BigInt;
-use num_traits::{Num, One};
+use num_traits::Num;
 use solang_parser::diagnostics::Diagnostic;
 use solang_parser::pt;
 use solang_parser::pt::CodeLocation;
@@ -63,28 +63,32 @@ pub fn expression(
             Ok(string_literal(v, context.file_no, diagnostics, resolve_to))
         }
         pt::Expression::HexLiteral(v) => hex_literal(v, diagnostics, resolve_to),
-        pt::Expression::NumberLiteral(loc, integer, exp) => number_literal(
-            loc,
-            integer,
-            exp,
-            ns,
-            &BigInt::one(),
-            diagnostics,
-            resolve_to,
-        ),
-        pt::Expression::RationalNumberLiteral(loc, integer, fraction, exp) => {
+        pt::Expression::NumberLiteral(loc, integer, exp, unit) => {
+            let unit = unit_literal(loc, unit, ns, diagnostics);
+
+            number_literal(loc, integer, exp, ns, &unit, diagnostics, resolve_to)
+        }
+        pt::Expression::RationalNumberLiteral(loc, integer, fraction, exp, unit) => {
+            let unit = unit_literal(loc, unit, ns, diagnostics);
+
             rational_number_literal(
                 loc,
                 integer,
                 fraction,
                 exp,
-                &BigInt::one(),
+                &unit,
                 ns,
                 diagnostics,
                 resolve_to,
             )
         }
-        pt::Expression::HexNumberLiteral(loc, n) => {
+        pt::Expression::HexNumberLiteral(loc, n, unit) => {
+            if unit.is_some() {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    "hexadecimal numbers cannot be used with unit denominations".into(),
+                ));
+            }
             hex_number_literal(loc, n, ns, diagnostics, resolve_to)
         }
         pt::Expression::AddressLiteral(loc, address) => {
@@ -279,16 +283,19 @@ pub fn expression(
             })
         }
         pt::Expression::UnaryMinus(loc, e) => match e.as_ref() {
-            pt::Expression::NumberLiteral(_, integer, exp) => number_literal(
-                loc,
-                integer,
-                exp,
-                ns,
-                &BigInt::from(-1),
-                diagnostics,
-                resolve_to,
-            ),
-            pt::Expression::HexNumberLiteral(_, v) => {
+            pt::Expression::NumberLiteral(_, integer, exp, unit) => {
+                let unit = unit_literal(loc, unit, ns, diagnostics);
+
+                number_literal(loc, integer, exp, ns, &-unit, diagnostics, resolve_to)
+            }
+            pt::Expression::HexNumberLiteral(_, v, unit) => {
+                if unit.is_some() {
+                    diagnostics.push(Diagnostic::error(
+                        *loc,
+                        "hexadecimal numbers cannot be used with unit denominations".into(),
+                    ));
+                }
+
                 // a hex literal with a minus before it cannot be an address literal or a bytesN value
                 let s: String = v.chars().skip(2).filter(|v| *v != '_').collect();
 
@@ -296,13 +303,15 @@ pub fn expression(
 
                 bigint_to_expression(loc, &-n, ns, diagnostics, resolve_to, Some(s.len()))
             }
-            pt::Expression::RationalNumberLiteral(loc, integer, fraction, exp) => {
+            pt::Expression::RationalNumberLiteral(loc, integer, fraction, exp, unit) => {
+                let unit = unit_literal(loc, unit, ns, diagnostics);
+
                 rational_number_literal(
                     loc,
                     integer,
                     fraction,
                     exp,
-                    &BigInt::from(-1),
+                    &-unit,
                     ns,
                     diagnostics,
                     resolve_to,
@@ -594,62 +603,6 @@ pub fn expression(
             ));
             Err(())
         }
-        pt::Expression::Unit(loc, expr, unit) => {
-            match unit {
-                pt::Unit::Wei(loc) | pt::Unit::Gwei(loc) | pt::Unit::Ether(loc)
-                    if ns.target != crate::Target::EVM =>
-                {
-                    diagnostics.push(Diagnostic::warning(
-                        *loc,
-                        "ethereum currency unit used while not targetting ethereum".to_owned(),
-                    ));
-                }
-                _ => (),
-            }
-
-            let unit = match unit {
-                pt::Unit::Seconds(_) => BigInt::from(1),
-                pt::Unit::Minutes(_) => BigInt::from(60),
-                pt::Unit::Hours(_) => BigInt::from(60 * 60),
-                pt::Unit::Days(_) => BigInt::from(60 * 60 * 24),
-                pt::Unit::Weeks(_) => BigInt::from(60 * 60 * 24 * 7),
-                pt::Unit::Wei(_) => BigInt::from(1),
-                pt::Unit::Gwei(_) => BigInt::from(10).pow(9u32),
-                pt::Unit::Ether(_) => BigInt::from(10).pow(18u32),
-            };
-
-            match expr.as_ref() {
-                pt::Expression::NumberLiteral(_, integer, exp) => {
-                    number_literal(loc, integer, exp, ns, &unit, diagnostics, resolve_to)
-                }
-                pt::Expression::RationalNumberLiteral(_, significant, mantissa, exp) => {
-                    rational_number_literal(
-                        loc,
-                        significant,
-                        mantissa,
-                        exp,
-                        &unit,
-                        ns,
-                        diagnostics,
-                        resolve_to,
-                    )
-                }
-                pt::Expression::HexNumberLiteral(loc, _) => {
-                    diagnostics.push(Diagnostic::error(
-                        *loc,
-                        "hexadecimal numbers cannot be used with unit denominations".to_owned(),
-                    ));
-                    Err(())
-                }
-                _ => {
-                    diagnostics.push(Diagnostic::error(
-                        *loc,
-                        "unit denominations can only be used with number literals".to_owned(),
-                    ));
-                    Err(())
-                }
-            }
-        }
         pt::Expression::This(loc) => match context.contract_no {
             Some(contract_no) => Ok(Expression::Builtin {
                 loc: *loc,

+ 16 - 13
src/sema/function_annotation.rs

@@ -4,7 +4,7 @@ use super::{
     ast::{ConstructorAnnotation, Diagnostic, Expression, Function, Namespace, Type},
     diagnostics::Diagnostics,
     eval::overflow_check,
-    expression::literals::hex_number_literal,
+    expression::literals::{hex_number_literal, unit_literal},
     expression::{ExprContext, ResolveTo},
     unused_variable::used_variable,
     Symtable,
@@ -12,8 +12,7 @@ use super::{
 use crate::sema::expression::literals::number_literal;
 use crate::sema::expression::resolve_expression::expression;
 use crate::Target;
-use num_bigint::BigInt;
-use num_traits::{One, ToPrimitive};
+use num_traits::ToPrimitive;
 use solang_parser::pt::{self, CodeLocation};
 
 /// Resolve the prototype annotation for functions (just the selector). These
@@ -94,18 +93,22 @@ fn function_selector(
                 let uint8 = Type::Uint(8);
 
                 let expr = match expr {
-                    pt::Expression::HexNumberLiteral(loc, n) => {
+                    pt::Expression::HexNumberLiteral(loc, n, None) => {
                         hex_number_literal(loc, n, ns, diagnostics, ResolveTo::Type(&uint8))
                     }
-                    pt::Expression::NumberLiteral(loc, base, exp) => number_literal(
-                        loc,
-                        base,
-                        exp,
-                        ns,
-                        &BigInt::one(),
-                        diagnostics,
-                        ResolveTo::Type(&uint8),
-                    ),
+                    pt::Expression::NumberLiteral(loc, base, exp, unit) => {
+                        let unit = unit_literal(loc, unit, ns, diagnostics);
+
+                        number_literal(
+                            loc,
+                            base,
+                            exp,
+                            ns,
+                            &unit,
+                            diagnostics,
+                            ResolveTo::Type(&uint8),
+                        )
+                    }
                     _ => {
                         diagnostics.push(Diagnostic::error(
                             expr.loc(),

+ 30 - 0
tests/contract_testcases/solana/expressions/units.dot

@@ -0,0 +1,30 @@
+strict digraph "tests/contract_testcases/solana/expressions/units.sol" {
+	contract [label="contract c\ntests/contract_testcases/solana/expressions/units.sol:2:9-8:10"]
+	test [label="function test\ncontract: c\ntests/contract_testcases/solana/expressions/units.sol:3:13-35\nsignature test()\nvisibility public\nmutability nonpayable"]
+	var_decl [label="variable decl int64 ether\ntests/contract_testcases/solana/expressions/units.sol:4:17-38"]
+	number_literal [label="int64 literal: 1000000000000000000\ntests/contract_testcases/solana/expressions/units.sol:4:31-38"]
+	var_decl_5 [label="variable decl int64 sol\ntests/contract_testcases/solana/expressions/units.sol:5:17-34"]
+	number_literal_6 [label="int64 literal: 1000000000\ntests/contract_testcases/solana/expressions/units.sol:5:29-34"]
+	var_decl_7 [label="variable decl int64 lamports\ntests/contract_testcases/solana/expressions/units.sol:6:17-44"]
+	number_literal_8 [label="int64 literal: 1\ntests/contract_testcases/solana/expressions/units.sol:6:34-44"]
+	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/solana/expressions/units.sol:2:9-8:10"]
+	diagnostic_11 [label="function can be declared 'pure'\nlevel Warning\ntests/contract_testcases/solana/expressions/units.sol:3:13-35"]
+	diagnostic_12 [label="local variable 'ether' has been assigned, but never read\nlevel Warning\ntests/contract_testcases/solana/expressions/units.sol:4:23-28"]
+	diagnostic_13 [label="ethereum currency unit used while targeting solana\nlevel Warning\ntests/contract_testcases/solana/expressions/units.sol:4:31-38"]
+	diagnostic_14 [label="local variable 'sol' has been assigned, but never read\nlevel Warning\ntests/contract_testcases/solana/expressions/units.sol:5:23-26"]
+	diagnostic_15 [label="local variable 'lamports' has been assigned, but never read\nlevel Warning\ntests/contract_testcases/solana/expressions/units.sol:6:23-31"]
+	contracts -> contract
+	contract -> test [label="function"]
+	test -> var_decl [label="body"]
+	var_decl -> number_literal [label="init"]
+	var_decl -> var_decl_5 [label="next"]
+	var_decl_5 -> number_literal_6 [label="init"]
+	var_decl_5 -> var_decl_7 [label="next"]
+	var_decl_7 -> number_literal_8 [label="init"]
+	diagnostics -> diagnostic [label="Debug"]
+	diagnostics -> diagnostic_11 [label="Warning"]
+	diagnostics -> diagnostic_12 [label="Warning"]
+	diagnostics -> diagnostic_13 [label="Warning"]
+	diagnostics -> diagnostic_14 [label="Warning"]
+	diagnostics -> diagnostic_15 [label="Warning"]
+}

+ 8 - 0
tests/contract_testcases/solana/expressions/units.sol

@@ -0,0 +1,8 @@
+
+        contract c {
+            function test() public {
+                int64 ether = 1 ether;
+                int64 sol = 1 sol;
+                int64 lamports = 1 lamports;
+            }
+        }

+ 1 - 1
tests/contract_testcases/solana/negative_exponent.dot

@@ -14,7 +14,7 @@ strict digraph "tests/contract_testcases/solana/negative_exponent.sol" {
 	h [label="function h\ncontract: c\ntests/contract_testcases/solana/negative_exponent.sol:8:5-44\nsignature h()\nvisibility public\nmutability pure"]
 	returns_14 [label="returns\nuint256 "]
 	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/solana/negative_exponent.sol:1:1-11:2"]
-	diagnostic_17 [label="ethereum currency unit used while not targetting ethereum\nlevel Warning\ntests/contract_testcases/solana/negative_exponent.sol:6:23-26"]
+	diagnostic_17 [label="ethereum currency unit used while targeting solana\nlevel Warning\ntests/contract_testcases/solana/negative_exponent.sol:6:16-26"]
 	diagnostic_18 [label="conversion to uint256 from rational not allowed\nlevel Error\ntests/contract_testcases/solana/negative_exponent.sol:9:2-20"]
 	contracts -> contract
 	contract -> f [label="function"]

+ 2 - 8
tests/contract_testcases/substrate/builtins/abi_decode_01.dot

@@ -1,10 +1,4 @@
 strict digraph "tests/contract_testcases/substrate/builtins/abi_decode_01.sol" {
-	contract [label="contract printer\ntests/contract_testcases/substrate/builtins/abi_decode_01.sol:2:9-6:10"]
-	test [label="function test\ncontract: printer\ntests/contract_testcases/substrate/builtins/abi_decode_01.sol:3:13-35\nsignature test()\nvisibility public\nmutability nonpayable"]
-	diagnostic [label="found contract 'printer'\nlevel Debug\ntests/contract_testcases/substrate/builtins/abi_decode_01.sol:2:9-6:10"]
-	diagnostic_5 [label="storage modifier 'storage' not allowed\nlevel Error\ntests/contract_testcases/substrate/builtins/abi_decode_01.sol:4:52-59"]
-	contracts -> contract
-	contract -> test [label="function"]
-	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_5 [label="Error"]
+	diagnostic [label="unrecognised token ')', expected \"case\", \"default\", \"leave\", \"revert\", \"switch\", identifier\nlevel Error\ntests/contract_testcases/substrate/builtins/abi_decode_01.sol:4:59-60"]
+	diagnostics -> diagnostic [label="Error"]
 }

+ 2 - 8
tests/contract_testcases/substrate/events/emit.dot

@@ -1,10 +1,4 @@
 strict digraph "tests/contract_testcases/substrate/events/emit.sol" {
-	contract [label="contract c\ntests/contract_testcases/substrate/events/emit.sol:2:9-6:10"]
-	f [label="function f\ncontract: c\ntests/contract_testcases/substrate/events/emit.sol:3:13-32\nsignature f()\nvisibility public\nmutability nonpayable"]
-	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/events/emit.sol:2:9-6:10"]
-	diagnostic_5 [label="expression found where type expected\nlevel Error\ntests/contract_testcases/substrate/events/emit.sol:4:22-23"]
-	contracts -> contract
-	contract -> f [label="function"]
-	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_5 [label="Error"]
+	diagnostic [label="unrecognised token '(', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"case\", \"default\", \"leave\", \"switch\", \"|\", \"|=\", \"||\", \"}\", identifier\nlevel Error\ntests/contract_testcases/substrate/events/emit.sol:4:24-25"]
+	diagnostics -> diagnostic [label="Error"]
 }

+ 24 - 2
tests/contract_testcases/substrate/expressions/destructure_02.dot

@@ -2,11 +2,33 @@ strict digraph "tests/contract_testcases/substrate/expressions/destructure_02.so
 	contract [label="contract test\ntests/contract_testcases/substrate/expressions/destructure_02.sol:1:1-8:10"]
 	foo [label="function foo\ncontract: test\ntests/contract_testcases/substrate/expressions/destructure_02.sol:2:13-42\nsignature foo(uint256)\nvisibility public\nmutability nonpayable"]
 	parameters [label="parameters\nuint256 bar"]
+	var_decl [label="variable decl int256 a\ntests/contract_testcases/substrate/expressions/destructure_02.sol:3:17-22"]
+	var_decl_5 [label="variable decl int256 b\ntests/contract_testcases/substrate/expressions/destructure_02.sol:4:17-22"]
+	destructure [label="destructure\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:17-32"]
+	variable [label="variable: a\nint256\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:18-19"]
+	variable_8 [label="variable: b\nint256\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:21-22"]
+	list [label="list\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:17-32"]
+	number_literal [label="int256 literal: 1\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:27-28"]
+	number_literal_11 [label="int256 literal: 2\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:30-31"]
 	diagnostic [label="found contract 'test'\nlevel Debug\ntests/contract_testcases/substrate/expressions/destructure_02.sol:1:1-8:10"]
-	diagnostic_6 [label="storage modifier 'memory' not permitted on assignment\nlevel Error\ntests/contract_testcases/substrate/expressions/destructure_02.sol:6:20-26"]
+	diagnostic_14 [label="function can be declared 'pure'\nlevel Warning\ntests/contract_testcases/substrate/expressions/destructure_02.sol:2:13-42"]
+	diagnostic_15 [label="function parameter 'bar' has never been read\nlevel Warning\ntests/contract_testcases/substrate/expressions/destructure_02.sol:2:31-34"]
+	diagnostic_16 [label="local variable 'a' has been assigned, but never read\nlevel Warning\ntests/contract_testcases/substrate/expressions/destructure_02.sol:3:21-22"]
+	diagnostic_17 [label="local variable 'b' has been assigned, but never read\nlevel Warning\ntests/contract_testcases/substrate/expressions/destructure_02.sol:4:21-22"]
 	contracts -> contract
 	contract -> foo [label="function"]
 	foo -> parameters [label="parameters"]
+	foo -> var_decl [label="body"]
+	var_decl -> var_decl_5 [label="next"]
+	var_decl_5 -> destructure [label="next"]
+	destructure -> variable [label="arg #0"]
+	destructure -> variable_8 [label="arg #1"]
+	destructure -> list [label="expr"]
+	list -> number_literal [label="entry #0"]
+	list -> number_literal_11 [label="entry #1"]
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_6 [label="Error"]
+	diagnostics -> diagnostic_14 [label="Warning"]
+	diagnostics -> diagnostic_15 [label="Warning"]
+	diagnostics -> diagnostic_16 [label="Warning"]
+	diagnostics -> diagnostic_17 [label="Warning"]
 }

+ 2 - 2
tests/contract_testcases/substrate/expressions/destructure_02.sol

@@ -3,6 +3,6 @@ contract test {
                 int a;
                 int b;
 
-                (a memory, b) = (1, 2);
+                (a, b) = (1, 2);
             }
-        }
+        }

+ 18 - 8
tests/contract_testcases/substrate/primitives/units.dot

@@ -1,16 +1,26 @@
 strict digraph "tests/contract_testcases/substrate/primitives/units.sol" {
-	contract [label="contract c\ntests/contract_testcases/substrate/primitives/units.sol:2:9-6:10"]
+	contract [label="contract c\ntests/contract_testcases/substrate/primitives/units.sol:2:9-9:10"]
 	test [label="function test\ncontract: c\ntests/contract_testcases/substrate/primitives/units.sol:3:13-35\nsignature test()\nvisibility public\nmutability nonpayable"]
-	var_decl [label="variable decl int32 x\ntests/contract_testcases/substrate/primitives/units.sol:4:17-34"]
-	number_literal [label="int32 literal: 1000000000000000000\ntests/contract_testcases/substrate/primitives/units.sol:4:27-34"]
-	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/primitives/units.sol:2:9-6:10"]
-	diagnostic_7 [label="value 1000000000000000000 does not fit into type int32.\nlevel Error\ntests/contract_testcases/substrate/primitives/units.sol:4:27-34"]
-	diagnostic_8 [label="ethereum currency unit used while not targetting ethereum\nlevel Warning\ntests/contract_testcases/substrate/primitives/units.sol:4:29-34"]
+	var_decl [label="variable decl int64 x\ntests/contract_testcases/substrate/primitives/units.sol:4:17-34"]
+	number_literal [label="int64 literal: 1000000000000000000\ntests/contract_testcases/substrate/primitives/units.sol:4:27-34"]
+	var_decl_5 [label="variable decl int64 wei\ntests/contract_testcases/substrate/primitives/units.sol:5:17-34"]
+	number_literal_6 [label="int64 literal: 1000000000\ntests/contract_testcases/substrate/primitives/units.sol:5:29-34"]
+	var_decl_7 [label="variable decl int64 dot\ntests/contract_testcases/substrate/primitives/units.sol:7:17-34"]
+	number_literal_8 [label="int64 literal: 1\ntests/contract_testcases/substrate/primitives/units.sol:7:29-34"]
+	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/primitives/units.sol:2:9-9:10"]
+	diagnostic_11 [label="ethereum currency unit used while targeting substrate\nlevel Warning\ntests/contract_testcases/substrate/primitives/units.sol:4:27-34"]
+	diagnostic_12 [label="solana currency unit used while targeting substrate\nlevel Warning\ntests/contract_testcases/substrate/primitives/units.sol:5:29-34"]
+	diagnostic_13 [label="unknown unit 'dot'\nlevel Error\ntests/contract_testcases/substrate/primitives/units.sol:7:29-34"]
 	contracts -> contract
 	contract -> test [label="function"]
 	test -> var_decl [label="body"]
 	var_decl -> number_literal [label="init"]
+	var_decl -> var_decl_5 [label="next"]
+	var_decl_5 -> number_literal_6 [label="init"]
+	var_decl_5 -> var_decl_7 [label="next"]
+	var_decl_7 -> number_literal_8 [label="init"]
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_7 [label="Error"]
-	diagnostics -> diagnostic_8 [label="Warning"]
+	diagnostics -> diagnostic_11 [label="Warning"]
+	diagnostics -> diagnostic_12 [label="Warning"]
+	diagnostics -> diagnostic_13 [label="Error"]
 }

+ 4 - 1
tests/contract_testcases/substrate/primitives/units.sol

@@ -1,6 +1,9 @@
 
         contract c {
             function test() public {
-                int32 x = 1 ether;
+                int64 x = 1 ether;
+                int64 wei = 1 sol;
+                // dot is not a unit, produce an error!
+                int64 dot = 1 dot;
             }
         }

+ 6 - 2
tests/contract_testcases/substrate/primitives/units_01.dot

@@ -1,10 +1,14 @@
 strict digraph "tests/contract_testcases/substrate/primitives/units_01.sol" {
 	contract [label="contract c\ntests/contract_testcases/substrate/primitives/units_01.sol:2:9-6:10"]
 	test [label="function test\ncontract: c\ntests/contract_testcases/substrate/primitives/units_01.sol:3:13-35\nsignature test()\nvisibility public\nmutability nonpayable"]
+	var_decl [label="variable decl int32 x\ntests/contract_testcases/substrate/primitives/units_01.sol:4:17-35"]
+	number_literal [label="int32 literal: 10\ntests/contract_testcases/substrate/primitives/units_01.sol:4:27-35"]
 	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/primitives/units_01.sol:2:9-6:10"]
-	diagnostic_5 [label="hexadecimal numbers cannot be used with unit denominations\nlevel Error\ntests/contract_testcases/substrate/primitives/units_01.sol:4:27-30"]
+	diagnostic_7 [label="hexadecimal numbers cannot be used with unit denominations\nlevel Error\ntests/contract_testcases/substrate/primitives/units_01.sol:4:27-35"]
 	contracts -> contract
 	contract -> test [label="function"]
+	test -> var_decl [label="body"]
+	var_decl -> number_literal [label="init"]
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_5 [label="Error"]
+	diagnostics -> diagnostic_7 [label="Error"]
 }

+ 2 - 8
tests/contract_testcases/substrate/primitives/units_02.dot

@@ -1,10 +1,4 @@
 strict digraph "tests/contract_testcases/substrate/primitives/units_02.sol" {
-	contract [label="contract c\ntests/contract_testcases/substrate/primitives/units_02.sol:2:9-6:10"]
-	test [label="function test\ncontract: c\ntests/contract_testcases/substrate/primitives/units_02.sol:3:13-35\nsignature test()\nvisibility public\nmutability nonpayable"]
-	diagnostic [label="found contract 'c'\nlevel Debug\ntests/contract_testcases/substrate/primitives/units_02.sol:2:9-6:10"]
-	diagnostic_5 [label="unit denominations can only be used with number literals\nlevel Error\ntests/contract_testcases/substrate/primitives/units_02.sol:4:27-39"]
-	contracts -> contract
-	contract -> test [label="function"]
-	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_5 [label="Error"]
+	diagnostic [label="unrecognised token 'days', expected \"!=\", \"%\", \"%=\", \"&\", \"&&\", \"&=\", \")\", \"*\", \"**\", \"*=\", \"+\", \"++\", \"+=\", \",\", \"-\", \"--\", \"-=\", \".\", \"/\", \"/=\", \":\", \";\", \"<\", \"<<\", \"<<=\", \"<=\", \"=\", \"==\", \">\", \">=\", \">>\", \">>=\", \"?\", \"[\", \"]\", \"^\", \"^=\", \"|\", \"|=\", \"||\", \"}\"\nlevel Error\ntests/contract_testcases/substrate/primitives/units_02.sol:4:35-39"]
+	diagnostics -> diagnostic [label="Error"]
 }