mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
[glsl-in] Implicit casts for function calls
This commit is contained in:
committed by
Dzmitry Malyshau
parent
6cdd332cef
commit
4bc4c60663
@@ -26,14 +26,10 @@ pub struct GlobalLookup {
|
||||
pub mutable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionSignature {
|
||||
pub name: String,
|
||||
pub parameters: Vec<Handle<Type>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionDeclaration {
|
||||
/// Normalized function parameters, modifiers are not applied
|
||||
pub parameters: Vec<Handle<Type>>,
|
||||
pub qualifiers: Vec<ParameterQualifier>,
|
||||
pub handle: Handle<Function>,
|
||||
/// Wheter this function was already defined or is just a prototype
|
||||
@@ -84,7 +80,7 @@ pub struct Program<'a> {
|
||||
pub workgroup_size: [u32; 3],
|
||||
pub early_fragment_tests: bool,
|
||||
|
||||
pub lookup_function: FastHashMap<FunctionSignature, FunctionDeclaration>,
|
||||
pub lookup_function: FastHashMap<String, Vec<FunctionDeclaration>>,
|
||||
pub lookup_type: FastHashMap<String, Handle<Type>>,
|
||||
|
||||
pub global_variables: Vec<(String, GlobalLookup)>,
|
||||
@@ -120,7 +116,7 @@ impl<'a> Program<'a> {
|
||||
}
|
||||
|
||||
pub fn resolve_type<'b>(
|
||||
&'b mut self,
|
||||
&'b self,
|
||||
context: &'b mut Context,
|
||||
handle: Handle<Expression>,
|
||||
meta: SourceMetadata,
|
||||
@@ -146,33 +142,6 @@ impl<'a> Program<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_handle(
|
||||
&mut self,
|
||||
context: &mut Context,
|
||||
handle: Handle<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Handle<Type>, ErrorKind> {
|
||||
let resolve_ctx = ResolveContext {
|
||||
constants: &self.module.constants,
|
||||
types: &self.module.types,
|
||||
global_vars: &self.module.global_variables,
|
||||
local_vars: context.locals,
|
||||
functions: &self.module.functions,
|
||||
arguments: context.arguments,
|
||||
};
|
||||
match context
|
||||
.typifier
|
||||
.grow(handle, context.expressions, &resolve_ctx)
|
||||
{
|
||||
//TODO: better error report
|
||||
Err(error) => Err(ErrorKind::SemanticError(
|
||||
meta,
|
||||
format!("Can't resolve type: {:?}", error).into(),
|
||||
)),
|
||||
Ok(()) => Ok(context.typifier.get_handle(handle, &mut self.module.types)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve_constant(
|
||||
&mut self,
|
||||
ctx: &Context,
|
||||
@@ -362,7 +331,7 @@ impl<'function> Context<'function> {
|
||||
pub fn add_function_arg(
|
||||
&mut self,
|
||||
program: &mut Program,
|
||||
sig: &mut FunctionSignature,
|
||||
parameters: &mut Vec<Handle<Type>>,
|
||||
body: &mut Block,
|
||||
name: Option<String>,
|
||||
ty: Handle<Type>,
|
||||
@@ -374,7 +343,7 @@ impl<'function> Context<'function> {
|
||||
ty,
|
||||
binding: None,
|
||||
};
|
||||
sig.parameters.push(ty);
|
||||
parameters.push(ty);
|
||||
|
||||
if qualifier.is_lhs() {
|
||||
arg.ty = program.module.types.fetch_or_append(Type {
|
||||
@@ -880,7 +849,7 @@ impl<'function> Context<'function> {
|
||||
}
|
||||
}
|
||||
|
||||
fn type_power(kind: ScalarKind) -> Option<u32> {
|
||||
pub fn type_power(kind: ScalarKind) -> Option<u32> {
|
||||
Some(match kind {
|
||||
ScalarKind::Sint => 0,
|
||||
ScalarKind::Uint => 1,
|
||||
|
||||
@@ -384,67 +384,126 @@ impl Program<'_> {
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
let mut parameters = Vec::new();
|
||||
let declarations = self.lookup_function.get(&name).ok_or_else(|| {
|
||||
ErrorKind::SemanticError(
|
||||
meta,
|
||||
format!("Unknown function '{}'", name).into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
for (e, meta) in args {
|
||||
let handle = self.resolve_handle(ctx, e, meta)?;
|
||||
let mut maybe_decl = None;
|
||||
|
||||
parameters.push(handle)
|
||||
'outer: for decl in declarations {
|
||||
if args.len() != decl.parameters.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut exact = true;
|
||||
|
||||
for (decl_arg, call_arg) in decl.parameters.iter().zip(args.iter()) {
|
||||
let decl_inner = &self.module.types[*decl_arg].inner;
|
||||
let call_inner = self.resolve_type(ctx, call_arg.0, call_arg.1)?;
|
||||
|
||||
if decl_inner != call_inner {
|
||||
exact = false;
|
||||
|
||||
match (
|
||||
decl_inner.scalar_kind().and_then(type_power),
|
||||
call_inner.scalar_kind().and_then(type_power),
|
||||
) {
|
||||
(Some(decl_power), Some(call_power)) => {
|
||||
if decl_power < call_power {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
_ => continue 'outer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if exact {
|
||||
maybe_decl = Some(decl);
|
||||
break;
|
||||
} else if maybe_decl.is_some() {
|
||||
return Err(ErrorKind::SemanticError(
|
||||
meta,
|
||||
format!("Ambiguous best function for '{}'", name).into(),
|
||||
));
|
||||
} else {
|
||||
maybe_decl = Some(decl)
|
||||
}
|
||||
}
|
||||
|
||||
let sig = FunctionSignature { name, parameters };
|
||||
let decl = maybe_decl.ok_or_else(|| {
|
||||
ErrorKind::SemanticError(
|
||||
meta,
|
||||
format!("Unknown function '{}'", name).into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let fun = self
|
||||
.lookup_function
|
||||
.get(&sig)
|
||||
.ok_or_else(|| {
|
||||
ErrorKind::SemanticError(
|
||||
meta,
|
||||
// FIXME: Proper signature display
|
||||
format!("Unknown function: {:?}", sig).into(),
|
||||
)
|
||||
})?
|
||||
.clone();
|
||||
let qualifiers = decl.qualifiers.clone();
|
||||
let parameters = decl.parameters.clone();
|
||||
let function = decl.handle;
|
||||
let is_void = decl.void;
|
||||
|
||||
let mut arguments = Vec::with_capacity(raw_args.len());
|
||||
let mut arguments = Vec::with_capacity(args.len());
|
||||
let mut proxy_writes = Vec::new();
|
||||
for (qualifier, expr) in fun.qualifiers.iter().zip(raw_args.iter()) {
|
||||
let handle = ctx.lower_expect(self, *expr, qualifier.is_lhs(), body)?.0;
|
||||
if qualifier.is_lhs()
|
||||
&& matches! { ctx.get_expression(handle), &Expression::Swizzle { .. } }
|
||||
for (qualifier, (expr, parameter)) in qualifiers
|
||||
.iter()
|
||||
.zip(raw_args.iter().zip(parameters.iter()))
|
||||
{
|
||||
let (mut handle, meta) =
|
||||
ctx.lower_expect(self, *expr, qualifier.is_lhs(), body)?;
|
||||
|
||||
if let TypeInner::Vector { size, kind, width } =
|
||||
*self.resolve_type(ctx, handle, meta)?
|
||||
{
|
||||
let meta = ctx.hir_exprs[*expr].meta;
|
||||
let ty = self.resolve_handle(ctx, handle, meta)?;
|
||||
let temp_var = ctx.locals.append(LocalVariable {
|
||||
name: None,
|
||||
ty,
|
||||
init: None,
|
||||
});
|
||||
let temp_expr =
|
||||
ctx.add_expression(Expression::LocalVariable(temp_var), body);
|
||||
if qualifier.is_lhs()
|
||||
&& matches!(
|
||||
*ctx.get_expression(handle),
|
||||
Expression::Swizzle { .. }
|
||||
)
|
||||
{
|
||||
let ty = self.module.types.append(Type {
|
||||
name: None,
|
||||
inner: TypeInner::Vector { size, kind, width },
|
||||
});
|
||||
let temp_var = ctx.locals.append(LocalVariable {
|
||||
name: None,
|
||||
ty,
|
||||
init: None,
|
||||
});
|
||||
let temp_expr = ctx
|
||||
.add_expression(Expression::LocalVariable(temp_var), body);
|
||||
|
||||
body.push(Statement::Store {
|
||||
pointer: temp_expr,
|
||||
value: handle,
|
||||
});
|
||||
body.push(Statement::Store {
|
||||
pointer: temp_expr,
|
||||
value: handle,
|
||||
});
|
||||
|
||||
arguments.push(temp_expr);
|
||||
proxy_writes.push((*expr, temp_expr));
|
||||
} else {
|
||||
arguments.push(handle);
|
||||
arguments.push(temp_expr);
|
||||
proxy_writes.push((*expr, temp_expr));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(kind) = self.module.types[*parameter].inner.scalar_kind() {
|
||||
ctx.implicit_conversion(self, &mut handle, meta, kind)?;
|
||||
}
|
||||
|
||||
arguments.push(handle)
|
||||
}
|
||||
|
||||
ctx.emit_flush(body);
|
||||
|
||||
let result = if !fun.void {
|
||||
Some(ctx.add_expression(Expression::Call(fun.handle), body))
|
||||
let result = if !is_void {
|
||||
Some(ctx.add_expression(Expression::Call(function), body))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
body.push(crate::Statement::Call {
|
||||
function: fun.handle,
|
||||
function,
|
||||
arguments,
|
||||
result,
|
||||
});
|
||||
@@ -505,22 +564,46 @@ impl Program<'_> {
|
||||
pub fn add_function(
|
||||
&mut self,
|
||||
mut function: Function,
|
||||
sig: FunctionSignature,
|
||||
name: String,
|
||||
// Normalized function parameters, modifiers are not applied
|
||||
parameters: Vec<Handle<Type>>,
|
||||
qualifiers: Vec<ParameterQualifier>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Handle<Function>, ErrorKind> {
|
||||
ensure_block_returns(&mut function.body);
|
||||
let stage = self.entry_points.get(&sig.name);
|
||||
let stage = self.entry_points.get(&name);
|
||||
|
||||
Ok(if let Some(&stage) = stage {
|
||||
let handle = self.module.functions.append(function);
|
||||
self.entries.push((sig.name, stage, handle));
|
||||
self.entries.push((name, stage, handle));
|
||||
self.function_arg_use.push(Vec::new());
|
||||
handle
|
||||
} else {
|
||||
let void = function.result.is_none();
|
||||
|
||||
if let Some(decl) = self.lookup_function.get_mut(&sig) {
|
||||
let &mut Program {
|
||||
ref mut lookup_function,
|
||||
ref mut module,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let declarations = lookup_function.entry(name).or_default();
|
||||
|
||||
'outer: for decl in declarations.iter_mut() {
|
||||
if parameters.len() != decl.parameters.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (new_parameter, old_parameter) in parameters.iter().zip(decl.parameters.iter())
|
||||
{
|
||||
let new_inner = &module.types[*new_parameter].inner;
|
||||
let old_inner = &module.types[*old_parameter].inner;
|
||||
|
||||
if new_inner != old_inner {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
if decl.defined {
|
||||
return Err(ErrorKind::SemanticError(
|
||||
meta,
|
||||
@@ -529,56 +612,73 @@ impl Program<'_> {
|
||||
}
|
||||
|
||||
decl.defined = true;
|
||||
decl.qualifiers = qualifiers;
|
||||
*self.module.functions.get_mut(decl.handle) = function;
|
||||
decl.handle
|
||||
} else {
|
||||
self.function_arg_use.push(Vec::new());
|
||||
let handle = self.module.functions.append(function);
|
||||
self.lookup_function.insert(
|
||||
sig,
|
||||
FunctionDeclaration {
|
||||
qualifiers,
|
||||
handle,
|
||||
defined: true,
|
||||
void,
|
||||
},
|
||||
);
|
||||
handle
|
||||
return Ok(decl.handle);
|
||||
}
|
||||
|
||||
self.function_arg_use.push(Vec::new());
|
||||
let handle = module.functions.append(function);
|
||||
declarations.push(FunctionDeclaration {
|
||||
parameters,
|
||||
qualifiers,
|
||||
handle,
|
||||
defined: true,
|
||||
void,
|
||||
});
|
||||
handle
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_prototype(
|
||||
&mut self,
|
||||
function: Function,
|
||||
sig: FunctionSignature,
|
||||
name: String,
|
||||
// Normalized function parameters, modifiers are not applied
|
||||
parameters: Vec<Handle<Type>>,
|
||||
qualifiers: Vec<ParameterQualifier>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<(), ErrorKind> {
|
||||
let void = function.result.is_none();
|
||||
|
||||
self.function_arg_use.push(Vec::new());
|
||||
let handle = self.module.functions.append(function);
|
||||
let &mut Program {
|
||||
ref mut lookup_function,
|
||||
ref mut module,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let declarations = lookup_function.entry(name).or_default();
|
||||
|
||||
'outer: for decl in declarations.iter_mut() {
|
||||
if parameters.len() != decl.parameters.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (new_parameter, old_parameter) in parameters.iter().zip(decl.parameters.iter()) {
|
||||
let new_inner = &module.types[*new_parameter].inner;
|
||||
let old_inner = &module.types[*old_parameter].inner;
|
||||
|
||||
if new_inner != old_inner {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
.lookup_function
|
||||
.insert(
|
||||
sig,
|
||||
FunctionDeclaration {
|
||||
qualifiers,
|
||||
handle,
|
||||
defined: false,
|
||||
void,
|
||||
},
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Err(ErrorKind::SemanticError(
|
||||
meta,
|
||||
"Prototype already defined".into(),
|
||||
));
|
||||
}
|
||||
|
||||
self.function_arg_use.push(Vec::new());
|
||||
let handle = module.functions.append(function);
|
||||
declarations.push(FunctionDeclaration {
|
||||
parameters,
|
||||
qualifiers,
|
||||
handle,
|
||||
defined: false,
|
||||
void,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use super::{
|
||||
ast::{
|
||||
Context, FunctionCall, FunctionCallKind, FunctionSignature, GlobalLookup, GlobalLookupKind,
|
||||
HirExpr, HirExprKind, ParameterQualifier, Profile, StorageQualifier, StructLayout,
|
||||
TypeQualifier,
|
||||
Context, FunctionCall, FunctionCallKind, GlobalLookup, GlobalLookupKind, HirExpr,
|
||||
HirExprKind, ParameterQualifier, Profile, StorageQualifier, StructLayout, TypeQualifier,
|
||||
},
|
||||
error::ErrorKind,
|
||||
lex::Lexer,
|
||||
@@ -667,12 +666,10 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
let mut expressions = Arena::new();
|
||||
let mut local_variables = Arena::new();
|
||||
let mut arguments = Vec::new();
|
||||
// Normalized function parameters, modifiers are not applied
|
||||
let mut parameters = Vec::new();
|
||||
let mut qualifiers = Vec::new();
|
||||
let mut body = Block::new();
|
||||
let mut sig = FunctionSignature {
|
||||
name: name.clone(),
|
||||
parameters: Vec::new(),
|
||||
};
|
||||
|
||||
let mut context = Context::new(
|
||||
self.program,
|
||||
@@ -685,8 +682,8 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
self.parse_function_args(
|
||||
&mut context,
|
||||
&mut body,
|
||||
&mut qualifiers,
|
||||
&mut parameters,
|
||||
&mut sig,
|
||||
)?;
|
||||
|
||||
let end_meta = self.expect(TokenValue::RightParen)?.meta;
|
||||
@@ -698,13 +695,14 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
// This branch handles function prototypes
|
||||
self.program.add_prototype(
|
||||
Function {
|
||||
name: Some(name),
|
||||
name: Some(name.clone()),
|
||||
result,
|
||||
arguments,
|
||||
..Default::default()
|
||||
},
|
||||
sig,
|
||||
name,
|
||||
parameters,
|
||||
qualifiers,
|
||||
meta,
|
||||
)?;
|
||||
|
||||
@@ -721,7 +719,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
let Context { arg_use, .. } = context;
|
||||
let handle = self.program.add_function(
|
||||
Function {
|
||||
name: Some(name),
|
||||
name: Some(name.clone()),
|
||||
result,
|
||||
expressions,
|
||||
named_expressions: crate::FastHashMap::default(),
|
||||
@@ -729,8 +727,9 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
arguments,
|
||||
body,
|
||||
},
|
||||
sig,
|
||||
name,
|
||||
parameters,
|
||||
qualifiers,
|
||||
meta,
|
||||
)?;
|
||||
|
||||
@@ -1737,19 +1736,26 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
&mut self,
|
||||
context: &mut Context,
|
||||
body: &mut Block,
|
||||
parameters: &mut Vec<ParameterQualifier>,
|
||||
sig: &mut FunctionSignature,
|
||||
qualifiers: &mut Vec<ParameterQualifier>,
|
||||
parameters: &mut Vec<Handle<Type>>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
if self.peek_type_name() || self.peek_parameter_qualifier() {
|
||||
let qualifier = self.parse_parameter_qualifier();
|
||||
parameters.push(qualifier);
|
||||
qualifiers.push(qualifier);
|
||||
let ty = self.parse_type_non_void()?.0;
|
||||
|
||||
match self.expect_peek()?.value {
|
||||
TokenValue::Comma => {
|
||||
self.bump()?;
|
||||
context.add_function_arg(&mut self.program, sig, body, None, ty, qualifier);
|
||||
context.add_function_arg(
|
||||
&mut self.program,
|
||||
parameters,
|
||||
body,
|
||||
None,
|
||||
ty,
|
||||
qualifier,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
TokenValue::Identifier(_) => {
|
||||
@@ -1760,7 +1766,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
|
||||
context.add_function_arg(
|
||||
&mut self.program,
|
||||
sig,
|
||||
parameters,
|
||||
body,
|
||||
Some(name),
|
||||
ty,
|
||||
|
||||
@@ -471,6 +471,54 @@ fn implicit_conversions() {
|
||||
&entry_points,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
parse_program(
|
||||
r#"
|
||||
# version 450
|
||||
void test(int a) {}
|
||||
void test(uint a) {}
|
||||
|
||||
void main() {
|
||||
test(1.0);
|
||||
}
|
||||
"#,
|
||||
&entry_points
|
||||
)
|
||||
.err()
|
||||
.unwrap(),
|
||||
ErrorKind::SemanticError(
|
||||
SourceMetadata {
|
||||
start: 156,
|
||||
end: 165
|
||||
},
|
||||
"Unknown function \'test\'".into()
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
parse_program(
|
||||
r#"
|
||||
# version 450
|
||||
void test(float a) {}
|
||||
void test(uint a) {}
|
||||
|
||||
void main() {
|
||||
test(1);
|
||||
}
|
||||
"#,
|
||||
&entry_points
|
||||
)
|
||||
.err()
|
||||
.unwrap(),
|
||||
ErrorKind::SemanticError(
|
||||
SourceMetadata {
|
||||
start: 158,
|
||||
end: 165
|
||||
},
|
||||
"Ambiguous best function for \'test\'".into()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -69,30 +69,6 @@ impl Typifier {
|
||||
self.resolutions[expr_handle.index()].inner_with(types)
|
||||
}
|
||||
|
||||
pub fn get_handle(
|
||||
&mut self,
|
||||
expr_handle: Handle<crate::Expression>,
|
||||
types: &mut Arena<crate::Type>,
|
||||
) -> Handle<crate::Type> {
|
||||
let mut dummy = TypeResolution::Value(crate::TypeInner::Sampler { comparison: false });
|
||||
let res = &mut self.resolutions[expr_handle.index()];
|
||||
|
||||
std::mem::swap(&mut dummy, res);
|
||||
|
||||
let v = match dummy {
|
||||
TypeResolution::Handle(h) => h,
|
||||
TypeResolution::Value(inner) => {
|
||||
let h = types.fetch_or_append(crate::Type { name: None, inner });
|
||||
dummy = TypeResolution::Handle(h);
|
||||
h
|
||||
}
|
||||
};
|
||||
|
||||
std::mem::swap(&mut dummy, res);
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn grow(
|
||||
&mut self,
|
||||
expr_handle: Handle<crate::Expression>,
|
||||
|
||||
15
tests/in/glsl/900-implicit-conversions.vert
Normal file
15
tests/in/glsl/900-implicit-conversions.vert
Normal file
@@ -0,0 +1,15 @@
|
||||
// ISSUE: #900
|
||||
#version 450
|
||||
|
||||
// Signature match call the second overload
|
||||
void exact(float a) {}
|
||||
void exact(int a) {}
|
||||
|
||||
// No signature match but one overload satisfies the cast rules
|
||||
void implicit(float a) {}
|
||||
void implicit(int a) {}
|
||||
|
||||
void main() {
|
||||
exact(1);
|
||||
implicit(1u);
|
||||
}
|
||||
39
tests/out/wgsl/900-implicit-conversions-vert.wgsl
Normal file
39
tests/out/wgsl/900-implicit-conversions-vert.wgsl
Normal file
@@ -0,0 +1,39 @@
|
||||
fn exact(a: f32) {
|
||||
var a1: f32;
|
||||
|
||||
a1 = a;
|
||||
return;
|
||||
}
|
||||
|
||||
fn exact1(a2: i32) {
|
||||
var a3: i32;
|
||||
|
||||
a3 = a2;
|
||||
return;
|
||||
}
|
||||
|
||||
fn implicit(a4: f32) {
|
||||
var a5: f32;
|
||||
|
||||
a5 = a4;
|
||||
return;
|
||||
}
|
||||
|
||||
fn implicit1(a6: i32) {
|
||||
var a7: i32;
|
||||
|
||||
a7 = a6;
|
||||
return;
|
||||
}
|
||||
|
||||
fn main1() {
|
||||
exact1(1);
|
||||
implicit(f32(1u));
|
||||
return;
|
||||
}
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn main() {
|
||||
main1();
|
||||
return;
|
||||
}
|
||||
Reference in New Issue
Block a user