Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pub attribute for struct fields #232

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

bufferhe4d
Copy link
Contributor

Attempts to resolve #224

Here is my approach:

I introduced a new component Option<Attribute> to fields in order to make use of the already defined Pub attribute that is used for public inputs.

The tricky part was to check whether we are inside a structs method or not. For that I introduced a new variable inside TypedFnEnv called current_fn_kind that keeps track of the function we are in. This is further utilized in the type checker.

Copy link
Collaborator

@katat katat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!
It would be great to support modifying the struct fields inside the methods, as this pub is also about a whitelist to controlling the values of a struct.

Maybe we can create another PR to implement the mut [arg] as described here.

fn main(pub beds: Field) {
let mut room = Room {beds: 2, size: 10};
room.beds = beds; // allowed
room.size = 5; // not allowed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just test the negative case here. Then the positive tests should cover the rest.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree ^ we can simplify the test

src/inputs.rs Outdated Show resolved Hide resolved
src/name_resolution/context.rs Outdated Show resolved Hide resolved
@katat
Copy link
Collaborator

katat commented Nov 14, 2024

@mimoo may want another pass

Copy link
Contributor

@mimoo mimoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a few more comments! Sorry for the delay

@@ -55,6 +55,26 @@ impl StructDef {
tokens.bump(ctx);
break;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

realized looking at this code that we can write an empty struct (e.g. struct Thing {}) and it will work. Should we disallow that? (we could do that by checking that the length of fields is not 0 at the end)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at the same time it seems OK to allow it...

tokens.bump(ctx);
Some(Attribute {
kind: AttributeKind::Pub,
span: ctx.last_span(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be the span from the matching token

@@ -8,7 +8,7 @@ hint fn divmod(dividend: Field, divisor: Field) -> [Field; 2];
// u8
// must use new() to create a Uint8, so the value is range checked
struct Uint8 {
inner: Field,
pub inner: Field,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo we shouldn't expose it like this, we can have a to_field function but we shouldn't allow direct mutation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, having this as private was failing the tests here:

return res.inner;

I changed it to to_field() instead now.

@@ -55,6 +55,9 @@ pub struct TypedFnEnv {

/// Determines if forloop variables are allowed to be accessed.
forbid_forloop_scope: bool,

/// The kind of function we're currently type checking
current_fn_kind: Option<FuncOrMethod>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it an option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, I thought we have global scope in the language and we might not be in a function while type checking. Should be fixed now.

@@ -331,6 +331,8 @@ impl<B: Backend> TypeChecker<B> {
// create a new typed fn environment to type check the function
let mut typed_fn_env = TypedFnEnv::default();

typed_fn_env.set_current_fn_kind(function.sig.kind.clone());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you just do let mut typed_fn_env = TypeFnEnv::new(&function.sig.kind);

let is_public = attribute
.as_ref()
.map(|attr| matches!(attr.kind, AttributeKind::Pub))
.unwrap_or(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't you just do let is_public = matches!(attribute, &Some(Attribute { kind: AttributeKind::Pub, .. })) ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(btw to avoid the option we could also have a AttributeKind::None)

Comment on lines 140 to 141
false
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's gotta be a way to decrease the LOC here as well. Maybe

let in_method = matches!(typed_fn_env.fn_kind, FuncOrMethod::Method(struc) if struc.name == struct_name);

@bufferhe4d
Copy link
Contributor Author

Alright, I did another iteration after the comments from @mimoo. I have one question about how we initialize the TypedFnEnv now below.

pub fn new() -> Self {
Self::default()
/// Creates a new TypeEnv with the given function kind
pub fn new(fn_kind: &FuncOrMethod) -> Self {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sure there is a more elegant way of doing this. The reason I went with this is that since current_fn_kind is not an option, calling Self::default() calls the default constructor of FuncOrMethod as well, which calls unreachable!:

unreachable!()

How can we do it better?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.
With FuncOrMethod, I think it should be always calling ::new to specify the fn_kind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I didn't fully understand the last part. Do you mean the default() for FuncOrMethod should change (return a new object instead of unreachable) or is the current approach okay as is?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant default() should be removed, if it is not used anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow pub attribute for struct fields
3 participants