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

Using constants at compile time to define vector lengths #3469

Closed
brendanzab opened this issue Sep 12, 2012 · 9 comments
Closed

Using constants at compile time to define vector lengths #3469

brendanzab opened this issue Sep 12, 2012 · 9 comments
Labels
A-frontend Area: Compiler frontend (errors, parsing and HIR) E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot.
Milestone

Comments

@brendanzab
Copy link
Member

I'm trying to use a constant at compile time to define the length of some vectors:

/* Gamma ramps */
const GLFW_GAMMA_RAMP_SIZE              : int = 256;

/* Gamma ramp */
struct GLFWgammaramp {
    red     : [c_ushort * GLFW_GAMMA_RAMP_SIZE],      // unsigned short red[GLFW_GAMMA_RAMP_SIZE];
    green   : [c_ushort * GLFW_GAMMA_RAMP_SIZE],      // unsigned short green[GLFW_GAMMA_RAMP_SIZE];
    blue    : [c_ushort * GLFW_GAMMA_RAMP_SIZE]       // unsigned short blue[GLFW_GAMMA_RAMP_SIZE];
}

(code from glfw3-rs)

I get this error:

 % rustc test.rs 
test.rs:6:26: 6:46 error: expected `]` but found `GLFW_GAMMA_RAMP_SIZE`
test.rs:6     red     : [c_ushort * GLFW_GAMMA_RAMP_SIZE],      // unsigned short red[GLFW_GAMMA_RAMP_SIZE];

Is this a bug or by design?

@ghost ghost assigned graydon Sep 12, 2012
@graydon
Copy link
Contributor

graydon commented Sep 12, 2012

It's an incompleteness, a bug. We're intending to support integer constant expressions in that context eventually. I'd pull up the github issue associated with it but their search engine fell over yesterday and still doesn't have indices back online. Will check again next time around.

@jdm
Copy link
Contributor

jdm commented Feb 13, 2013

It would also be swell to allow values like enum discriminants, which would allow things like

enum A {
  A1,
  A2,
  A_Count
}
let foo: [int * A_Count];

@nikomatsakis
Copy link
Contributor

Regarding the enum trick, I'd prefer to have some way of getting the max discriminant of an enum. I do this trick of adding an extra value in C all the time but it's annoying because it foils exhaustiveness checks.

@luqmana
Copy link
Member

luqmana commented Feb 25, 2013

So I have a patch that addresses this, problem is that it only half works. Simple constant expressions like 2*3 or 32/2 work but attempting to use a previously defined constant fails (Non-constant path in constant expr). This seems to be because const_eval::eval_const_expr can't find the expr in the def_map when called in typeck::astconv. I'm not sure about order of things in that stage of the compiler so I'm a bit lost in how to approach this.

@jdm
Copy link
Contributor

jdm commented Feb 25, 2013

It looks the astconv stuff might happen in the collect pass, which directly precedes the resolve pass which does record information about constants in def_map.

@luqmana
Copy link
Member

luqmana commented Feb 25, 2013

So trying to avoid the const_eval in astconv by propagating @ast::expr down through ty::vstore_fixed doesn't work since it necessitates creating expr in various places in trans which results in errors like

expected `[u8]/8` but found `[u8]/8` ([] storage differs: expected 8 but found 8)

@luqmana
Copy link
Member

luqmana commented Mar 6, 2013

Thanks to some help from pcwalton, I have this working properly now.

@nikomatsakis
Copy link
Contributor

@pcwalton @graydon regarding the set of expressions that can be included in constants, I think there are issues around field projection and the like because we need to evaluate constants during the "collect" of type checking.

That is, type checking first iterates over all struct declarations and converts them into types. It then descends into function bodies and expressions to check those. The tricky part here is that checking a function body / expression requires the types for the struct declarations and so forth. In particular, imagine we have to type check something like x.f --- we need to compute the type of x, but if it has a struct type, for example, we need to lookup that struct and figure out the declared type of the field f. Right now the code that type checks a constant expression is the same as the code that type checks a function body, so it e.g. assumes that the types of structs are available.

Basically this means that either the code that type checks expressions will have to be parameterized over what phase it is operating in (hard and annoying but not impossible), or else we will wind up having to duplicate the code that type checks constants so that when we are evaluating constants to compute the final types we don't require that type conversion has taken place.

Thus far we have taken the duplication path. If you look in eval_const.rs, you will see there that the function which evaluates constants basically duplicates the type rules, which is fine because it adds additional logic. You'll also see that it aborts for things like field projection. So in essence we already have established a subset of constant expressions, though I don't think failure is handled gracefully.

If we are duplicating, it makes sense from a practical perspective to duplicate as little as possible. Duplicating integer arithmetic and so on seems pretty straightforward. I am not sure though if there is a theoretical reason that we can't handle more complex expressions, but the logic involved is fairly complex (think autoderef etc).

It is not really clear to me when it is necessary to have field projection (a.b), pointer dereferences (*a), indexing (a[b]) in constant expressions. I feel like any example that makes use of such things can be written more clearly by having an intermediate constant for the value you are trying to obtain.

Does this make sense?

bors added a commit that referenced this issue Mar 19, 2013
So this is a partial fix for #3469. Partial because it only works for simple constant expressions like `32/2` and `2+2` and not for any actual constants.

For example:
```
const FOO: uint = 2+2;
let v: [int * FOO];
```

results in:
```
error: expected constant expr for vector length: Non-constant path in constant expr
```

This seems to be because at the point of error (`typeck::astconv`) the def_map doesn't contain the constant and thus it can't lookup the actual expression (`2+2` in this case).

So, feedback on what I have so far and suggestions for how to address the constant issue?
@pnkfelix
Copy link
Member

The original issue as described was resolved by #5112. There appears to be some remaining questions of how expressive we should allow constant expressions to be, but I do not think this ticket is a good place for such discussion to continue; closing.

bors pushed a commit to rust-lang-ci/rust that referenced this issue May 15, 2021
RalfJung pushed a commit to RalfJung/rust that referenced this issue Apr 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-frontend Area: Compiler frontend (errors, parsing and HIR) E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot.
Projects
None yet
Development

No branches or pull requests

6 participants