Fix redundant warning when destructuring validator params

This is not a "proper" fix as it simply get rid of the warning
  altogether (whether you use or not the destructured values).

  The reason for removing the warning entirely is because (1) it's
  simpler, but more so (2) there's no impact on the final code produced
  _anyway_. Redundant let bindings are already removed by the compiler;
  and while it's an implicit behaviour that requires a proper warning
  when it's coming from a user-defined assignment; here the redundant
  assignment is introduced by the compiler to begin with as another
  implicit behavior!

  So we have an implicit behaviour triggering a warning on another
  implicit behaviour. Truth is, there's no impact in having those
  parameters destructured and unused. So since users are already not
  aware that this results in an implicit let assignment being inserted
  in place for them; there's no need for the warning at all.
This commit is contained in:
KtorZ 2024-10-01 13:17:00 +02:00
parent 5737556efc
commit 0060804d1a
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
5 changed files with 111 additions and 8 deletions

View File

@ -9,6 +9,7 @@
### Changed ### Changed
- **aiken-lang**: Fix compiler crash on trace + expect as last expression of a clause. See #1029. @KtorZ - **aiken-lang**: Fix compiler crash on trace + expect as last expression of a clause. See #1029. @KtorZ
- **aiken-lang**: Fix redundant warning on introduced identifiers when destructuring validator params. @KtorZ
- **uplc**: Fix (again :grimacing:) cost-models for PlutusV1 & PlutusV2. @MicroProofs - **uplc**: Fix (again :grimacing:) cost-models for PlutusV1 & PlutusV2. @MicroProofs
### Removed ### Removed

View File

@ -1047,7 +1047,7 @@ impl UntypedArg {
// NOTE: We use ordinal here not only because it's cute, but because // NOTE: We use ordinal here not only because it's cute, but because
// such a name cannot be parsed to begin with and thus, will not clash // such a name cannot be parsed to begin with and thus, will not clash
// with any user-defined name. // with any user-defined name.
let name = format!("{}_arg", Ordinal::<usize>(ix).suffix()); let name = format!("{}{}_arg", ix + 1, Ordinal::<usize>(ix + 1).suffix());
ArgName::Named { ArgName::Named {
label: name.clone(), label: name.clone(),
name, name,
@ -1770,6 +1770,36 @@ impl UntypedPattern {
is_record: false, is_record: false,
} }
} }
pub fn collect_identifiers<F>(&self, collect: &mut F)
where
F: FnMut((String, Span)),
{
match self {
Pattern::Var { name, location } => {
collect((name.to_string(), *location));
}
Pattern::List { elements, .. } => {
elements.iter().for_each(|e| e.collect_identifiers(collect));
}
Pattern::Pair { fst, snd, .. } => {
fst.collect_identifiers(collect);
snd.collect_identifiers(collect);
}
Pattern::Tuple { elems, .. } => {
elems.iter().for_each(|e| e.collect_identifiers(collect));
}
Pattern::Constructor { arguments, .. } => {
arguments
.iter()
.for_each(|arg| arg.value.collect_identifiers(collect));
}
Pattern::Int { .. }
| Pattern::ByteArray { .. }
| Pattern::Discard { .. }
| Pattern::Assign { .. } => {}
}
}
} }
impl TypedPattern { impl TypedPattern {

View File

@ -3342,3 +3342,55 @@ fn dangling_trace_let_in_trace() {
Err((_, Error::LastExpressionIsAssignment { .. })) Err((_, Error::LastExpressionIsAssignment { .. }))
)) ))
} }
#[test]
fn destructuring_validator_params_tuple() {
let source_code = r#"
validator foo((x, y): (Int, Int)) {
mint(_redeemer, _policy_id, _self) {
x + y > 42
}
else(_) {
fail
}
}
"#;
let result = check_validator(parse(source_code));
assert!(result.is_ok());
let (warnings, _) = result.unwrap();
assert!(
matches!(&warnings[..], &[]),
"should be empty: {warnings:#?}"
);
}
#[test]
fn destructuring_validator_params_record() {
let source_code = r#"
pub type Foo {
Foo(Int, Int)
}
validator foo(Foo(x, y): Foo) {
mint(_redeemer, _policy_id, _self) {
x + y > 42
}
else(_) {
fail
}
}
"#;
let result = check_validator(parse(source_code));
assert!(result.is_ok());
let (warnings, _) = result.unwrap();
assert!(
matches!(&warnings[..], &[]),
"should be empty: {warnings:#?}"
);
}

View File

@ -39,6 +39,7 @@ pub struct Environment<'a> {
pub entity_usages: Vec<HashMap<String, (EntityKind, Span, bool)>>, pub entity_usages: Vec<HashMap<String, (EntityKind, Span, bool)>>,
pub id_gen: IdGenerator, pub id_gen: IdGenerator,
pub importable_modules: &'a HashMap<String, TypeInfo>, pub importable_modules: &'a HashMap<String, TypeInfo>,
pub validator_params: HashSet<(String, Span)>,
/// Modules that have been imported by the current module, along with the /// Modules that have been imported by the current module, along with the
/// location of the import statement where they were imported. /// location of the import statement where they were imported.
@ -792,6 +793,7 @@ impl<'a> Environment<'a> {
annotations: HashMap::new(), annotations: HashMap::new(),
warnings, warnings,
entity_usages: vec![HashMap::new()], entity_usages: vec![HashMap::new()],
validator_params: HashSet::new(),
target_env, target_env,
} }
} }

View File

@ -7,10 +7,10 @@ use super::{
}; };
use crate::{ use crate::{
ast::{ ast::{
Annotation, ArgName, ArgVia, DataType, Definition, Function, ModuleConstant, ModuleKind, Annotation, ArgBy, ArgName, ArgVia, DataType, Definition, Function, ModuleConstant,
RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg, TypedDefinition, ModuleKind, RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg,
TypedModule, TypedValidator, UntypedArg, UntypedDefinition, UntypedModule, UntypedPattern, TypedDefinition, TypedModule, TypedValidator, UntypedArg, UntypedDefinition, UntypedModule,
UntypedValidator, Use, Validator, UntypedPattern, UntypedValidator, Use, Validator,
}, },
expr::{TypedExpr, UntypedAssignmentKind}, expr::{TypedExpr, UntypedAssignmentKind},
tipo::{expr::infer_function, Span, Type, TypeVar}, tipo::{expr::infer_function, Span, Type, TypeVar},
@ -100,6 +100,14 @@ impl UntypedModule {
.collect(); .collect();
// Generate warnings for unused items // Generate warnings for unused items
println!("warnings: {:#?}", environment.warnings);
println!("params: {:#?}", environment.validator_params);
environment.warnings.retain(|warning| match warning {
Warning::UnusedVariable { location, name } => !environment
.validator_params
.contains(&(name.to_string(), *location)),
_ => true,
});
environment.convert_unused_to_warnings(); environment.convert_unused_to_warnings();
// Remove private and imported types and values to create the public interface // Remove private and imported types and values to create the public interface
@ -812,7 +820,11 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
} }
} }
fn put_params_in_scope(name: &str, environment: &mut Environment, params: &[UntypedArg]) { fn put_params_in_scope<'a>(
name: &'_ str,
environment: &'a mut Environment,
params: &'a [UntypedArg],
) {
let preregistered_fn = environment let preregistered_fn = environment
.get_variable(name) .get_variable(name)
.expect("Could not find preregistered type for function"); .expect("Could not find preregistered type for function");
@ -828,7 +840,7 @@ fn put_params_in_scope(name: &str, environment: &mut Environment, params: &[Unty
.zip(args_types[0..params.len()].iter()) .zip(args_types[0..params.len()].iter())
.enumerate() .enumerate()
{ {
match &arg.arg_name(ix) { match arg.arg_name(ix) {
ArgName::Named { ArgName::Named {
name, name,
label: _, label: _,
@ -842,7 +854,13 @@ fn put_params_in_scope(name: &str, environment: &mut Environment, params: &[Unty
t.clone(), t.clone(),
); );
environment.init_usage(name.to_string(), EntityKind::Variable, arg.location); if let ArgBy::ByPattern(ref pattern) = arg.by {
pattern.collect_identifiers(&mut |identifier| {
environment.validator_params.insert(identifier);
})
}
environment.init_usage(name, EntityKind::Variable, arg.location);
} }
ArgName::Named { .. } | ArgName::Discarded { .. } => (), ArgName::Named { .. } | ArgName::Discarded { .. } => (),
}; };