Files
clang-p2996/clang/test/SemaHLSL/parameter_modifiers.hlsl
Chris B d462621694 [HLSL] Parameter modifier parsing and AST (#72139)
This change implements parsing for HLSL's parameter modifier keywords
`in`, `out` and `inout`. Because HLSL doesn't support references or
pointers, these keywords are used to allow parameters to be passed in
and out of functions.

This change only implements the parsing and AST support. In the HLSL
ASTs we represent `out` and `inout` parameters as references, and we
implement the semantics of by-value passing during IR generation.

In HLSL parameters marked `out` and `inout` are ambiguous in function
declarations, and `in`, `out` and `inout` may be ambiguous at call
sites.

This means a function may be defined as `fn(in T)` and `fn(inout T)` or
`fn(out T)`, but not `fn(inout T)` and `fn(out T)`. If a funciton `fn`
is declared with `in` and `inout` or `out` arguments, the call will be
ambiguous the same as a C++ call would be ambiguous given declarations
`fn(T)` and `fn(T&)`.

Fixes #59849
2023-11-28 15:03:10 -06:00

95 lines
3.0 KiB
HLSL

// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s -verify
void fn(in out float f); // #fn
// expected-error@#fn2{{duplicate parameter modifier 'in'}}
// expected-note@#fn2{{conflicting attribute is here}}
void fn2(in in float f); // #fn2
// expected-error@#fn3{{duplicate parameter modifier 'out'}}
// expected-note@#fn3{{conflicting attribute is here}}
void fn3(out out float f); // #fn3
// expected-error@#fn4{{duplicate parameter modifier 'in'}}
// expected-error@#fn4{{duplicate parameter modifier 'out'}}
// expected-note@#fn4{{conflicting attribute is here}}
// expected-note@#fn4{{conflicting attribute is here}}
void fn4(inout in out float f); // #fn4
// expected-error@#fn5{{duplicate parameter modifier 'in'}}
// expected-note@#fn5{{conflicting attribute is here}}
void fn5(inout in float f); // #fn5
// expected-error@#fn6{{duplicate parameter modifier 'out'}}
// expected-note@#fn6{{conflicting attribute is here}}
void fn6(inout out float f); // #fn6
// expected-error@#fn-def{{conflicting parameter qualifier 'out' on parameter 'f'}}
// expected-note@#fn{{previously declared as 'inout' here}}
void fn(out float f) { // #fn-def
f = 2;
}
// Overload resolution failure.
void fn(in float f); // #fn-in
void failOverloadResolution() {
float f = 1.0;
fn(f); // expected-error{{call to 'fn' is ambiguous}}
// expected-note@#fn-def{{candidate function}}
// expected-note@#fn-in{{candidate function}}
}
void implicitFn(float f);
void inFn(in float f);
void inoutFn(inout float f); // #inoutFn
void outFn(out float f); // #outFn
void callFns() {
// Call with literal arguments.
implicitFn(1); // Ok.
inFn(1); // Ok.
inoutFn(1); // expected-error{{no matching function for call to 'inoutFn'}}
// expected-note@#inoutFn{{candidate function not viable: no known conversion from 'int' to 'float &' for 1st argument}}
outFn(1); // expected-error{{no matching function for call to 'outFn}}
// expected-note@#outFn{{candidate function not viable: no known conversion from 'int' to 'float &' for 1st argument}}
// Call with variables.
float f;
implicitFn(f); // Ok.
inFn(f); // Ok.
inoutFn(f); // Ok.
outFn(f); // Ok.
}
// No errors on these scenarios.
// Alternating `inout` and `in out` spellings between declaration and
// definitions is fine since they have the same semantic meaning.
void fn7(inout float f);
void fn7(in out float f) {}
void fn8(in out float f);
void fn8(inout float f) {}
// These two declare two different functions (although calling them will be
// ambiguous). This is equivalent to declaring a function that takes a
// reference and a function that takes a value of the same type.
void fn9(in float f);
void fn9(out float f);
// The `in` attribute is effectively optional. If no attribute is present it is
// the same as `in`, so these declarations match the functions.
void fn10(in float f);
void fn10(float f) {}
void fn11(float f);
void fn11(in float f) {}
template <typename T>
void fn12(inout T f);
void fn13() {
float f;
fn12<float>(f);
}