-
Notifications
You must be signed in to change notification settings - Fork 400
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
Propagate unsafety to definitions that depend on unsafe definitions #379
Conversation
TODO: - Document how the algorithm works along with complexities - From the original bug that led to this, `ok.hvm` now works as expectes but `bad.hvm` still doesn't... - Investigate
src/ast.rs
Outdated
pub fn direct_dependencies<'name>(&'name self) -> BTreeSet<&'name str> { | ||
match self { | ||
Tree::Ref { nam } => BTreeSet::from([nam.as_str()]), | ||
Tree::Con { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Dup { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Opr { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Swi { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Num { val } => BTreeSet::new(), | ||
Tree::Var { nam } => BTreeSet::new(), | ||
Tree::Era => BTreeSet::new(), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Writing a recursive function like this may not be the best idea since we still don't have a way to increase stack size as needed like in the hvm-64
repo. I think it's fine for now to add this function like this, since pretty much all the other functions related to trees are also recursive and if this one fails, they do too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it becomes a problem we can just write this imperatively
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is quadratic, due to repeatedly cloning these btreesets; (@a (@b (@c (@d (@e ...)))))
will take triangular time (which is asymptotically quadratic).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is quadratic, due to repeatedly cloning these btreesets;
(@a (@b (@c (@d (@e ...)))))
will take triangular time (which is asymptotically quadratic).
Thanks! I thought it would be similar to merging in merge sort, but I tested it and it really is slow. It should be better now.
println!("only testing rust implementation for {path:?}"); | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the possibility to only run tests in the Rust implementation, since the C one doesn't throw an error in this same case. This is something to discuss - should the C and CUDA implementations also "panic" printing this error and stopping the program?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine as is, and is in line with the other test-io
pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if I understand correctly, this doesn't account for user-introduced unsafety? It only accounts for when users reference an unsafe function?
src/ast.rs
Outdated
pub fn direct_dependencies<'name>(&'name self) -> BTreeSet<&'name str> { | ||
match self { | ||
Tree::Ref { nam } => BTreeSet::from([nam.as_str()]), | ||
Tree::Con { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Dup { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Opr { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Swi { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Num { val } => BTreeSet::new(), | ||
Tree::Var { nam } => BTreeSet::new(), | ||
Tree::Era => BTreeSet::new(), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it becomes a problem we can just write this imperatively
println!("only testing rust implementation for {path:?}"); | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine as is, and is in line with the other test-io
pattern.
Depends on what you mean by "user-introduced", since users also introduce definitions. It will only catch unsafe operations if the unsafe operation is contained in a definition, and a reference to that definition is directly duplicated. For example, exponentiation of church naturals (which I think should behave poorly in IC?) unsf = @f @x (f (f x))
main = (unsf unsf) This currently gets compiled to
which is correctly marked as unsafe. We could also do
which is not marked as unsafe, and does not terminate (loops forever) |
@developedby this PR breaks (surprisingly only) one of the tests in Bend, # given a shader, returns a square image
def render(depth, shader):
bend d = 0, i = 0:
when d < depth:
color = (fork(d+1, i*2+0), fork(d+1, i*2+1))
else:
width = depth / 2
color = shader(i % width, i / width)
return color
# given a position, returns a color
# for this demo, it just busy loops
def demo_shader(x, y):
bend i = 0:
when i < 10:
color = fork(i + 1)
else:
color = 0x000001
return color
# renders a 256x256 image using demo_shader
def main:
return render(5, demo_shader)
This is a very natural program to write, and the safety check this PR introduces will probably break many more programs created by users of Bend. I'm not sure how to deal with this... Should we really add this check in the way it's done by this PR, or should we do something else? |
Yes, this is the intended behaviour and a big limitation of hvm32 |
If we plan on continuing with hvm32 for a lot longer, I should probably add some monomorphization system to Bend to avoid hitting this limitation so often |
src/ast.rs
Outdated
pub fn direct_dependencies<'name>(&'name self) -> BTreeSet<&'name str> { | ||
match self { | ||
Tree::Ref { nam } => BTreeSet::from([nam.as_str()]), | ||
Tree::Con { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Dup { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Opr { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Swi { fst, snd } => &fst.direct_dependencies() | &snd.direct_dependencies(), | ||
Tree::Num { val } => BTreeSet::new(), | ||
Tree::Var { nam } => BTreeSet::new(), | ||
Tree::Era => BTreeSet::new(), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is quadratic, due to repeatedly cloning these btreesets; (@a (@b (@c (@d (@e ...)))))
will take triangular time (which is asymptotically quadratic).
All suggestions were resolved |
fine by me, although slight nit: |
Change "dependency reversed" to "dependent"
9ade570
to
9465666
Compare
Co-authored-by: T6 <[email protected]>
9465666
to
23ad6dc
Compare
Related to #362.
This PR kind of fixes the issue, but not really. While this new version does return an error in the previously thought "correct" version of the program, which is expected since it actually performed unsafe operations, it doesn't error in the second version, which does the same. Returning an error in this version is much harder, since it's not cloning an unsafe definition, but an unsafe expression. According to Nicolas, that could be solved with an affine type system.