variables.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. use super::{
  2. ast::{
  3. BuiltinStruct, Diagnostic, Expression, Function, Namespace, Parameter, Statement, Symbol,
  4. Type, Variable,
  5. },
  6. expression::{expression, ExprContext, ResolveTo},
  7. symtable::Symtable,
  8. symtable::{VariableInitializer, VariableUsage},
  9. tags::resolve_tags,
  10. };
  11. use crate::parser::pt::{self, CodeLocation, OptionalCodeLocation};
  12. pub struct DelayedResolveInitializer<'a> {
  13. var_no: usize,
  14. contract_no: usize,
  15. initializer: &'a pt::Expression,
  16. }
  17. pub fn contract_variables<'a>(
  18. def: &'a pt::ContractDefinition,
  19. file_no: usize,
  20. contract_no: usize,
  21. ns: &mut Namespace,
  22. ) -> Vec<DelayedResolveInitializer<'a>> {
  23. let mut symtable = Symtable::new();
  24. let mut delayed = Vec::new();
  25. for parts in &def.parts {
  26. if let pt::ContractPart::VariableDefinition(ref s) = parts {
  27. if let Some(delay) =
  28. variable_decl(Some(def), s, file_no, Some(contract_no), ns, &mut symtable)
  29. {
  30. delayed.push(delay);
  31. }
  32. }
  33. }
  34. delayed
  35. }
  36. pub fn variable_decl<'a>(
  37. contract: Option<&pt::ContractDefinition>,
  38. def: &'a pt::VariableDefinition,
  39. file_no: usize,
  40. contract_no: Option<usize>,
  41. ns: &mut Namespace,
  42. symtable: &mut Symtable,
  43. ) -> Option<DelayedResolveInitializer<'a>> {
  44. let mut attrs = def.attrs.clone();
  45. let mut ty = def.ty.clone();
  46. let mut ret = None;
  47. // For function types, the parser adds the attributes incl visibility to the type,
  48. // not the pt::VariableDefinition attrs. We need to chomp off the visibility
  49. // from the attributes before resolving the type
  50. if let pt::Expression::Type(
  51. _,
  52. pt::Type::Function {
  53. attributes,
  54. trailing_attributes,
  55. returns,
  56. ..
  57. },
  58. ) = &mut ty
  59. {
  60. if let Some(pt::FunctionAttribute::Visibility(v)) = trailing_attributes.last() {
  61. attrs.push(pt::VariableAttribute::Visibility(v.clone()));
  62. trailing_attributes.pop();
  63. } else if returns.is_empty() {
  64. if let Some(pt::FunctionAttribute::Visibility(v)) = attributes.last() {
  65. attrs.push(pt::VariableAttribute::Visibility(v.clone()));
  66. attributes.pop();
  67. }
  68. }
  69. }
  70. let mut diagnostics = Vec::new();
  71. let ty = match ns.resolve_type(file_no, contract_no, false, &ty, &mut diagnostics) {
  72. Ok(s) => s,
  73. Err(()) => {
  74. ns.diagnostics.extend(diagnostics);
  75. return None;
  76. }
  77. };
  78. let mut constant = false;
  79. let mut visibility: Option<pt::Visibility> = None;
  80. let mut has_immutable: Option<pt::Loc> = None;
  81. let mut has_override: Option<pt::Loc> = None;
  82. for attr in attrs {
  83. match &attr {
  84. pt::VariableAttribute::Constant(loc) => {
  85. if constant {
  86. ns.diagnostics.push(Diagnostic::error(
  87. *loc,
  88. "duplicate constant attribute".to_string(),
  89. ));
  90. }
  91. constant = true;
  92. }
  93. pt::VariableAttribute::Immutable(loc) => {
  94. if let Some(prev) = &has_immutable {
  95. ns.diagnostics.push(Diagnostic::error_with_note(
  96. *loc,
  97. "duplicate 'immutable' attribute".to_string(),
  98. *prev,
  99. "previous 'immutable' attribute".to_string(),
  100. ));
  101. }
  102. has_immutable = Some(*loc);
  103. }
  104. pt::VariableAttribute::Override(loc) => {
  105. if let Some(prev) = &has_override {
  106. ns.diagnostics.push(Diagnostic::error_with_note(
  107. *loc,
  108. "duplicate 'override' attribute".to_string(),
  109. *prev,
  110. "previous 'override' attribute".to_string(),
  111. ));
  112. }
  113. has_override = Some(*loc);
  114. }
  115. pt::VariableAttribute::Visibility(v) if contract_no.is_none() => {
  116. ns.diagnostics.push(Diagnostic::error(
  117. v.loc().unwrap(),
  118. format!("'{}': global variable cannot have visibility specifier", v),
  119. ));
  120. return None;
  121. }
  122. pt::VariableAttribute::Visibility(pt::Visibility::External(loc)) => {
  123. ns.diagnostics.push(Diagnostic::error(
  124. loc.unwrap(),
  125. "variable cannot be declared external".to_string(),
  126. ));
  127. return None;
  128. }
  129. pt::VariableAttribute::Visibility(v) => {
  130. if let Some(e) = &visibility {
  131. ns.diagnostics.push(Diagnostic::error_with_note(
  132. v.loc().unwrap(),
  133. format!("variable visibility redeclared '{}'", v),
  134. e.loc().unwrap(),
  135. format!("location of previous declaration of '{}'", e),
  136. ));
  137. return None;
  138. }
  139. visibility = Some(v.clone());
  140. }
  141. }
  142. }
  143. if let Some(loc) = &has_immutable {
  144. if constant {
  145. ns.diagnostics.push(Diagnostic::error(
  146. *loc,
  147. "variable cannot be declared both 'immutable' and 'constant'".to_string(),
  148. ));
  149. constant = false;
  150. }
  151. }
  152. let visibility = match visibility {
  153. Some(v) => v,
  154. None => pt::Visibility::Internal(Some(def.ty.loc())),
  155. };
  156. if let pt::Visibility::Public(_) = &visibility {
  157. // override allowed
  158. } else if let Some(loc) = &has_override {
  159. ns.diagnostics.push(Diagnostic::error(
  160. *loc,
  161. "only public variable can be declared 'override'".to_string(),
  162. ));
  163. has_override = None;
  164. }
  165. if let Some(contract) = contract {
  166. if matches!(contract.ty, pt::ContractTy::Interface(_))
  167. || (matches!(contract.ty, pt::ContractTy::Library(_)) && !constant)
  168. {
  169. ns.diagnostics.push(Diagnostic::error(
  170. def.loc,
  171. format!(
  172. "{} '{}' is not allowed to have contract variable '{}'",
  173. contract.ty, contract.name.name, def.name.name
  174. ),
  175. ));
  176. return None;
  177. }
  178. } else {
  179. if !constant {
  180. ns.diagnostics.push(Diagnostic::error(
  181. def.ty.loc(),
  182. "global variable must be constant".to_string(),
  183. ));
  184. return None;
  185. }
  186. if ty.contains_internal_function(ns) {
  187. ns.diagnostics.push(Diagnostic::error(
  188. def.ty.loc(),
  189. "global variable cannot be of type internal function".to_string(),
  190. ));
  191. return None;
  192. }
  193. }
  194. if ty.contains_internal_function(ns)
  195. && matches!(
  196. visibility,
  197. pt::Visibility::Public(_) | pt::Visibility::External(_)
  198. )
  199. {
  200. ns.diagnostics.push(Diagnostic::error(
  201. def.ty.loc(),
  202. format!(
  203. "variable of type internal function cannot be '{}'",
  204. visibility
  205. ),
  206. ));
  207. return None;
  208. } else if let Some(ty) = ty.contains_builtins(ns, BuiltinStruct::AccountInfo) {
  209. let message = format!("variable cannot be of builtin type '{}'", ty.to_string(ns));
  210. ns.diagnostics
  211. .push(Diagnostic::error(def.ty.loc(), message));
  212. return None;
  213. } else if let Some(ty) = ty.contains_builtins(ns, BuiltinStruct::AccountMeta) {
  214. let message = format!("variable cannot be of builtin type '{}'", ty.to_string(ns));
  215. ns.diagnostics
  216. .push(Diagnostic::error(def.ty.loc(), message));
  217. return None;
  218. }
  219. let initializer = if constant {
  220. if let Some(initializer) = &def.initializer {
  221. let mut diagnostics = Vec::new();
  222. let context = ExprContext {
  223. file_no,
  224. unchecked: false,
  225. contract_no,
  226. function_no: None,
  227. constant,
  228. lvalue: false,
  229. yul_function: false,
  230. };
  231. match expression(
  232. initializer,
  233. &context,
  234. ns,
  235. symtable,
  236. &mut diagnostics,
  237. ResolveTo::Type(&ty),
  238. ) {
  239. Ok(res) => {
  240. // implicitly conversion to correct ty
  241. match res.cast(&def.loc, &ty, true, ns, &mut diagnostics) {
  242. Ok(res) => Some(res),
  243. Err(_) => {
  244. ns.diagnostics.extend(diagnostics);
  245. None
  246. }
  247. }
  248. }
  249. Err(()) => {
  250. ns.diagnostics.extend(diagnostics);
  251. None
  252. }
  253. }
  254. } else {
  255. ns.diagnostics.push(Diagnostic::decl_error(
  256. def.loc,
  257. "missing initializer for constant".to_string(),
  258. ));
  259. None
  260. }
  261. } else {
  262. None
  263. };
  264. let bases: Vec<&str> = if let Some(contract) = contract {
  265. contract
  266. .base
  267. .iter()
  268. .map(|base| -> &str { &base.name.name })
  269. .collect()
  270. } else {
  271. Vec::new()
  272. };
  273. let tags = resolve_tags(
  274. def.name.loc.file_no(),
  275. if contract_no.is_none() {
  276. "global variable"
  277. } else {
  278. "state variable"
  279. },
  280. &def.doc,
  281. None,
  282. None,
  283. Some(&bases),
  284. ns,
  285. );
  286. let sdecl = Variable {
  287. name: def.name.name.to_string(),
  288. loc: def.loc,
  289. tags,
  290. visibility: visibility.clone(),
  291. ty: ty.clone(),
  292. constant,
  293. immutable: has_immutable.is_some(),
  294. assigned: def.initializer.is_some(),
  295. initializer,
  296. read: matches!(visibility, pt::Visibility::Public(_)),
  297. };
  298. let var_no = if let Some(contract_no) = contract_no {
  299. let var_no = ns.contracts[contract_no].variables.len();
  300. ns.contracts[contract_no].variables.push(sdecl);
  301. if !constant {
  302. if let Some(initializer) = &def.initializer {
  303. ret = Some(DelayedResolveInitializer {
  304. var_no,
  305. contract_no,
  306. initializer,
  307. });
  308. }
  309. }
  310. var_no
  311. } else {
  312. let var_no = ns.constants.len();
  313. ns.constants.push(sdecl);
  314. var_no
  315. };
  316. let success = ns.add_symbol(
  317. file_no,
  318. contract_no,
  319. &def.name,
  320. Symbol::Variable(def.loc, contract_no, var_no),
  321. );
  322. // for public variables in contracts, create an accessor function
  323. if success && matches!(visibility, pt::Visibility::Public(_)) {
  324. if let Some(contract_no) = contract_no {
  325. // The accessor function returns the value of the storage variable, constant or not.
  326. let mut expr = if constant {
  327. Expression::ConstantVariable(
  328. pt::Loc::Implicit,
  329. ty.clone(),
  330. Some(contract_no),
  331. var_no,
  332. )
  333. } else {
  334. Expression::StorageVariable(
  335. pt::Loc::Implicit,
  336. Type::StorageRef(false, Box::new(ty.clone())),
  337. contract_no,
  338. var_no,
  339. )
  340. };
  341. // If the variable is an array or mapping, the accessor function takes mapping keys
  342. // or array indices as arguments, and returns the dereferenced value
  343. let mut symtable = Symtable::new();
  344. let mut params = Vec::new();
  345. let ty = collect_parameters(&ty, &mut symtable, &mut params, &mut expr, ns);
  346. if ty.contains_mapping(ns) {
  347. // we can't return a mapping
  348. ns.diagnostics.push(Diagnostic::decl_error(
  349. def.loc,
  350. "mapping in a struct variable cannot be public".to_string(),
  351. ));
  352. }
  353. let mut func = Function::new(
  354. def.name.loc,
  355. def.name.name.to_owned(),
  356. Some(contract_no),
  357. Vec::new(),
  358. pt::FunctionTy::Function,
  359. // accessors for constant variables have view mutability
  360. Some(pt::Mutability::View(def.name.loc)),
  361. visibility,
  362. params,
  363. vec![Parameter {
  364. id: None,
  365. loc: def.name.loc,
  366. ty: ty.clone(),
  367. ty_loc: Some(def.ty.loc()),
  368. indexed: false,
  369. readonly: false,
  370. }],
  371. ns,
  372. );
  373. // Create the implicit body - just return the value
  374. func.body = vec![Statement::Return(
  375. pt::Loc::Implicit,
  376. Some(if constant {
  377. expr
  378. } else {
  379. Expression::StorageLoad(pt::Loc::Implicit, ty.clone(), Box::new(expr))
  380. }),
  381. )];
  382. func.is_accessor = true;
  383. func.has_body = true;
  384. func.is_override = has_override.map(|loc| (loc, Vec::new()));
  385. func.symtable = symtable;
  386. // add the function to the namespace and then to our contract
  387. let func_no = ns.functions.len();
  388. ns.functions.push(func);
  389. ns.contracts[contract_no].functions.push(func_no);
  390. // we already have a symbol for
  391. let symbol = Symbol::Function(vec![(def.loc, func_no)]);
  392. ns.function_symbols.insert(
  393. (
  394. def.loc.file_no(),
  395. Some(contract_no),
  396. def.name.name.to_owned(),
  397. ),
  398. symbol,
  399. );
  400. }
  401. }
  402. ret
  403. }
  404. /// For accessor functions, create the parameter list and the return expression
  405. fn collect_parameters<'a>(
  406. ty: &'a Type,
  407. symtable: &mut Symtable,
  408. params: &mut Vec<Parameter>,
  409. expr: &mut Expression,
  410. ns: &mut Namespace,
  411. ) -> &'a Type {
  412. match ty {
  413. Type::Mapping(key, value) => {
  414. let map = (*expr).clone();
  415. let id = pt::Identifier {
  416. loc: pt::Loc::Implicit,
  417. name: "".to_owned(),
  418. };
  419. let arg_ty = key.as_ref().clone();
  420. let arg_no = symtable
  421. .add(
  422. &id,
  423. arg_ty.clone(),
  424. ns,
  425. VariableInitializer::Solidity(None),
  426. VariableUsage::Parameter,
  427. None,
  428. )
  429. .unwrap();
  430. symtable.arguments.push(Some(arg_no));
  431. *expr = Expression::Subscript(
  432. pt::Loc::Implicit,
  433. ty.storage_array_elem(),
  434. Type::StorageRef(false, Box::new(ty.clone())),
  435. Box::new(map),
  436. Box::new(Expression::Variable(pt::Loc::Implicit, arg_ty, arg_no)),
  437. );
  438. params.push(Parameter {
  439. id: Some(id),
  440. loc: pt::Loc::Implicit,
  441. ty: key.as_ref().clone(),
  442. ty_loc: None,
  443. indexed: false,
  444. readonly: false,
  445. });
  446. collect_parameters(value, symtable, params, expr, ns)
  447. }
  448. Type::Array(elem_ty, dims) => {
  449. let mut ty = Type::StorageRef(false, Box::new(ty.clone()));
  450. for _ in 0..dims.len() {
  451. let map = (*expr).clone();
  452. let id = pt::Identifier {
  453. loc: pt::Loc::Implicit,
  454. name: "".to_owned(),
  455. };
  456. let arg_ty = Type::Uint(256);
  457. let var_no = symtable
  458. .add(
  459. &id,
  460. arg_ty.clone(),
  461. ns,
  462. VariableInitializer::Solidity(None),
  463. VariableUsage::Parameter,
  464. None,
  465. )
  466. .unwrap();
  467. symtable.arguments.push(Some(var_no));
  468. *expr = Expression::Subscript(
  469. pt::Loc::Implicit,
  470. ty.storage_array_elem(),
  471. ty.clone(),
  472. Box::new(map),
  473. Box::new(Expression::Variable(
  474. pt::Loc::Implicit,
  475. Type::Uint(256),
  476. var_no,
  477. )),
  478. );
  479. ty = ty.storage_array_elem();
  480. params.push(Parameter {
  481. id: Some(id),
  482. loc: pt::Loc::Implicit,
  483. ty: arg_ty,
  484. ty_loc: None,
  485. indexed: false,
  486. readonly: false,
  487. });
  488. }
  489. collect_parameters(elem_ty, symtable, params, expr, ns)
  490. }
  491. _ => ty,
  492. }
  493. }
  494. pub fn resolve_initializers(
  495. initializers: &[DelayedResolveInitializer],
  496. file_no: usize,
  497. ns: &mut Namespace,
  498. ) {
  499. let mut symtable = Symtable::new();
  500. let mut diagnostics = Vec::new();
  501. for DelayedResolveInitializer {
  502. var_no,
  503. contract_no,
  504. initializer,
  505. } in initializers
  506. {
  507. let var = &ns.contracts[*contract_no].variables[*var_no];
  508. let ty = var.ty.clone();
  509. let context = ExprContext {
  510. file_no,
  511. unchecked: false,
  512. contract_no: Some(*contract_no),
  513. function_no: None,
  514. constant: false,
  515. lvalue: false,
  516. yul_function: false,
  517. };
  518. if let Ok(res) = expression(
  519. initializer,
  520. &context,
  521. ns,
  522. &mut symtable,
  523. &mut diagnostics,
  524. ResolveTo::Type(&ty),
  525. ) {
  526. if let Ok(res) = res.cast(&initializer.loc(), &ty, true, ns, &mut diagnostics) {
  527. ns.contracts[*contract_no].variables[*var_no].initializer = Some(res);
  528. }
  529. }
  530. }
  531. ns.diagnostics.extend(diagnostics);
  532. }