<p dir="auto">Hi,</p>
<p dir="auto">I was trying to use the LLVM C bindings from Rust, but I ran into a mysterious error when I tried to use <code class="notranslate">LLVMGetTypeKind</code>. The Rust program is like this:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="#[allow(cstack)];
use std::libc::{c_uint};

pub enum Context_opaque {}
pub type ContextPtr = *Context_opaque;
pub enum Type_opaque {}
pub type TypePtr = *Type_opaque;

#[repr(C)]
pub enum LLVMTypeKind {
  LLVMVoidTypeKind,
  LLVMHalfTypeKind,
  LLVMFloatTypeKind,
  LLVMDoubleTypeKind,
  LLVMX86_FP80TypeKind,
  LLVMFP128TypeKind,
  LLVMPPC_FP128TypeKind,
  LLVMLabelTypeKind,
  LLVMIntegerTypeKind,
  LLVMFunctionTypeKind,
  LLVMStructTypeKind,
  LLVMArrayTypeKind,
  LLVMPointerTypeKind,
  LLVMVectorTypeKind,
  LLVMMetadataTypeKind,
  LLVMX86_MMXTypeKind
}

#[link_args = &quot;-lLLVMCore -lLLVMSupport&quot;]
extern {
  pub fn LLVMContextCreate() -&gt; ContextPtr;
  pub fn LLVMContextDispose(c: ContextPtr);
  pub fn LLVMGetTypeKind(t: TypePtr) -&gt; LLVMTypeKind;
  pub fn LLVMIntTypeInContext(c: ContextPtr, bits: c_uint) -&gt; TypePtr;
}

fn main() {
  unsafe {
    let ctx_ptr = LLVMContextCreate();
    let int32_ptr = LLVMIntTypeInContext(ctx_ptr, 32);
    let kind = LLVMGetTypeKind(int32_ptr); // boom!
    println!(&quot;{}&quot;, kind as uint);
    LLVMContextDispose(ctx_ptr);
  }
}"><pre class="notranslate"><code class="notranslate">#[allow(cstack)];
use std::libc::{c_uint};

pub enum Context_opaque {}
pub type ContextPtr = *Context_opaque;
pub enum Type_opaque {}
pub type TypePtr = *Type_opaque;

#[repr(C)]
pub enum LLVMTypeKind {
  LLVMVoidTypeKind,
  LLVMHalfTypeKind,
  LLVMFloatTypeKind,
  LLVMDoubleTypeKind,
  LLVMX86_FP80TypeKind,
  LLVMFP128TypeKind,
  LLVMPPC_FP128TypeKind,
  LLVMLabelTypeKind,
  LLVMIntegerTypeKind,
  LLVMFunctionTypeKind,
  LLVMStructTypeKind,
  LLVMArrayTypeKind,
  LLVMPointerTypeKind,
  LLVMVectorTypeKind,
  LLVMMetadataTypeKind,
  LLVMX86_MMXTypeKind
}

#[link_args = "-lLLVMCore -lLLVMSupport"]
extern {
  pub fn LLVMContextCreate() -&gt; ContextPtr;
  pub fn LLVMContextDispose(c: ContextPtr);
  pub fn LLVMGetTypeKind(t: TypePtr) -&gt; LLVMTypeKind;
  pub fn LLVMIntTypeInContext(c: ContextPtr, bits: c_uint) -&gt; TypePtr;
}

fn main() {
  unsafe {
    let ctx_ptr = LLVMContextCreate();
    let int32_ptr = LLVMIntTypeInContext(ctx_ptr, 32);
    let kind = LLVMGetTypeKind(int32_ptr); // boom!
    println!("{}", kind as uint);
    LLVMContextDispose(ctx_ptr);
  }
}
</code></pre></div>
<p dir="auto">When compiled and executed, LLVM crashes with</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Unhandled TypeID.
UNREACHABLE executed at Core.cpp:164!
Aborted (core dumped)"><pre class="notranslate"><code class="notranslate">Unhandled TypeID.
UNREACHABLE executed at Core.cpp:164!
Aborted (core dumped)
</code></pre></div>
<p dir="auto">After some investigation, I made <code class="notranslate">rustc</code> emit LLVM bitcode and found something strange:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="declare void @LLVMGetTypeKind(%enum.LLVMTypeKind* sret, %enum.Type_opaque*) unnamed_addr

call void @LLVMGetTypeKind(%enum.LLVMTypeKind* sret %kind, %enum.Type_opaque* %11)"><pre class="notranslate"><code class="notranslate">declare void @LLVMGetTypeKind(%enum.LLVMTypeKind* sret, %enum.Type_opaque*) unnamed_addr

call void @LLVMGetTypeKind(%enum.LLVMTypeKind* sret %kind, %enum.Type_opaque* %11)
</code></pre></div>
<p dir="auto"><code class="notranslate">rustc</code> declared the <code class="notranslate">LLVMGetTypeKind</code> function with wrong parameters, so the function, when called, got a pointer to unitialized value on stack instead of a <code class="notranslate">ContextRef</code>.</p>
<p dir="auto">I tried to declare <code class="notranslate">LLVMGetTypeKind</code> as a function returning <code class="notranslate">c_uint</code> and everything worked well (it is probably the reason why <code class="notranslate">rustc</code>'s LLVM bindings declare <code class="notranslate">LLVMTypeKind</code> as a series of static variables).</p>
<p dir="auto">Is this a bug or a feature? If it is a feature, it is worth documenting! :)</p>
<p dir="auto">First off, the operator precedence of <code class="notranslate">a..b</code> is extremely low.<br>
<code class="notranslate">struct Range</code> recently got #[derive(Eq)], so people might want to compare a range to an expression in range notation.</p>
<p dir="auto"><code class="notranslate">r == 1..10</code> parses as <code class="notranslate">(r == 1)..10</code>. Similarly, <code class="notranslate">1..10 == r</code> parses as <code class="notranslate">1..(10 == r)</code>. Both result in type errors.<br>
I think there's no good reason for <code class="notranslate">..</code> to have a precedence lower than comparisons. I'd suggest to put it in-between the comparison operators and the bitwise operators.</p>
<p dir="auto">But the current implementation gets weirder when we consider the other forms of range notation:<br>
<code class="notranslate">r == 1..</code> parses as <code class="notranslate">(r == 1)..</code>, and is a type error (or maybe a <code class="notranslate">RangeFrom&lt;bool&gt;</code>).<br>
But <code class="notranslate">r == ..1</code> parses as <code class="notranslate">r == (..1)</code>, and is a valid range comparison.</p>
<p dir="auto">In the other direction, <code class="notranslate">..1 == r</code> parses as <code class="notranslate">..(1 == r)</code> and is potentially a <code class="notranslate">RangeTo&lt;bool&gt;</code>.<br>
But <code class="notranslate">1.. == r</code> is a syntax error (expected one of <code class="notranslate">)</code> or <code class="notranslate">,</code>, found <code class="notranslate">==</code>).<br>
This inconsistent with the prefix form.</p>
<p dir="auto">If we keep the current precedence of <code class="notranslate">..</code> (lower than all binops), I think we should disallow <code class="notranslate">r == ..1</code>. I think this can be implemented easily by using RESTRICTION_NO_DOTS when parsing the RHS of any binop.</p>
<p dir="auto">Also, we should use RESTRICTION_NO_DOTS when parsing the RHS of prefix-version <code class="notranslate">..expr</code> -- currently <code class="notranslate">..1..2</code> is a <code class="notranslate">RangeTo&lt;Range&lt;_&gt;&gt;</code>, while <code class="notranslate">1..2..3</code> and <code class="notranslate">1..2..</code> both are syntax errors.</p>
<p dir="auto">If we increase the precedence, we will need to be careful not to allow code like <code class="notranslate">1 + ..2</code>. I think any binop with precedence smaller than that of <code class="notranslate">..</code> will have to use RESTRICTION_NO_DOTS on the RHS.</p>
<p dir="auto">I also think the implementation can be simplified quite a bit if the prefix <code class="notranslate">..</code> wasn't handled in <code class="notranslate">parse_prefix_expr</code> (as that's only supposed to be used for operators with high precedence), but at the same level as the binary and postfix version (<code class="notranslate">parse_assign_expr</code>, or <code class="notranslate">parse_binops</code> if we change the precedence). This would allow us to get rid of RESTRICTION_NO_DOTS and use the normal operator precedence handling instead.</p>