1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
use std::{borrow::Cow, fmt};

use super::{builtins::MacroCall, Span};
use crate::{
    AddressSpace, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle,
    Interpolation, Literal, Sampling, StorageAccess, Type, UnaryOperator,
};

#[derive(Debug, Clone, Copy)]
pub enum GlobalLookupKind {
    Variable(Handle<GlobalVariable>),
    Constant(Handle<Constant>, Handle<Type>),
    BlockSelect(Handle<GlobalVariable>, u32),
}

#[derive(Debug, Clone, Copy)]
pub struct GlobalLookup {
    pub kind: GlobalLookupKind,
    pub entry_arg: Option<usize>,
    pub mutable: bool,
}

#[derive(Debug, Clone)]
pub struct ParameterInfo {
    pub qualifier: ParameterQualifier,
    /// Whether the parameter should be treated as a depth image instead of a
    /// sampled image.
    pub depth: bool,
}

/// How the function is implemented
#[derive(Clone, Copy)]
pub enum FunctionKind {
    /// The function is user defined
    Call(Handle<Function>),
    /// The function is a builtin
    Macro(MacroCall),
}

impl fmt::Debug for FunctionKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::Call(_) => write!(f, "Call"),
            Self::Macro(_) => write!(f, "Macro"),
        }
    }
}

#[derive(Debug)]
pub struct Overload {
    /// Normalized function parameters, modifiers are not applied
    pub parameters: Vec<Handle<Type>>,
    pub parameters_info: Vec<ParameterInfo>,
    /// How the function is implemented
    pub kind: FunctionKind,
    /// Whether this function was already defined or is just a prototype
    pub defined: bool,
    /// Whether this overload is the one provided by the language or has
    /// been redeclared by the user (builtins only)
    pub internal: bool,
    /// Whether or not this function returns void (nothing)
    pub void: bool,
}

bitflags::bitflags! {
    /// Tracks the variations of the builtin already generated, this is needed because some
    /// builtins overloads can't be generated unless explicitly used, since they might cause
    /// unneeded capabilities to be requested
    #[derive(Default)]
    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub struct BuiltinVariations: u32 {
        /// Request the standard overloads
        const STANDARD = 1 << 0;
        /// Request overloads that use the double type
        const DOUBLE = 1 << 1;
        /// Request overloads that use `samplerCubeArray(Shadow)`
        const CUBE_TEXTURES_ARRAY = 1 << 2;
        /// Request overloads that use `sampler2DMSArray`
        const D2_MULTI_TEXTURES_ARRAY = 1 << 3;
    }
}

#[derive(Debug, Default)]
pub struct FunctionDeclaration {
    pub overloads: Vec<Overload>,
    /// Tracks the builtin overload variations that were already generated
    pub variations: BuiltinVariations,
}

#[derive(Debug)]
pub struct EntryArg {
    pub name: Option<String>,
    pub binding: Binding,
    pub handle: Handle<GlobalVariable>,
    pub storage: StorageQualifier,
}

#[derive(Debug, Clone)]
pub struct VariableReference {
    pub expr: Handle<Expression>,
    /// Whether the variable is of a pointer type (and needs loading) or not
    pub load: bool,
    /// Whether the value of the variable can be changed or not
    pub mutable: bool,
    pub constant: Option<(Handle<Constant>, Handle<Type>)>,
    pub entry_arg: Option<usize>,
}

#[derive(Debug, Clone)]
pub struct HirExpr {
    pub kind: HirExprKind,
    pub meta: Span,
}

#[derive(Debug, Clone)]
pub enum HirExprKind {
    Access {
        base: Handle<HirExpr>,
        index: Handle<HirExpr>,
    },
    Select {
        base: Handle<HirExpr>,
        field: String,
    },
    Literal(Literal),
    Binary {
        left: Handle<HirExpr>,
        op: BinaryOperator,
        right: Handle<HirExpr>,
    },
    Unary {
        op: UnaryOperator,
        expr: Handle<HirExpr>,
    },
    Variable(VariableReference),
    Call(FunctionCall),
    /// Represents the ternary operator in glsl (`:?`)
    Conditional {
        /// The expression that will decide which branch to take, must evaluate to a boolean
        condition: Handle<HirExpr>,
        /// The expression that will be evaluated if [`condition`] returns `true`
        ///
        /// [`condition`]: Self::Conditional::condition
        accept: Handle<HirExpr>,
        /// The expression that will be evaluated if [`condition`] returns `false`
        ///
        /// [`condition`]: Self::Conditional::condition
        reject: Handle<HirExpr>,
    },
    Assign {
        tgt: Handle<HirExpr>,
        value: Handle<HirExpr>,
    },
    /// A prefix/postfix operator like `++`
    PrePostfix {
        /// The operation to be performed
        op: BinaryOperator,
        /// Whether this is a postfix or a prefix
        postfix: bool,
        /// The target expression
        expr: Handle<HirExpr>,
    },
    /// A method call like `what.something(a, b, c)`
    Method {
        /// expression the method call applies to (`what` in the example)
        expr: Handle<HirExpr>,
        /// the method name (`something` in the example)
        name: String,
        /// the arguments to the method (`a`, `b`, and `c` in the example)
        args: Vec<Handle<HirExpr>>,
    },
}

#[derive(Debug, Hash, PartialEq, Eq)]
pub enum QualifierKey<'a> {
    String(Cow<'a, str>),
    /// Used for `std140` and `std430` layout qualifiers
    Layout,
    /// Used for image formats
    Format,
}

#[derive(Debug)]
pub enum QualifierValue {
    None,
    Uint(u32),
    Layout(StructLayout),
    Format(crate::StorageFormat),
}

#[derive(Debug, Default)]
pub struct TypeQualifiers<'a> {
    pub span: Span,
    pub storage: (StorageQualifier, Span),
    pub invariant: Option<Span>,
    pub interpolation: Option<(Interpolation, Span)>,
    pub precision: Option<(Precision, Span)>,
    pub sampling: Option<(Sampling, Span)>,
    /// Memory qualifiers used in the declaration to set the storage access to be used
    /// in declarations that support it (storage images and buffers)
    pub storage_access: Option<(StorageAccess, Span)>,
    pub layout_qualifiers: crate::FastHashMap<QualifierKey<'a>, (QualifierValue, Span)>,
}

impl<'a> TypeQualifiers<'a> {
    /// Appends `errors` with errors for all unused qualifiers
    pub fn unused_errors(&self, errors: &mut Vec<super::Error>) {
        if let Some(meta) = self.invariant {
            errors.push(super::Error {
                kind: super::ErrorKind::SemanticError(
                    "Invariant qualifier can only be used in in/out variables".into(),
                ),
                meta,
            });
        }

        if let Some((_, meta)) = self.interpolation {
            errors.push(super::Error {
                kind: super::ErrorKind::SemanticError(
                    "Interpolation qualifiers can only be used in in/out variables".into(),
                ),
                meta,
            });
        }

        if let Some((_, meta)) = self.sampling {
            errors.push(super::Error {
                kind: super::ErrorKind::SemanticError(
                    "Sampling qualifiers can only be used in in/out variables".into(),
                ),
                meta,
            });
        }

        if let Some((_, meta)) = self.storage_access {
            errors.push(super::Error {
                kind: super::ErrorKind::SemanticError(
                    "Memory qualifiers can only be used in storage variables".into(),
                ),
                meta,
            });
        }

        for &(_, meta) in self.layout_qualifiers.values() {
            errors.push(super::Error {
                kind: super::ErrorKind::SemanticError("Unexpected qualifier".into()),
                meta,
            });
        }
    }

    /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
    /// a [`QualifierValue::Uint`]
    pub fn uint_layout_qualifier(
        &mut self,
        name: &'a str,
        errors: &mut Vec<super::Error>,
    ) -> Option<u32> {
        match self
            .layout_qualifiers
            .remove(&QualifierKey::String(name.into()))
        {
            Some((QualifierValue::Uint(v), _)) => Some(v),
            Some((_, meta)) => {
                errors.push(super::Error {
                    kind: super::ErrorKind::SemanticError("Qualifier expects a uint value".into()),
                    meta,
                });
                // Return a dummy value instead of `None` to differentiate from
                // the qualifier not existing, since some parts might require the
                // qualifier to exist and throwing another error that it doesn't
                // exist would be unhelpful
                Some(0)
            }
            _ => None,
        }
    }

    /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
    /// a [`QualifierValue::None`]
    pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec<super::Error>) -> bool {
        match self
            .layout_qualifiers
            .remove(&QualifierKey::String(name.into()))
        {
            Some((QualifierValue::None, _)) => true,
            Some((_, meta)) => {
                errors.push(super::Error {
                    kind: super::ErrorKind::SemanticError(
                        "Qualifier doesn't expect a value".into(),
                    ),
                    meta,
                });
                // Return a `true` to since the qualifier is defined and adding
                // another error for it not being defined would be unhelpful
                true
            }
            _ => false,
        }
    }
}

#[derive(Debug, Clone)]
pub enum FunctionCallKind {
    TypeConstructor(Handle<Type>),
    Function(String),
}

#[derive(Debug, Clone)]
pub struct FunctionCall {
    pub kind: FunctionCallKind,
    pub args: Vec<Handle<HirExpr>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StorageQualifier {
    AddressSpace(AddressSpace),
    Input,
    Output,
    Const,
}

impl Default for StorageQualifier {
    fn default() -> Self {
        StorageQualifier::AddressSpace(AddressSpace::Function)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructLayout {
    Std140,
    Std430,
}

// TODO: Encode precision hints in the IR
/// A precision hint used in GLSL declarations.
///
/// Precision hints can be used to either speed up shader execution or control
/// the precision of arithmetic operations.
///
/// To use a precision hint simply add it before the type in the declaration.
/// ```glsl
/// mediump float a;
/// ```
///
/// The default when no precision is declared is `highp` which means that all
/// operations operate with the type defined width.
///
/// For `mediump` and `lowp` operations follow the spir-v
/// [`RelaxedPrecision`][RelaxedPrecision] decoration semantics.
///
/// [RelaxedPrecision]: https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#_a_id_relaxedprecisionsection_a_relaxed_precision
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum Precision {
    /// `lowp` precision
    Low,
    /// `mediump` precision
    Medium,
    /// `highp` precision
    High,
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum ParameterQualifier {
    In,
    Out,
    InOut,
    Const,
}

impl ParameterQualifier {
    /// Returns true if the argument should be passed as a lhs expression
    pub const fn is_lhs(&self) -> bool {
        match *self {
            ParameterQualifier::Out | ParameterQualifier::InOut => true,
            _ => false,
        }
    }
}

/// The GLSL profile used by a shader.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Profile {
    /// The `core` profile, default when no profile is specified.
    Core,
}