Introduction Projects and Files Statements Expressions Symbols Types
Attributes Labels Non-Member Functions Constructing Declarations Example Programs Index

Constructing Declarations

In this chapter, we discuss how to use the base classes to construct various declarations.

Building Variable and Parameter Declarations

To create a new variable and declare it in a given scope requires three steps. First, a type chain must be created. Second, a symbol of that type is created in the given scope. Third, a declaration statement is generated.

In C and C++ variable declarations can be very complicated, because the full type is really described in the C source text as a combination of type attributes and a reference expression. For example,

     int & (*x)[10];

has part of the declaration that consists of the type name int and the rest is given by the expression

     & (*x)[10]

Sage++ has another way to describe the type of this object that is closer to how the programmer views it. In this case, the type of x is

This is represented in Sage as a chain of type objects of the form:

The reference expression, &(*x)[10], in the declaration is represented by Sage++ expressions as a chain of the form

The actual dimension list [10] is an expression list of integer values shared by both the SgPntrArrRefExp and the SgArrayType objects.

Note that the reference expression is related to the reverse of the type chain, so it is possible to build the reference expression chain given the type chain.

The SgMakeDeclExp and Related Methods

The function that will automatically create the correct expression chain from the type chain is

  SgExpression * SgMakeDeclExp(SgSymbol *x, SgType *type_chain);

This is a new function, that has been added in Sage++ version 1.7. There is also a method in the SgSymbol class:

  SgExpression *SgSymbol::makeDeclExpr()

(note the subtle differences in spelling!) that does the same thing; it actually calls the SgMakeDeclExp function; the type is set to the type associated with the symbol of interest.

While the user may need to call the SgMakeDeclExp function in some situations, there are more convenient ways to create a declaration without explicitly doing this.

For example, let fun be a pointer to s SgFunctHedrStmt object created as in

SgFuncHedrStmt *fun = new SgFuncHedrStmt("foo");

Then, to add the local variable declaration

  int *z; // - a pointer to an int.

we :

  1. create the type chain,
    SgType *p = new SgPointerType(*SgTypeInt());
  2. create the symbol
    SgSymbol *sym = new SgVariableSymb("z", *p, *fun);
  3. declare it.
The method

void SgSymbol::declareTheSymbol(SgStatement &st)

also calls the SgMakeDeclExp function, but, in addition, it takes care of inserting the declaration in a proper position determined by the scope specified by the st argument.

A more interesting example is the pointer to array of character references described above.

char & (*y)[3]; // - a pointer to an array of char refs.

The three steps are shown below. Notice that the type chain is created from the end of the chain to the front.

  SgType *q = new SgReferenceType(*SgTypeChar());
  SgArrayType *ar = new SgArrayType(*q);
  q = new SgPointerType(*ar);  // q is the final type chain.
  sym = new SgVariableSymb("y", *q, *fun);

There are some cases where declareTheSymbol() method is not the best approach. For example, it will not work for a declaration with an initializer, as in:

  int w[] = {1,2};  // an initialized array.

It is easiest to create the variable declaration statement (SgVarDeclStmt) explicitly and then add the expression for the initializer. The code for this example is shown below.

  // create the int w[]; declaration statement.
  SgArrayType *ar = new SgArrayType(*SgTypeInt());
  SgSymbol *sym = new SgVariableSymb("w", *ar, *fun);
  SgVarDeclStmt *decl = sym->makeVarDeclStmt();

// create the initializer list. {1, 2} SgExpression * one = new SgValueExp(1); SgExpression * two = new SgValueExp(2); SgExprListExp * ls = new SgExprListExp(*one); ls->append(*two); SgExpression * in_ls = new SgInitListExp(*ls); decl->setInitialValue(0, *in_ls);

// explicitly add the declaration to the function body. fun->insertStmtAfter(*decl);

The method

SgVarDeclStmt *SgSymbol::makeVarDeclStmt()

is related to the other methods described above (it also calls the SgMakeDeclExp function).

Another important case involves adding qualifiers (like const, long, short, etc.) to variables. For example, consider the following

  const int x;

The const qualifier must be added as a modifier of the type int. This is accomplished by using a SgDescriptType object which has a special bit field for describing the modifier. The proper type chain is given by

  p = new SgDescriptType(*SgTypeInt(), BIT_CONST);
  sym = new SgVariableSymb("x", *p,  *fun);

Unfortunately, this is not completely general. In the declaration

   int * const x;

the pointer type is constant but not the integer. We do not use a SgDescriptType node for this task. Instead, we set the modifier flag in the SgPointerType object as follows.

   SgPointerType *z = new SgPointerType(*SgTypeInt());
   sym = new SgVariableSymb("x", *z,  *fun);

Function Prototypes

Function prototypes are another type of variable declaration. They are a bit harder to construct than ordinary variables. For example, to build the prototype

  char f(int);

we first must create a function name symbol whose type is the type of the returned value of the function. Function symbols are referred to in expressions by SgFunctionRefExp in much the same way that regular variables are referred to by SgVarRefExp variable reference objects. A SgFunctionRefExp has a pointer to the symbol object for the function and a list of arguments. The

SgExpression * SgFunctionRefExp::AddArg( char *name, SgType &t)

method takes as arguments the name of the argument (which can be an empty string for prototypes) and the type. AddArg automatically creates the reference expression for the argument. Once the SgFunctionRefExp expression is generated, then it must be added to an expression list and a declaration statement must be created as shown below.

  SgFunctionSymb *proto = new SgFunctionSymb(FUNCTION_NAME, "f", 
                                             *SgTypeChar(), *fun);
  SgFunctionRefExp *fref = new SgFunctionRefExp(*proto);
  fref->AddArg("", *SgTypeInt());
  SgExpression *ls = new SgExprListExp(*fref);
  SgStatement *prot_st = new SgVarDeclStmt(*ls, *SgTypeChar());

Function Headers

When building a new function, one must often add parameters. The AddArg method described above is used to add formal parameters to function reference expressions. There is another AddArg method:

SgExpression * SgProcHedrStmt::AddArg(char *name, SgType &t)

that is used to add formal parameters to function header statements (the class SgFuncHedrStmt is a subclass of SgProcHedrStmt). For example, given a function fun constructed as above with

SgFuncHedrStmt *fun = new SgFuncHedrStmt("foo");

we can add formal parameters to this by using AddArg. For example, to create

int foo( char * x[][10]){ }

we proceed exactly as with a variable declaration: build the type chain for the parameter and add the parameter with AddArg. In this case the type chain is

and the code to construct this object and add it as a parameter is given by the following.

  SgType *q = new SgPointerType(*SgTypeChar());
  SgArrayType *ar = new SgArrayType(*q);

Note that AddArg automatically creates the symbol object for the argument and places it in the correct scope.

Pointers To Functions

To create a variable or formal parameter that is a pointer to a function we use the SgFunctionType class. The parameter

     int & (*f)()

is a pointer to a function that returns a reference to an integer and has type chain

The code to generate this object and add it as a parameter to our function foo is given below.

  SgType* q = new SgReferenceType(*SgTypeInt()); 
  q = new SgFunctionType(*q);
  q = new SgPointerType(*q);

Finally, consider the case of a pointer to a function with a more complete prototype, i.e. one that lists the arguments to functions, such as

     int & (*f)( int &, char *)

This object is a pointer to a function that returns a reference to an int and takes a reference to an int and a pointer to a char as parameters. To build this object, we first create the pointer-to-function object and add it as a parameter to our base function fun. Then we use the fact that the AddArg function returns the declaration expression. With the declaration expression, we can find the SgFunctionPntrExp node and add the remaining parameters to that object. The AddArg method used here is:

SgExpression * SgFuncPntrExp::AddArg(SgSymbol *f,  char *name, SgType &t).

It requires an additional parameter.

  SgType * q = new SgReferenceType(*SgTypeInt()); 
  SgType * p = new SgFunctionType(*q);
  q = new SgPointerType(*p);
  SgExpression *exp = fun->AddArg("f",*q);
  // find the function_op (SgFuncPntrExp) node.
  while(exp && (exp->variant() != FUNCTION_OP)) exp = exp->lhs();
  SgFuncPntrExp *fptr = isSgFuncPntrExp(exp);

// find the symbol generated by AddArg e = exp; while(e && !isSgArrayRefExp(e) && !isSgVarRefExp(e)) e = e->lhs(); SgSymbol *fname = e->symbol();

// now add the arguments to f. q = new SgReferenceType(*SgTypeInt()); fptr->AddArg(fname, "", *q); q = new SgPointerType(*SgTypeChar()); fptr->AddArg(fname, "", *q);

Exit Sage++ User's Guide