Skip to content

Commit

Permalink
Type check analysis (#5134)
Browse files Browse the repository at this point in the history
## Description

This PR introduces a typecheck analysis pass, which works in a similar
way to control flow graph analysis, but is specialized to ordering
issues.

We use it to build a graph that keeps track of function call
dependencies, which we use at a later stage during the type check
finalization stage to process nodes in order.

Right now this step is only used for processing impl self items in the
right order, but will be useful in a following PR for recursive call
chain analysis.

During impl self typechecking, we process each item into a dummy nodes
with deferred monomorphization, the nodes are then used for type check
analysis, which we use later to build the dependency graph. After that
we have the right order and re-type check the nodes with
monomorphization enabled.

Another PR will follow after this one is merged that introduces support
for impl self methods calling each other based on this code.

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
tritao authored Oct 19, 2023
1 parent 26d2fe1 commit 264fb70
Show file tree
Hide file tree
Showing 26 changed files with 816 additions and 89 deletions.
2 changes: 1 addition & 1 deletion sway-core/src/control_flow_analysis/flow_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ impl<'cfg> ControlFlowGraph<'cfg> {
let result = fs::write(graph_path.clone(), output);
if let Some(error) = result.err() {
tracing::error!(
"There was an issue while outputing DCA grap to path {graph_path:?}\n{error}"
"There was an issue while outputing DCA graph to path {graph_path:?}\n{error}"
);
}
}
Expand Down
6 changes: 4 additions & 2 deletions sway-core/src/decl_engine/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ use crate::{
type_system::*,
};

pub type DeclIdIndexType = usize;

/// An ID used to refer to an item in the [DeclEngine](super::decl_engine::DeclEngine)
pub struct DeclId<T>(usize, PhantomData<T>);
pub struct DeclId<T>(DeclIdIndexType, PhantomData<T>);

impl<T> fmt::Debug for DeclId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -22,7 +24,7 @@ impl<T> fmt::Debug for DeclId<T> {
}

impl<T> DeclId<T> {
pub(crate) fn inner(&self) -> usize {
pub(crate) fn inner(&self) -> DeclIdIndexType {
self.0
}
}
Expand Down
34 changes: 33 additions & 1 deletion sway-core/src/language/ty/ast_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
decl_engine::*,
engine_threading::*,
language::{parsed::TreeType, ty::*, Visibility},
semantic_analysis::{TypeCheckContext, TypeCheckFinalization, TypeCheckFinalizationContext},
semantic_analysis::{
TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext, TypeCheckFinalization,
TypeCheckFinalizationContext,
},
transform::{AllowDeprecatedState, AttributeKind},
type_system::*,
types::*,
Expand Down Expand Up @@ -112,6 +115,16 @@ impl UpdateConstantExpression for TyAstNode {
}
}

impl TypeCheckAnalysis for TyAstNode {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
self.content.type_check_analyze(handler, ctx)
}
}

impl TypeCheckFinalization for TyAstNode {
fn type_check_finalize(
&mut self,
Expand Down Expand Up @@ -432,6 +445,25 @@ impl HashWithEngines for TyAstNodeContent {
}
}

impl TypeCheckAnalysis for TyAstNodeContent {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
match self {
TyAstNodeContent::Declaration(node) => node.type_check_analyze(handler, ctx)?,
TyAstNodeContent::Expression(node) => node.type_check_analyze(handler, ctx)?,
TyAstNodeContent::ImplicitReturnExpression(node) => {
node.type_check_analyze(handler, ctx)?
}
TyAstNodeContent::SideEffect(_) => {}
TyAstNodeContent::Error(_, _) => {}
}
Ok(())
}
}

impl TypeCheckFinalization for TyAstNodeContent {
fn type_check_finalize(
&mut self,
Expand Down
33 changes: 31 additions & 2 deletions sway-core/src/language/ty/declaration/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use crate::{
},
engine_threading::*,
language::{parsed, Visibility},
semantic_analysis::type_check_context::MonomorphizeHelper,
semantic_analysis::{TypeCheckFinalization, TypeCheckFinalizationContext},
semantic_analysis::{
type_check_context::MonomorphizeHelper, TypeCheckAnalysis, TypeCheckAnalysisContext,
TypeCheckFinalization, TypeCheckFinalizationContext,
},
transform,
type_system::*,
};
Expand Down Expand Up @@ -142,6 +144,33 @@ impl HashWithEngines for TyTraitItem {
}
}

impl TypeCheckAnalysis for TyTraitItem {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
let decl_engine = ctx.engines.de();

match self {
TyTraitItem::Fn(node) => {
let item_fn = decl_engine.get_function(node);
item_fn.type_check_analyze(handler, ctx)?;
}
TyTraitItem::Constant(node) => {
let item_const = decl_engine.get_constant(node);
item_const.type_check_analyze(handler, ctx)?;
}
TyTraitItem::Type(node) => {
let item_type = decl_engine.get_type(node);
item_type.type_check_analyze(handler, ctx)?;
}
}

Ok(())
}
}

impl TypeCheckFinalization for TyTraitItem {
fn type_check_finalize(
&mut self,
Expand Down
16 changes: 15 additions & 1 deletion sway-core/src/language/ty/declaration/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use sway_types::Ident;
use crate::{
engine_threading::*,
language::ty::*,
semantic_analysis::{TypeCheckFinalization, TypeCheckFinalizationContext},
semantic_analysis::{
TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckFinalization,
TypeCheckFinalizationContext,
},
type_system::*,
};

Expand Down Expand Up @@ -59,6 +62,17 @@ impl SubstTypes for TyVariableDecl {
}
}

impl TypeCheckAnalysis for TyVariableDecl {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
self.body.type_check_analyze(handler, ctx)?;
Ok(())
}
}

impl TypeCheckFinalization for TyVariableDecl {
fn type_check_finalize(
&mut self,
Expand Down
15 changes: 14 additions & 1 deletion sway-core/src/language/ty/expression/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
decl_engine::*,
engine_threading::*,
language::{ty::*, Literal},
semantic_analysis::{TypeCheckContext, TypeCheckFinalization, TypeCheckFinalizationContext},
semantic_analysis::{
TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext, TypeCheckFinalization,
TypeCheckFinalizationContext,
},
transform::{AllowDeprecatedState, AttributeKind, AttributesMap},
type_system::*,
types::*,
Expand Down Expand Up @@ -96,6 +99,16 @@ impl DebugWithEngines for TyExpression {
}
}

impl TypeCheckAnalysis for TyExpression {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
self.expression.type_check_analyze(handler, ctx)
}
}

impl TypeCheckFinalization for TyExpression {
fn type_check_finalize(
&mut self,
Expand Down
126 changes: 112 additions & 14 deletions sway-core/src/language/ty/expression/expression_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::{
engine_threading::*,
language::{ty::*, *},
semantic_analysis::{
typed_expression::replace_decls_method_application, TypeCheckContext,
TypeCheckFinalization, TypeCheckFinalizationContext,
TyNodeDepGraphEdge, TyNodeDepGraphEdgeInfo, TypeCheckAnalysis, TypeCheckAnalysisContext,
TypeCheckContext, TypeCheckFinalization, TypeCheckFinalizationContext,
},
type_system::*,
};
Expand Down Expand Up @@ -898,6 +898,112 @@ impl ReplaceDecls for TyExpressionVariant {
}
}

impl TypeCheckAnalysis for TyExpressionVariant {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
match self {
TyExpressionVariant::Literal(_) => {}
TyExpressionVariant::FunctionApplication { fn_ref, .. } => {
let fn_node = ctx.get_node_from_impl_trait_fn_ref_app(fn_ref);
if let Some(fn_node) = fn_node {
ctx.add_edge_from_current(
fn_node,
TyNodeDepGraphEdge(TyNodeDepGraphEdgeInfo::FnApp),
);
}
}
TyExpressionVariant::LazyOperator { lhs, rhs, .. } => {
lhs.type_check_analyze(handler, ctx)?;
rhs.type_check_analyze(handler, ctx)?
}
TyExpressionVariant::ConstantExpression { const_decl, .. } => {
const_decl.type_check_analyze(handler, ctx)?
}
TyExpressionVariant::VariableExpression { .. } => {}
TyExpressionVariant::Tuple { fields } => {
for field in fields.iter() {
field.type_check_analyze(handler, ctx)?
}
}
TyExpressionVariant::Array { contents, .. } => {
for elem in contents.iter() {
elem.type_check_analyze(handler, ctx)?
}
}
TyExpressionVariant::ArrayIndex { prefix, index } => {
prefix.type_check_analyze(handler, ctx)?;
index.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::StructExpression { fields: _, .. } => {}
TyExpressionVariant::CodeBlock(block) => {
block.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::FunctionParameter => {}
TyExpressionVariant::MatchExp {
desugared,
scrutinees: _,
} => {
desugared.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::IfExp {
condition,
then,
r#else,
} => {
condition.type_check_analyze(handler, ctx)?;
then.type_check_analyze(handler, ctx)?;
if let Some(r#else) = r#else {
r#else.type_check_analyze(handler, ctx)?;
}
}
TyExpressionVariant::AsmExpression { .. } => {}
TyExpressionVariant::StructFieldAccess { prefix, .. } => {
prefix.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::TupleElemAccess { prefix, .. } => {
prefix.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::EnumInstantiation { contents, .. } => {
for expr in contents.iter() {
expr.type_check_analyze(handler, ctx)?
}
}
TyExpressionVariant::AbiCast { address, .. } => {
address.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::StorageAccess(_node) => {}
TyExpressionVariant::IntrinsicFunction(node) => {
for arg in node.arguments.iter() {
arg.type_check_analyze(handler, ctx)?
}
}
TyExpressionVariant::AbiName(_node) => {}
TyExpressionVariant::EnumTag { exp } => {
exp.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::UnsafeDowncast { exp, .. } => {
exp.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::WhileLoop { condition, body } => {
condition.type_check_analyze(handler, ctx)?;
body.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::Break => {}
TyExpressionVariant::Continue => {}
TyExpressionVariant::Reassignment(node) => {
node.type_check_analyze(handler, ctx)?;
}
TyExpressionVariant::Return(node) => {
node.type_check_analyze(handler, ctx)?;
}
}
Ok(())
}
}

impl TypeCheckFinalization for TyExpressionVariant {
fn type_check_finalize(
&mut self,
Expand All @@ -912,21 +1018,13 @@ impl TypeCheckFinalization for TyExpressionVariant {
deferred_monomorphization,
..
} => {
// If the function application was deferred we need to monomorphize it here.
// But at the moment monomorphization is fully resolved before type check finalization.
assert!(!(*deferred_monomorphization));

for (_, arg) in arguments.iter_mut() {
let _ = arg.type_check_finalize(handler, ctx);
}
// If the function application was deferred we need to monomorphize it.
// This is because sometimes we don't know the correct order to evaluate
// the items. So we create an initial "stub" typed function application node,
// run an analysis pass to compute a dependency graph, and then finalize type
// checking in the correct ordering.
if *deferred_monomorphization {
replace_decls_method_application(
self,
handler,
ctx.type_check_ctx.by_ref(),
)?;
}
}
TyExpressionVariant::LazyOperator { lhs, rhs, .. } => {
lhs.type_check_finalize(handler, ctx)?;
Expand Down
15 changes: 14 additions & 1 deletion sway-core/src/language/ty/expression/reassignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
decl_engine::*,
engine_threading::*,
language::ty::*,
semantic_analysis::{TypeCheckContext, TypeCheckFinalization, TypeCheckFinalizationContext},
semantic_analysis::{
TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext, TypeCheckFinalization,
TypeCheckFinalizationContext,
},
type_system::*,
};

Expand Down Expand Up @@ -71,6 +74,16 @@ impl ReplaceDecls for TyReassignment {
}
}

impl TypeCheckAnalysis for TyReassignment {
fn type_check_analyze(
&self,
handler: &Handler,
ctx: &mut TypeCheckAnalysisContext,
) -> Result<(), ErrorEmitted> {
self.rhs.type_check_analyze(handler, ctx)
}
}

impl TypeCheckFinalization for TyReassignment {
fn type_check_finalize(
&mut self,
Expand Down
7 changes: 6 additions & 1 deletion sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub use build_config::{BuildConfig, BuildTarget};
use control_flow_analysis::ControlFlowGraph;
use metadata::MetadataManager;
use query_engine::{ModuleCacheKey, ModulePath, ProgramsCacheEntry};
use semantic_analysis::{TypeCheckAnalysis, TypeCheckAnalysisContext};
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -484,6 +485,10 @@ pub fn parsed_to_ast(

typed_program.check_deprecated(engines, handler);

// Analyze the AST for dependency information.
let mut ctx = TypeCheckAnalysisContext::new(engines);
typed_program.type_check_analyze(handler, &mut ctx)?;

// Collect information about the types used in this program
let types_metadata_result = typed_program
.collect_types_metadata(handler, &mut CollectTypesMetadataContext::new(engines));
Expand Down Expand Up @@ -525,7 +530,7 @@ pub fn parsed_to_ast(
print_graph_url_format,
);

// Evaluate const declarations, to allow storage slots initializion with consts.
// Evaluate const declarations, to allow storage slots initialization with consts.
let mut ctx = Context::new(engines.se());
let mut md_mgr = MetadataManager::default();
let module = Module::new(&mut ctx, Kind::Contract);
Expand Down
Loading

0 comments on commit 264fb70

Please sign in to comment.