Improve error checking for unsized arrays.

Added support for resolving dimensions of multi-dimensional unsized arrays
from their initializer exprerssions (previously, only the first dimension
would be resolved.)

Added checks to make sure that no unsized array dimensions remain after
doing this (except for the first dimensision of array parameters to
functions.)
This commit is contained in:
Matt Pharr
2011-11-21 09:16:29 -08:00
parent 068ea3e4c4
commit d3e6879223
13 changed files with 140 additions and 29 deletions

View File

@@ -272,11 +272,24 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
}
}
// Arrays are passed by reference, so convert array
// parameters to be references here.
if (dynamic_cast<const ArrayType *>(sym->type) != NULL)
const ArrayType *at = dynamic_cast<const ArrayType *>(sym->type);
if (at != NULL) {
// Arrays are passed by reference, so convert array
// parameters to be references here.
sym->type = new ReferenceType(sym->type, sym->type->IsConstType());
// Make sure there are no unsized arrays (other than the
// first dimension) in function parameter lists.
at = dynamic_cast<const ArrayType *>(at->GetElementType());
while (at != NULL) {
if (at->GetElementCount() == 0)
Error(sym->pos, "Arrays with unsized dimensions in "
"dimensions after the first one are illegal in "
"function parameter lists.");
at = dynamic_cast<const ArrayType *>(at->GetElementType());
}
}
args.push_back(sym->type);
argNames.push_back(sym->name);
argPos.push_back(sym->pos);

View File

@@ -237,6 +237,18 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
return;
}
sym->type = ArrayType::SizeUnsizedArrays(sym->type, initExpr);
if (sym->type == NULL)
return;
const ArrayType *at = dynamic_cast<const ArrayType *>(sym->type);
if (at != NULL && at->TotalElementCount() == 0) {
Error(sym->pos, "Illegal to declare a global variable with unsized "
"array dimensions that aren't set with an initializer "
"expression.");
return;
}
LLVM_TYPE_CONST llvm::Type *llvmType = sym->type->LLVMType(g->ctx);
// See if we have an initializer expression for the global. If so,

View File

@@ -255,6 +255,19 @@ lInitSymbol(llvm::Value *lvalue, const char *symName, const Type *symType,
}
static bool
lHasUnsizedArrays(const Type *type) {
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
if (at == NULL)
return false;
if (at->GetElementCount() == 0)
return true;
else
return lHasUnsizedArrays(at->GetElementType());
}
void
DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
if (!ctx->GetCurrentBasicBlock())
@@ -263,8 +276,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
for (unsigned int i = 0; i < vars.size(); ++i) {
Symbol *sym = vars[i].sym;
assert(sym != NULL);
const Type *type = sym->type;
if (type == NULL)
if (sym->type == NULL)
continue;
Expr *initExpr = vars[i].init;
@@ -280,32 +292,29 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
// If it's an array that was declared without a size but has an
// initializer list, then use the number of elements in the
// initializer list to finally set the array's size.
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
if (at && at->GetElementCount() == 0) {
ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
if (exprList) {
ArrayType *t = at->GetSizedArray(exprList->exprs.size());
assert(t != NULL);
sym->type = type = t;
}
else {
Error(sym->pos, "Can't declare an unsized array as a local "
"variable without providing an initializer expression to "
"set its size.");
continue;
}
sym->type = ArrayType::SizeUnsizedArrays(sym->type, initExpr);
if (sym->type == NULL)
continue;
if (lHasUnsizedArrays(sym->type)) {
Error(pos, "Illegal to declare an unsized array variable without "
"providing an initializer expression to set its size.");
continue;
}
// References must have initializer expressions as well.
if (dynamic_cast<const ReferenceType *>(type) && initExpr == NULL) {
if (dynamic_cast<const ReferenceType *>(sym->type) && initExpr == NULL) {
Error(sym->pos,
"Must provide initializer for reference-type variable \"%s\".",
sym->name.c_str());
continue;
}
LLVM_TYPE_CONST llvm::Type *llvmType = type->LLVMType(g->ctx);
assert(llvmType != NULL);
LLVM_TYPE_CONST llvm::Type *llvmType = sym->type->LLVMType(g->ctx);
if (llvmType == NULL) {
assert(m->errorCount > 0);
return;
}
if (sym->storageClass == SC_STATIC) {
// For static variables, we need a compile-time constant value
@@ -313,20 +322,21 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
// zero value.
llvm::Constant *cinit = NULL;
if (initExpr != NULL) {
if (lPossiblyResolveFunctionOverloads(initExpr, type) == false)
if (lPossiblyResolveFunctionOverloads(initExpr, sym->type) == false)
continue;
// FIXME: we only need this for function pointers; it was
// already done for atomic types and enums in
// DeclStmt::TypeCheck()...
if (dynamic_cast<ExprList *>(initExpr) == NULL) {
initExpr = TypeConvertExpr(initExpr, type, "initializer");
initExpr = TypeConvertExpr(initExpr, sym->type,
"initializer");
// FIXME: and this is only needed to re-establish
// constant-ness so that GetConstant below works for
// constant artithmetic expressions...
initExpr = initExpr->Optimize();
}
cinit = initExpr->GetConstant(type);
cinit = initExpr->GetConstant(sym->type);
if (cinit == NULL)
Error(initExpr->pos, "Initializer for static variable "
"\"%s\" must be a constant.", sym->name.c_str());
@@ -337,7 +347,8 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
// Allocate space for the static variable in global scope, so
// that it persists across function calls
sym->storagePtr =
new llvm::GlobalVariable(*m->module, llvmType, type->IsConstType(),
new llvm::GlobalVariable(*m->module, llvmType,
sym->type->IsConstType(),
llvm::GlobalValue::InternalLinkage, cinit,
llvm::Twine("static.") +
llvm::Twine(sym->pos.first_line) +
@@ -353,8 +364,8 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
ctx->EmitVariableDebugInfo(sym);
// And then get it initialized...
sym->parentFunction = ctx->GetFunction();
lInitSymbol(sym->storagePtr, sym->name.c_str(), type, initExpr,
ctx, sym->pos);
lInitSymbol(sym->storagePtr, sym->name.c_str(), sym->type,
initExpr, ctx, sym->pos);
}
}
}

11
tests/unsized-array.ispc Normal file
View File

@@ -0,0 +1,11 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a[][2][] = { { { 1 }, { 2 } }, { { 3 }, { 4 } }, { { 5 }, { 6 } } };
RET[programIndex] = a[1][1][0];
}
export void result(uniform float RET[]) {
RET[programIndex] = 4;
}

View File

@@ -1,4 +1,4 @@
// Can't declare an unsized array as a local variable without providing an initializer expression to set its size
// Illegal to declare an unsized array variable without providing an initializer expression to set its size
int func() {
int a[];

View File

@@ -0,0 +1,3 @@
// Initializer list for array "int32[2][4]" must have 2 elements (has 3).
int a[2][4] = { { 1, 2, 3 }, { 1, 2, 3, 4 }, 1 };

View File

@@ -0,0 +1,6 @@
// Inconsistent expression list lengths found in initializer list
void foo() {
int a[2][] = { { 1, 2, 3 }, { 1, 2, 3, 4 } };
}

View File

@@ -0,0 +1,4 @@
// Illegal to declare an unsized array variable without providing an initializer expression to set its size
void foo() { int a[][]; }

View File

@@ -0,0 +1,4 @@
// Illegal to declare a global variable with unsized array dimensions that aren't set with an initializer expression
int a[][];

View File

@@ -0,0 +1,3 @@
// Initializer list for array "int32[4]" must have 4 elements
int a[2][4] = { { 1, 2, 3 }, { 1, 2, 3, 4 } };

View File

@@ -0,0 +1,4 @@
// Arrays with unsized dimensions in dimensions after the first one are illegal in function parameter lists
void func(int a[1][]) {
}

View File

@@ -1103,6 +1103,44 @@ ArrayType::GetSizedArray(int sz) const {
}
const Type *
ArrayType::SizeUnsizedArrays(const Type *type, Expr *initExpr) {
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
if (at == NULL)
return type;
ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
if (exprList == NULL || exprList->exprs.size() == 0)
return type;
if (at->GetElementCount() == 0)
type = at->GetSizedArray(exprList->exprs.size());
ExprList *nextList = dynamic_cast<ExprList *>(exprList->exprs[0]);
if (nextList == NULL)
return type;
unsigned int nextSize = nextList->exprs.size();
for (unsigned int i = 1; i < exprList->exprs.size(); ++i) {
if (exprList->exprs[i] == NULL) {
assert(m->errorCount > 0);
continue;
}
ExprList *el = dynamic_cast<ExprList *>(exprList->exprs[i]);
if (el == NULL || el->exprs.size() != nextSize) {
Error(Union(exprList->exprs[0]->pos, exprList->exprs[i]->pos),
"Inconsistent expression list lengths found in initializer "
"list.");
return NULL;
}
}
return new ArrayType(SizeUnsizedArrays(at->GetElementType(), nextList),
exprList->exprs.size());
}
///////////////////////////////////////////////////////////////////////////
// SOAArrayType

2
type.h
View File

@@ -429,6 +429,8 @@ public:
length. */
virtual ArrayType *GetSizedArray(int length) const;
static const Type *SizeUnsizedArrays(const Type *type, Expr *initExpr);
private:
friend class SOAArrayType;
/** Type of the elements of the array. */