Skip to content

Commit

Permalink
Ensure well-formed and concrete types before IR gen (FuelLabs#1400)
Browse files Browse the repository at this point in the history
* Add well-formed type pass

* use constant for main fn check in ir

Co-authored-by: Alexander Hansen <[email protected]>
  • Loading branch information
sezna and Alexander Hansen authored Apr 27, 2022
1 parent c41a542 commit 6b911bf
Show file tree
Hide file tree
Showing 14 changed files with 385 additions and 6 deletions.
3 changes: 3 additions & 0 deletions sway-core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ pub const CONTRACT_CALL_COINS_PARAMETER_DEFAULT_VALUE: u64 = 0;

pub const CONTRACT_CALL_ASSET_ID_PARAMETER_NAME: &str = "asset_id";
pub const CONTRACT_CALL_ASSET_ID_PARAMETER_DEFAULT_VALUE: [u8; 32] = [0; 32];

/// The default entry point for scripts and predicates.
pub const DEFAULT_ENTRY_POINT_FN_NAME: &str = "main";
2 changes: 1 addition & 1 deletion sway-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ pub enum CompileError {
},
#[error("Unknown opcode: \"{op_name}\".")]
UnrecognizedOp { op_name: Ident, span: Span },
#[error("Generic type \"{ty}\" was unable to be inferred. Insufficient type information provided. Try annotating its type.")]
#[error("Cannot infer type for type parameter \"{ty}\". Insufficient type information provided. Try annotating its type.")]
UnableToInferGeneric { ty: String, span: Span },
#[error("The value \"{val}\" is too large to fit in this 6-bit immediate spot.")]
Immediate06TooLarge { val: u64, span: Span },
Expand Down
21 changes: 20 additions & 1 deletion sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,24 @@ pub(crate) fn compile_ast_to_ir_to_asm(
let mut warnings = Vec::new();
let mut errors = Vec::new();

// the IR pipeline relies on type information being fully resolved.
// If type information is found to still be generic or unresolved inside of
// IR, this is considered an internal compiler error. To resolve this situation,
// we need to explicitly ensure all types are resolved before going into IR.
//
// We _could_ introduce a new type here that uses TypeInfo instead of TypeId and throw away
// the engine, since we don't need inference for IR. That'd be a _lot_ of copy-pasted code,
// though, so instead, we are just going to do a pass and throw any unresolved generics as
// errors and then hold as a runtime invariant that none of the types will be unresolved in the
// IR phase.

check!(
ast.finalize_types(),
return err(warnings, errors),
warnings,
errors
);

let mut ir = match optimize::compile_ast(ast) {
Ok(ir) => ir,
Err(e) => {
Expand All @@ -478,7 +496,8 @@ pub(crate) fn compile_ast_to_ir_to_asm(
// a selector.
let mut functions_to_inline_to = Vec::new();
for (idx, fc) in &ir.functions {
if (matches!(tree_type, TreeType::Script | TreeType::Predicate) && fc.name == "main")
if (matches!(tree_type, TreeType::Script | TreeType::Predicate)
&& fc.name == crate::constants::DEFAULT_ENTRY_POINT_FN_NAME)
|| (tree_type == TreeType::Contract && fc.selector.is_some())
{
functions_to_inline_to.push(::sway_ir::function::Function(idx));
Expand Down
14 changes: 13 additions & 1 deletion sway-core/src/semantic_analysis/ast_node/declaration/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
TypedExpressionVariant, TypedReturnStatement, TypedVariableDeclaration,
VariableMutability,
},
create_new_scope, NamespaceWrapper, TypeCheckArguments,
create_new_scope, NamespaceWrapper, TypeCheckArguments, TypedAstNode, TypedAstNodeContent,
},
type_engine::*,
Ident, TypeParameter,
Expand Down Expand Up @@ -37,6 +37,18 @@ pub struct TypedFunctionDeclaration {
pub(crate) purity: Purity,
}

impl From<&TypedFunctionDeclaration> for TypedAstNode {
fn from(o: &TypedFunctionDeclaration) -> Self {
let span = o.span.clone();
TypedAstNode {
content: TypedAstNodeContent::Declaration(TypedDeclaration::FunctionDeclaration(
o.clone(),
)),
span,
}
}
}

// NOTE: Hash and PartialEq must uphold the invariant:
// k1 == k2 -> hash(k1) == hash(k2)
// https://doc.rust-lang.org/std/collections/struct.HashMap.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pub(crate) fn error_recovery_expr(span: Span) -> TypedExpression {

#[allow(clippy::too_many_arguments)]
impl TypedExpression {
/// If this expression deterministically_aborts 100% of the time, this function returns
/// `true`. Used in dead-code and control-flow analysis.
pub(crate) fn deterministically_aborts(&self) -> bool {
use TypedExpressionVariant::*;
match &self.expression {
Expand Down
29 changes: 29 additions & 0 deletions sway-core/src/semantic_analysis/ast_node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,35 @@ impl std::fmt::Display for TypedAstNode {
}

impl TypedAstNode {
/// Returns `true` if this AST node will be exported in a library, i.e. it is a public declaration.
pub(crate) fn is_public(&self) -> bool {
use TypedAstNodeContent::*;
match &self.content {
Declaration(decl) => decl.visibility().is_public(),
ReturnStatement(_)
| Expression(_)
| WhileLoop(_)
| SideEffect
| ImplicitReturnExpression(_) => false,
}
}

/// Naive check to see if this node is a function declaration of a function called `main` if
/// the [TreeType] is Script or Predicate.
pub(crate) fn is_main_function(&self, tree_type: TreeType) -> bool {
match &self {
TypedAstNode {
content:
TypedAstNodeContent::Declaration(TypedDeclaration::FunctionDeclaration(
TypedFunctionDeclaration { name, .. },
)),
..
} if name.as_str() == crate::constants::DEFAULT_ENTRY_POINT_FN_NAME => {
matches!(tree_type, TreeType::Script | TreeType::Predicate)
}
_ => false,
}
}
/// if this ast node _deterministically_ panics/aborts, then this is true.
/// This is used to assist in type checking branches that abort control flow and therefore
/// don't need to return a type.
Expand Down
35 changes: 35 additions & 0 deletions sway-core/src/semantic_analysis/syntax_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,41 @@ impl TypedParseTree {
}
}

/// Ensures there are no unresolved types or types awaiting resolution in the AST.
pub(crate) fn finalize_types(&self) -> CompileResult<()> {
use TypedParseTree::*;
// Get all of the entry points for this tree type. For libraries, that's everything
// public. For contracts, ABI entries. For scripts and predicates, any function named `main`.
let errors: Vec<_> = match self {
Library { all_nodes, .. } => all_nodes
.iter()
.filter(|x| x.is_public())
.flat_map(UnresolvedTypeCheck::check_for_unresolved_types)
.collect(),
Script { all_nodes, .. } => all_nodes
.iter()
.filter(|x| x.is_main_function(TreeType::Script))
.flat_map(UnresolvedTypeCheck::check_for_unresolved_types)
.collect(),
Predicate { all_nodes, .. } => all_nodes
.iter()
.filter(|x| x.is_main_function(TreeType::Predicate))
.flat_map(UnresolvedTypeCheck::check_for_unresolved_types)
.collect(),
Contract { abi_entries, .. } => abi_entries
.iter()
.map(TypedAstNode::from)
.flat_map(|x| x.check_for_unresolved_types())
.collect(),
};

if errors.is_empty() {
ok((), vec![], errors)
} else {
err(vec![], errors)
}
}

pub(crate) fn type_check(
parsed: ParseTree,
new_namespace: NamespaceRef,
Expand Down
6 changes: 3 additions & 3 deletions sway-core/src/type_engine.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use crate::error::*;

use sway_types::span::Span;

use std::iter::FromIterator;
use sway_types::span::Span;

mod engine;
mod integer_bits;
mod type_info;
mod unresolved_type_check;
pub use engine::*;
pub use integer_bits::*;
use sway_types::Property;
pub use type_info::*;
pub(crate) use unresolved_type_check::UnresolvedTypeCheck;

/// A identifier to uniquely refer to our type terms
pub type TypeId = usize;
Expand Down
Loading

0 comments on commit 6b911bf

Please sign in to comment.