Multiple small fixes for better C conformance.
Allow atomic types to be initialized with single-element expression lists:
int x = { 5 };
Issue an error if a storage class is provided with a function parameter.
Issue an error if two members of a struct have the same name.
Issue an error on trying to assign to a struct with a const member, even if
the struct itself isn't const.
Issue an error if a function is redefined.
Issue an error if a function overload is declared that differs only in return
type from a previously-declared function.
Issue an error if "inline" or "task" qualifiers are used outside of function
declarations.
Allow trailing ',' at the end of enumerator lists.
Multiple tests for all of the above.
This commit is contained in:
38
decl.cpp
38
decl.cpp
@@ -44,7 +44,7 @@
|
||||
#include "stmt.h"
|
||||
#include "expr.h"
|
||||
#include <stdio.h>
|
||||
#include <llvm/Module.h>
|
||||
#include <set>
|
||||
|
||||
/** Given a Type and a set of type qualifiers, apply the type qualifiers to
|
||||
the type, returning the type that is the result.
|
||||
@@ -112,13 +112,24 @@ DeclSpecs::GetBaseType(SourcePos pos) const {
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
lGetStorageClassName(StorageClass storageClass) {
|
||||
switch (storageClass) {
|
||||
case SC_NONE: return "";
|
||||
case SC_EXTERN: return "extern";
|
||||
case SC_EXTERN_C: return "extern \"C\"";
|
||||
case SC_EXPORT: return "export";
|
||||
case SC_STATIC: return "static";
|
||||
case SC_TYPEDEF: return "typedef";
|
||||
default: FATAL("Unhandled storage class in lGetStorageClassName");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DeclSpecs::Print() const {
|
||||
if (storageClass == SC_EXTERN) printf("extern ");
|
||||
if (storageClass == SC_EXTERN_C) printf("extern \"C\" ");
|
||||
if (storageClass == SC_EXPORT) printf("export ");
|
||||
if (storageClass == SC_STATIC) printf("static ");
|
||||
if (storageClass == SC_TYPEDEF) printf("typedef ");
|
||||
printf("%s ", lGetStorageClassName(storageClass));
|
||||
|
||||
if (soaWidth > 0) printf("soa<%d> ", soaWidth);
|
||||
|
||||
@@ -310,11 +321,17 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
|
||||
// Handle more complex anonymous declarations like
|
||||
// float (float **).
|
||||
sprintf(buf, "__anon_parameter_%d", i);
|
||||
sym = new Symbol(buf, pos);
|
||||
sym = new Symbol(buf, d->declarators[0]->pos);
|
||||
sym->type = d->declarators[0]->GetType(d->declSpecs);
|
||||
}
|
||||
}
|
||||
|
||||
if (d->declSpecs->storageClass != SC_NONE)
|
||||
Error(sym->pos, "Storage class \"%s\" is illegal in "
|
||||
"function parameter declaration for parameter \"%s\".",
|
||||
lGetStorageClassName(d->declSpecs->storageClass),
|
||||
sym->name.c_str());
|
||||
|
||||
const ArrayType *at = dynamic_cast<const ArrayType *>(sym->type);
|
||||
if (at != NULL) {
|
||||
// As in C, arrays are passed to functions as pointers to
|
||||
@@ -497,6 +514,7 @@ GetStructTypesNamesPositions(const std::vector<StructDeclaration *> &sd,
|
||||
std::vector<const Type *> *elementTypes,
|
||||
std::vector<std::string> *elementNames,
|
||||
std::vector<SourcePos> *elementPositions) {
|
||||
std::set<std::string> seenNames;
|
||||
for (unsigned int i = 0; i < sd.size(); ++i) {
|
||||
const Type *type = sd[i]->type;
|
||||
// FIXME: making this fake little DeclSpecs here is really
|
||||
@@ -523,6 +541,12 @@ GetStructTypesNamesPositions(const std::vector<StructDeclaration *> &sd,
|
||||
else
|
||||
elementTypes->push_back(sym->type);
|
||||
|
||||
if (seenNames.find(sym->name) != seenNames.end())
|
||||
Error(d->pos, "Struct member \"%s\" has same name as a "
|
||||
"previously-declared member.", sym->name.c_str());
|
||||
else
|
||||
seenNames.insert(sym->name);
|
||||
|
||||
elementNames->push_back(sym->name);
|
||||
elementPositions->push_back(sym->pos);
|
||||
}
|
||||
|
||||
13
expr.cpp
13
expr.cpp
@@ -2155,7 +2155,12 @@ AssignExpr::TypeCheck() {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (dynamic_cast<const ArrayType *>(lhsType) != NULL) {
|
||||
Error(pos, "Illegal to assign to array type \"%s\".",
|
||||
lhsType->GetString().c_str());
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
rvalue = TypeConvertExpr(rvalue, lhsType, lOpString(op));
|
||||
|
||||
if (rvalue == NULL)
|
||||
@@ -2755,6 +2760,12 @@ ExprList::TypeCheck() {
|
||||
|
||||
llvm::Constant *
|
||||
ExprList::GetConstant(const Type *type) const {
|
||||
if (exprs.size() == 1 &&
|
||||
(dynamic_cast<const AtomicType *>(type) != NULL ||
|
||||
dynamic_cast<const EnumType *>(type) != NULL ||
|
||||
dynamic_cast<const PointerType *>(type) != NULL))
|
||||
return exprs[0]->GetConstant(type);
|
||||
|
||||
const CollectionType *collectionType =
|
||||
dynamic_cast<const CollectionType *>(type);
|
||||
if (collectionType == NULL)
|
||||
|
||||
7
func.cpp
7
func.cpp
@@ -339,6 +339,13 @@ Function::GenerateIR() {
|
||||
llvm::Function *function = sym->function;
|
||||
assert(function != NULL);
|
||||
|
||||
// But if that function has a definition, we don't want to redefine it.
|
||||
if (function->empty() == false) {
|
||||
Error(sym->pos, "Ignoring redefinition of function \"%s\".",
|
||||
sym->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out a reasonable source file position for the start of the
|
||||
// function body. If possible, get the position of the first actual
|
||||
// non-StmtList statment...
|
||||
|
||||
45
module.cpp
45
module.cpp
@@ -400,10 +400,40 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbolTable->LookupFunction(funSym->name.c_str(),
|
||||
functionType) != NULL)
|
||||
// Ignore redeclaration of a function with the same name and type
|
||||
return;
|
||||
std::vector<Symbol *> *overloadFuncs =
|
||||
symbolTable->LookupFunction(funSym->name.c_str());
|
||||
if (overloadFuncs != NULL) {
|
||||
for (unsigned int i = 0; i < overloadFuncs->size(); ++i) {
|
||||
Symbol *overloadFunc = (*overloadFuncs)[i];
|
||||
|
||||
// Check for a redeclaration of a function with the same
|
||||
// name and type
|
||||
if (Type::Equal(overloadFunc->type, functionType))
|
||||
return;
|
||||
|
||||
// If all of the parameter types match but the return type is
|
||||
// different, return an error--overloading by return type isn't
|
||||
// allowed.
|
||||
const FunctionType *ofType =
|
||||
dynamic_cast<const FunctionType *>(overloadFunc->type);
|
||||
assert(ofType != NULL);
|
||||
if (ofType->GetNumParameters() == functionType->GetNumParameters()) {
|
||||
int i;
|
||||
for (i = 0; i < functionType->GetNumParameters(); ++i) {
|
||||
if (Type::Equal(ofType->GetParameterType(i),
|
||||
functionType->GetParameterType(i)) == false)
|
||||
break;
|
||||
}
|
||||
if (i == functionType->GetNumParameters()) {
|
||||
Error(funSym->pos, "Illegal to overload function by return "
|
||||
"type only (previous declaration was at line %d of "
|
||||
"file %s).", overloadFunc->pos.first_line,
|
||||
overloadFunc->pos.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (funSym->storageClass == SC_EXTERN_C) {
|
||||
// Make sure the user hasn't supplied both an 'extern "C"' and a
|
||||
@@ -538,13 +568,6 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
|
||||
}
|
||||
funSym->function = function;
|
||||
|
||||
// But if that function has a definition, we don't want to redefine it.
|
||||
if (!function->empty()) {
|
||||
Warning(funSym->pos, "Ignoring redefinition of function \"%s\".",
|
||||
funSym->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, we know all is good and we can add the function to the
|
||||
// symbol table
|
||||
bool ok = symbolTable->AddFunction(funSym);
|
||||
|
||||
67
parse.yy
67
parse.yy
@@ -34,7 +34,7 @@
|
||||
%locations
|
||||
|
||||
/* supress shift-reduces conflict message for dangling else */
|
||||
/* one for 'if', one for 'uif' */
|
||||
/* one for 'if', one for 'cif' */
|
||||
%expect 2
|
||||
|
||||
%pure-parser
|
||||
@@ -96,6 +96,8 @@ static void lAddThreadIndexCountToSymbolTable(SourcePos pos);
|
||||
static std::string lGetAlternates(std::vector<std::string> &alternates);
|
||||
static const char *lGetStorageClassString(StorageClass sc);
|
||||
static bool lGetConstantInt(Expr *expr, int *value, SourcePos pos, const char *usage);
|
||||
static EnumType *lCreateEnumType(const char *name, std::vector<Symbol *> *enums,
|
||||
SourcePos pos);
|
||||
static void lFinalizeEnumeratorSymbols(std::vector<Symbol *> &enums,
|
||||
const EnumType *enumType);
|
||||
|
||||
@@ -732,9 +734,18 @@ specifier_qualifier_list
|
||||
$$ = $2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
UNIMPLEMENTED;
|
||||
else if ($1 == TYPEQUAL_INLINE) {
|
||||
Error(@1, "\"inline\" qualifier is illegal outside of "
|
||||
"function declarations.");
|
||||
$$ = $2;
|
||||
}
|
||||
else if ($1 == TYPEQUAL_TASK) {
|
||||
Error(@1, "\"task\" qualifier is illegal outside of "
|
||||
"function declarations.");
|
||||
$$ = $2;
|
||||
}
|
||||
else
|
||||
FATAL("Unhandled type qualifier in parser.");
|
||||
}
|
||||
else
|
||||
$$ = NULL;
|
||||
@@ -773,32 +784,19 @@ enum_identifier
|
||||
enum_specifier
|
||||
: TOKEN_ENUM '{' enumerator_list '}'
|
||||
{
|
||||
if ($3 != NULL) {
|
||||
EnumType *enumType = new EnumType(@1);
|
||||
|
||||
lFinalizeEnumeratorSymbols(*$3, enumType);
|
||||
for (unsigned int i = 0; i < $3->size(); ++i)
|
||||
m->symbolTable->AddVariable((*$3)[i]);
|
||||
enumType->SetEnumerators(*$3);
|
||||
$$ = enumType;
|
||||
}
|
||||
else
|
||||
$$ = NULL;
|
||||
$$ = lCreateEnumType(NULL, $3, @1);
|
||||
}
|
||||
| TOKEN_ENUM enum_identifier '{' enumerator_list '}'
|
||||
{
|
||||
if ($4 != NULL) {
|
||||
EnumType *enumType = new EnumType($2, $2);
|
||||
m->symbolTable->AddType($2, enumType, @2);
|
||||
|
||||
lFinalizeEnumeratorSymbols(*$4, enumType);
|
||||
for (unsigned int i = 0; i < $4->size(); ++i)
|
||||
m->symbolTable->AddVariable((*$4)[i]);
|
||||
enumType->SetEnumerators(*$4);
|
||||
$$ = enumType;
|
||||
}
|
||||
else
|
||||
$$ = NULL;
|
||||
$$ = lCreateEnumType($2, $4, @2);
|
||||
}
|
||||
| TOKEN_ENUM '{' enumerator_list ',' '}'
|
||||
{
|
||||
$$ = lCreateEnumType(NULL, $3, @1);
|
||||
}
|
||||
| TOKEN_ENUM enum_identifier '{' enumerator_list ',' '}'
|
||||
{
|
||||
$$ = lCreateEnumType($2, $4, @2);
|
||||
}
|
||||
| TOKEN_ENUM enum_identifier
|
||||
{
|
||||
@@ -1579,6 +1577,23 @@ lGetConstantInt(Expr *expr, int *value, SourcePos pos, const char *usage) {
|
||||
}
|
||||
|
||||
|
||||
static EnumType *
|
||||
lCreateEnumType(const char *name, std::vector<Symbol *> *enums, SourcePos pos) {
|
||||
if (enums == NULL)
|
||||
return NULL;
|
||||
|
||||
EnumType *enumType = name ? new EnumType(name, pos) : new EnumType(pos);
|
||||
if (name != NULL)
|
||||
m->symbolTable->AddType(name, enumType, pos);
|
||||
|
||||
lFinalizeEnumeratorSymbols(*enums, enumType);
|
||||
for (unsigned int i = 0; i < enums->size(); ++i)
|
||||
m->symbolTable->AddVariable((*enums)[i]);
|
||||
enumType->SetEnumerators(*enums);
|
||||
return enumType;
|
||||
}
|
||||
|
||||
|
||||
/** Given an array of enumerator symbols, make sure each of them has a
|
||||
ConstExpr * in their Symbol::constValue member that stores their
|
||||
unsigned integer value. Symbols that had values explicitly provided
|
||||
|
||||
14
stmt.cpp
14
stmt.cpp
@@ -182,10 +182,16 @@ lInitSymbol(llvm::Value *lvalue, const char *symName, const Type *symType,
|
||||
if (dynamic_cast<const AtomicType *>(symType) != NULL ||
|
||||
dynamic_cast<const EnumType *>(symType) != NULL ||
|
||||
dynamic_cast<const PointerType *>(symType) != NULL) {
|
||||
if (dynamic_cast<ExprList *>(initExpr) != NULL)
|
||||
Error(initExpr->pos, "Expression list initializers can't be used for "
|
||||
"variable \"%s\' with type \"%s\".", symName,
|
||||
symType->GetString().c_str());
|
||||
ExprList *elist = dynamic_cast<ExprList *>(initExpr);
|
||||
if (elist != NULL) {
|
||||
if (elist->exprs.size() == 1)
|
||||
lInitSymbol(lvalue, symName, symType, elist->exprs[0], ctx,
|
||||
pos);
|
||||
else
|
||||
Error(initExpr->pos, "Expression list initializers can't be used for "
|
||||
"variable \"%s\' with type \"%s\".", symName,
|
||||
symType->GetString().c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
|
||||
struct Foo { float f; };
|
||||
|
||||
void f(uniform Foo foo[], float a) {
|
||||
++foo[a].f;
|
||||
}
|
||||
|
||||
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
|
||||
float a = aFOO[programIndex];
|
||||
float f[40], g[40];
|
||||
for (uniform int i = 0; i < 40; ++i) {
|
||||
f[i] = a;
|
||||
g[i] = b;
|
||||
}
|
||||
if (a < 2)
|
||||
f = g;
|
||||
RET[programIndex] = f[a];
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 1+programIndex;
|
||||
RET[0] = 5;
|
||||
}
|
||||
14
tests/init-atomic-with-expr-list.ispc
Normal file
14
tests/init-atomic-with-expr-list.ispc
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
int b = { 2. };
|
||||
|
||||
export void f_f(uniform float RET[], uniform float aFOO[]) {
|
||||
float a = aFOO[programIndex];
|
||||
float aa = { a };
|
||||
RET[programIndex] = aa+b;
|
||||
}
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 3 + 1*programIndex;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
export uniform int width() { return programCount; }
|
||||
|
||||
|
||||
export void f_v(uniform float RET[]) { RET[programIndex] = 1.; }
|
||||
export void f_v(uniform float RET[]) { RET[programIndex] = 2.; }
|
||||
|
||||
export void result(uniform float RET[]) {
|
||||
RET[programIndex] = 1.000000;
|
||||
}
|
||||
6
tests_errors/array-plus-equals.ispc
Normal file
6
tests_errors/array-plus-equals.ispc
Normal file
@@ -0,0 +1,6 @@
|
||||
// Illegal to assign to array type "float[5]"
|
||||
|
||||
void foo(float *x) {
|
||||
float a[5] = { 1,2,3,4,5};
|
||||
a += 3;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// ffofoof
|
||||
// Illegal to assign to array type "float[5]"
|
||||
|
||||
void foo(float *x) {
|
||||
float a[5] = { 1,2,3,4,5};
|
||||
|
||||
14
tests_errors/assign-struct-with-const-member-2.ispc
Normal file
14
tests_errors/assign-struct-with-const-member-2.ispc
Normal file
@@ -0,0 +1,14 @@
|
||||
// Illegal to assign to type "uniform struct Bar" in type "uniform struct Foo" due to element "a" with type "const int32"
|
||||
|
||||
struct Bar {
|
||||
const int a;
|
||||
};
|
||||
|
||||
struct Foo {
|
||||
struct Bar b;
|
||||
};
|
||||
|
||||
void foo(Foo f) {
|
||||
Foo g;
|
||||
g = f;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// ffofoof
|
||||
// Illegal to assign to type "uniform struct Foo" due to element "a" with type "const int32"
|
||||
|
||||
struct Foo {
|
||||
const int a;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// Expression list initializers can't be used
|
||||
|
||||
int func() {
|
||||
int a = { 1 };
|
||||
}
|
||||
16
tests_errors/func-overload-by-return-type.ispc
Normal file
16
tests_errors/func-overload-by-return-type.ispc
Normal file
@@ -0,0 +1,16 @@
|
||||
// Illegal to overload function by return type only
|
||||
|
||||
float foo() {
|
||||
int x = { 2 };
|
||||
}
|
||||
|
||||
int y = { 2 };
|
||||
|
||||
void foo() {
|
||||
//CO while (true)
|
||||
//CO ;
|
||||
//CO for (;;)
|
||||
//CO ;
|
||||
do ; while(1);
|
||||
}
|
||||
|
||||
6
tests_errors/func-param-static.ispc
Normal file
6
tests_errors/func-param-static.ispc
Normal file
@@ -0,0 +1,6 @@
|
||||
// Storage class "static" is illegal in function parameter declaration for parameter "x"
|
||||
|
||||
void foo(static int x) {
|
||||
}
|
||||
|
||||
|
||||
16
tests_errors/function-redefinition.ispc
Normal file
16
tests_errors/function-redefinition.ispc
Normal file
@@ -0,0 +1,16 @@
|
||||
// Ignoring redefinition of function "foo".
|
||||
|
||||
float foo() {
|
||||
int x = { 2 };
|
||||
}
|
||||
|
||||
int y = { 2 };
|
||||
|
||||
float foo() {
|
||||
//CO while (true)
|
||||
//CO ;
|
||||
//CO for (;;)
|
||||
//CO ;
|
||||
do ; while(1);
|
||||
}
|
||||
|
||||
5
tests_errors/struct-bad-qualifiers.ispc
Normal file
5
tests_errors/struct-bad-qualifiers.ispc
Normal file
@@ -0,0 +1,5 @@
|
||||
// "task" qualifier is illegal outside of function declarations
|
||||
|
||||
struct Foo {
|
||||
task float x;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
// ffofoof
|
||||
// Struct member "a" has same name as a previously-declared member
|
||||
|
||||
struct Foo {
|
||||
int a, a;
|
||||
|
||||
3
tests_errors/unsigned-float.ispc
Normal file
3
tests_errors/unsigned-float.ispc
Normal file
@@ -0,0 +1,3 @@
|
||||
// "unsigned" qualifier is illegal with "float" type
|
||||
|
||||
unsigned float foo = 1;
|
||||
Reference in New Issue
Block a user