Skip to content

Commit

Permalink
Support feature at module and fn level.
Browse files Browse the repository at this point in the history
commit-id:22ee9464
  • Loading branch information
orizi committed Jan 28, 2024
1 parent 9e25909 commit bf45849
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 40 deletions.
1 change: 1 addition & 0 deletions crates/cairo-lang-defs/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ fn allowed_attributes(db: &dyn DefsGroup) -> Arc<OrderedHashSet<String>> {
INLINE_ATTR.into(),
MUST_USE_ATTR.into(),
UNSTABLE_ATTR.into(),
FEATURE_ATTR.into(),
IMPLICIT_PRECEDENCE_ATTR.into(),
FMT_SKIP_ATTR.into(),
// TODO(orizi): Remove this once `starknet` is removed from corelib.
Expand Down
118 changes: 101 additions & 17 deletions crates/cairo-lang-semantic/src/expr/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ast::PathSegment;
use cairo_lang_defs::db::validate_attributes_flat;
use cairo_lang_defs::ids::{
EnumId, FunctionTitleId, FunctionWithBodyId, GenericKind, LanguageElementId, LocalVarLongId,
MemberId, TraitFunctionId, TraitId,
LookupItemId, MemberId, ModuleId, TraitFunctionId, TraitId,
};
use cairo_lang_diagnostics::{Maybe, ToOption};
use cairo_lang_filesystem::ids::{FileKind, FileLongId, VirtualFile};
Expand Down Expand Up @@ -157,7 +157,7 @@ impl<'ctx> ComputationContext<'ctx> {
F: FnOnce(&mut Self) -> T,
{
// Push an environment to the stack.
let new_environment = Box::<Environment>::default();
let new_environment = Box::new(Environment::empty());
let old_environment = std::mem::replace(&mut self.environment, new_environment);
self.environment.parent = Some(old_environment);

Expand Down Expand Up @@ -206,7 +206,7 @@ pub type EnvVariables = OrderedHashMap<SmolStr, Variable>;
// TODO(spapini): Consider using identifiers instead of SmolStr everywhere in the code.
/// A state which contains all the variables defined at the current resolver until now, and a
/// pointer to the parent environment.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Environment {
parent: Option<Box<Environment>>,
variables: EnvVariables,
Expand All @@ -232,6 +232,76 @@ impl Environment {
))
}
}

fn empty() -> Self {
Self {
parent: None,
variables: Default::default(),
used_variables: Default::default(),
allowed_features: Default::default(),
}
}

pub fn from_lookup_item_id(
db: &dyn SemanticGroup,
lookup_item_id: LookupItemId,
diagnostics: &mut SemanticDiagnostics,
) -> Self {
let defs_db = db.upcast();
let semantic_db = db.upcast();
let allowed_features = match lookup_item_id {
LookupItemId::ModuleItem(id) => extract_allowed_features(
semantic_db,
&id.stable_location(defs_db).syntax_node(defs_db),
diagnostics,
),
LookupItemId::TraitItem(id) => extract_allowed_features(
semantic_db,
&id.stable_location(defs_db).syntax_node(defs_db),
diagnostics,
),
LookupItemId::ImplItem(id) => extract_allowed_features(
semantic_db,
&id.stable_location(defs_db).syntax_node(defs_db),
diagnostics,
),
};

Self::from_element_id(db, lookup_item_id, allowed_features.into_iter().collect())
}

fn from_element_id(
db: &dyn SemanticGroup,
element_id: impl LanguageElementId,
mut allowed_features: UnorderedHashSet<SmolStr>,
) -> Environment {
let defs_db = db.upcast();
let syntax_db = db.upcast();
let ignored_diagnostics = &mut SemanticDiagnostics::new(
element_id.module_file_id(defs_db).file_id(defs_db).unwrap(),
);
let mut curr_module_id = element_id.parent_module(defs_db);
loop {
let submodule_id = match curr_module_id {
ModuleId::CrateRoot(_) => break,
ModuleId::Submodule(id) => id,
};
let parent = submodule_id.parent_module(defs_db);
let module = &defs_db.module_submodules(parent).unwrap()[&submodule_id];
// TODO(orizi): Add parent module diagnostics.
for allowed_feature in extract_allowed_features(syntax_db, module, ignored_diagnostics)
{
allowed_features.insert(allowed_feature);
}
curr_module_id = parent;
}
Self {
parent: None,
variables: Default::default(),
used_variables: Default::default(),
allowed_features,
}
}
}

/// Computes the semantic model of an expression.
Expand Down Expand Up @@ -2252,20 +2322,7 @@ pub fn compute_statement_semantic(
// are allowed.
validate_statement_attributes(ctx, &syntax);
let mut features_to_remove = vec![];
for attr_syntax in syntax.query_attr(syntax_db, FEATURE_ATTR) {
let attr = attr_syntax.clone().structurize(syntax_db);
let feature_name = match &attr.args[..] {
[
AttributeArg {
variant: AttributeArgVariant::Unnamed { value: ast::Expr::String(value), .. },
..
},
] => value.text(syntax_db),
_ => {
ctx.diagnostics.report(&attr_syntax, UnsupportedFeatureAttrArguments);
continue;
}
};
for feature_name in extract_allowed_features(syntax_db, &syntax, ctx.diagnostics) {
if ctx.environment.allowed_features.insert(feature_name.clone()) {
features_to_remove.push(feature_name);
}
Expand Down Expand Up @@ -2447,6 +2504,33 @@ pub fn compute_statement_semantic(
Ok(ctx.statements.alloc(statement))
}

/// Returns the allowed features of a query
fn extract_allowed_features(
db: &dyn SyntaxGroup,
syntax: &impl QueryAttrs,
diagnostics: &mut SemanticDiagnostics,
) -> Vec<SmolStr> {
let mut features = vec![];
for attr_syntax in syntax.query_attr(db, FEATURE_ATTR) {
let attr = attr_syntax.structurize(db);
let feature_name = match &attr.args[..] {
[
AttributeArg {
variant: AttributeArgVariant::Unnamed { value: ast::Expr::String(value), .. },
..
},
] => value.text(db),
_ => {
diagnostics
.report_by_ptr(attr.args_stable_ptr.untyped(), UnsupportedFeatureAttrArguments);
continue;
}
};
features.push(feature_name);
}
features
}

/// Computes the semantic model of an expression and reports diaganostics if
/// the expression does not evaluate to a boolean value.
fn compute_bool_condition_semantic(
Expand Down
9 changes: 4 additions & 5 deletions crates/cairo-lang-semantic/src/items/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ pub fn priv_constant_semantic_data(
let const_ast = db.module_constant_by_id(const_id)?.to_maybe()?;
let syntax_db = db.upcast();

let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
ModuleItemId::Constant(const_id),
));
let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::Constant(const_id));
let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
let mut resolver = Resolver::new(db, module_file_id, inference_id);

let const_type = resolve_type(
Expand All @@ -60,8 +59,8 @@ pub fn priv_constant_semantic_data(
&const_ast.type_clause(syntax_db).ty(syntax_db),
);

let mut ctx =
ComputationContext::new(db, &mut diagnostics, None, resolver, None, Environment::default());
let environment = Environment::from_lookup_item_id(db, lookup_item_id, &mut diagnostics);
let mut ctx = ComputationContext::new(db, &mut diagnostics, None, resolver, None, environment);
let value = compute_expr_semantic(&mut ctx, &const_ast.value(syntax_db));
if let Err(err) = ctx.resolver.inference().conform_ty(value.ty(), const_type) {
err.report(ctx.diagnostics, const_ast.stable_ptr().untyped());
Expand Down
7 changes: 3 additions & 4 deletions crates/cairo-lang-semantic/src/items/extern_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,15 @@ pub fn priv_extern_function_declaration_data(
let generic_params_data =
db.extern_function_declaration_generic_params_data(extern_function_id)?;
let generic_params = generic_params_data.generic_params;
let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
ModuleItemId::ExternFunction(extern_function_id),
));
let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::ExternFunction(extern_function_id));
let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
let mut resolver = Resolver::with_data(
db,
(*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
);
diagnostics.diagnostics.extend(generic_params_data.diagnostics);

let mut environment = Environment::default();
let mut environment = Environment::from_lookup_item_id(db, lookup_item_id, &mut diagnostics);
let signature_syntax = declaration.signature(syntax_db);
let signature = semantic::Signature::from_ast(
&mut diagnostics,
Expand Down
7 changes: 3 additions & 4 deletions crates/cairo-lang-semantic/src/items/free_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,15 @@ pub fn priv_free_function_declaration_data(
// Generic params.
let generic_params_data = db.free_function_generic_params_data(free_function_id)?;
let generic_params = generic_params_data.generic_params;
let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(
ModuleItemId::FreeFunction(free_function_id),
));
let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::FreeFunction(free_function_id));
let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
let mut resolver = Resolver::with_data(
db,
(*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
);
diagnostics.diagnostics.extend(generic_params_data.diagnostics);

let mut environment = Environment::default();
let mut environment = Environment::from_lookup_item_id(db, lookup_item_id, &mut diagnostics);

let signature_syntax = declaration.signature(syntax_db);
let signature = semantic::Signature::from_ast(
Expand Down
7 changes: 3 additions & 4 deletions crates/cairo-lang-semantic/src/items/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1467,9 +1467,8 @@ pub fn priv_impl_function_declaration_data(

let generic_params_data = db.priv_impl_function_generic_params_data(impl_function_id)?;
let generic_params = generic_params_data.generic_params;
let inference_id = InferenceId::LookupItemGenerics(LookupItemId::ImplItem(
ImplItemId::Function(impl_function_id),
));
let lookup_item_id = LookupItemId::ImplItem(ImplItemId::Function(impl_function_id));
let inference_id = InferenceId::LookupItemGenerics(lookup_item_id);
let mut resolver = Resolver::with_data(
db,
(*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
Expand All @@ -1478,7 +1477,7 @@ pub fn priv_impl_function_declaration_data(

let signature_syntax = declaration.signature(syntax_db);

let mut environment = Environment::default();
let mut environment = Environment::from_lookup_item_id(db, lookup_item_id, &mut diagnostics);
let signature = semantic::Signature::from_ast(
&mut diagnostics,
db,
Expand Down
7 changes: 3 additions & 4 deletions crates/cairo-lang-semantic/src/items/trt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,17 +683,16 @@ pub fn priv_trait_function_declaration_data(
let function_generic_params_data =
db.priv_trait_function_generic_params_data(trait_function_id)?;
let function_generic_params = function_generic_params_data.generic_params;
let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::TraitItem(
TraitItemId::Function(trait_function_id),
));
let lookup_item_id = LookupItemId::TraitItem(TraitItemId::Function(trait_function_id));
let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
let mut resolver = Resolver::with_data(
db,
(*function_generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
);
diagnostics.diagnostics.extend(function_generic_params_data.diagnostics);

let signature_syntax = declaration.signature(syntax_db);
let mut environment = Environment::default();
let mut environment = Environment::from_lookup_item_id(db, lookup_item_id, &mut diagnostics);
let signature = semantic::Signature::from_ast(
&mut diagnostics,
db,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ fn test_flow() {
}

#[test]
#[feature("safe_dispatcher")]
fn test_flow_safe_dispatcher() {
// Set up.
let (contract_address, _) = deploy_syscall(
Expand All @@ -78,14 +79,12 @@ fn test_flow_safe_dispatcher() {
let mut contract = IContractSafeDispatcher { contract_address };

// Interact.
#[feature("safe_dispatcher")]
assert_eq!(contract.foo(300), Result::Ok(100));

// Library calls.
let mut library = IContractSafeLibraryDispatcher {
class_hash: contract_a::TEST_CLASS_HASH.try_into().unwrap()
};
#[feature("safe_dispatcher")]
assert_eq!(library.foo(300), Result::Ok(0));
}

Expand Down

0 comments on commit bf45849

Please sign in to comment.