The Crystal Programming Language Forum

Compiler bug(?) with invalid operation of $? and $~

When I played with special variables($? and $~), I faced two strange behaviors.

At now, some special variables($? and $~) can be assigned any value in the method.

  1. When manually assigned values to those variables in methods, the assigned value can be referenced from out of that method. (normally, the value set in a method can only be referenced in that method.)

  2. When the invalid type of value is manually assigned after compiler set the value, or when compiler set the value after the invalid type of value is manually assigned, it occur “Module validation failed”.

The compiler should reject all manually assignments to those variables imho.

At least, the special variables should not accept invalid type of values.

In the 2nd case, the error message said “its a bug of the compiler”.
Should I make an issue for that?

Code:

def foo
  $? = 1
end

foo 

$? #=> 1

`ls`

Output:

Module validation failed: Call parameter type does not match function signature!
  %"$?" = alloca %"(Int32 | Process::Status | Nil)", !dbg !9
 %"(Int32 | Nil)"*  %58 = call i32 @"*foo:Int32"(%"(Int32 | Process::Status | Nil)"* %"$?"), !dbg !81
Call parameter type does not match function signature!
  %"$?" = alloca %"(Int32 | Process::Status | Nil)", !dbg !9
 %"Process::Status"**  %59 = call %String* @"*`<String>:String"(%String* bitcast ({ i32, i32, i32, [3 x i8] }* @"'ls'" to %String*), %"(Int32 | Process::Status | Nil)"* %"$?"), !dbg !82
 (Exception)
  from raise<Exception>:NoReturn
  from raise<String>:NoReturn
  from Crystal::CodeGenVisitor#finish:Nil
  from Crystal::Compiler#codegen<Crystal::Program, Crystal::ASTNode+, Array(Crystal::Compiler::Source), String>:(Tuple(Array(Crystal::Compiler::CompilationUnit), Array(String)) | Nil)
  from Crystal::Compiler#compile<Array(Crystal::Compiler::Source), String>:Crystal::Compiler::Result
  from Crystal::Command#run_command<Bool>:Nil
  from Crystal::Command#run:(Bool | Nil)
  from __crystal_main
  from main
Error: you've found a bug in the Crystal compiler. Please open an issue, including source code that will allow us to reproduce the bug: https://github.com/crystal-lang/crystal/issues

Number 1. is intended and the only reason why these special variables exist. They’re used to make the contextual information available outside a method call. Stdlib uses them for the process state of a command literal and the matchdata of a regex match operator.
It seems we’re missing documentation on that, though.

Number 2. actually unexpected and probably a bug caused by the irregular scoping of these variables. A compiler error is always a bug.
Simplified reproduction:

def foo(x)
  $? = x
end
foo(1)
foo(true)

Yeah, maybe the assignability should be limited so that it isn’t used accidentally. But it’s impossible for the compiler to determine, what would classify as a “manual assignment”. The stdlib code that uses them isn’t really different from any other code.
An option could be to use a special syntax/name for assigning to these variables.

3 Likes

As Johannes says, point 1 can’t be changed because that’s actually the way to use these variables. Here’s an example:

And this allows you do extend String and define functions that also set $~ so they work just like the standard library. The idea is to make them a regular language feature, not something obscure and secret like in Ruby.

That said, they are built that way so the standard library developers can easily choose where to use them, and what type to give them. For example Regex::MatchData had a different name in the past, and we were able to rename it without a problem. If $~ was tied to a single type that would have been impossible.

And also, it’s not expected for anyone else other than the standard library developers to use these variables. So we should document that, and that’s it. If you use them, it’s your responsibility. If it breaks, it’s fine: you are not supposed to use them.

3 Likes

Thank you for your replies.
I understand background of this behavior.

I also think its a corner case.
It will not be any problems for almost users, expect the one(about me ^_^;) that worry about the detailed behavior.