@@ -81,6 +81,7 @@ class AST
8181 // This function will be used to check thre type of return case in functions.
8282 // Default is NO_RETURN so we don't have to override it in every class.
8383 virtual ReturnInfo check_return_case () const { return ReturnInfo (NO_RETURN, false , false ); }
84+ virtual bool contains_loop () const { return false ; }
8485
8586 virtual llvm::Value *igen () { return nullptr ; }
8687 virtual void LLVM_IR_gen ();
@@ -211,6 +212,11 @@ class AST
211212 {
212213 return llvm::ConstantInt::get (TheContext, llvm::APInt (32 , n, true ));
213214 }
215+ static llvm::ConstantInt *c64 (int64_t n)
216+ {
217+ return llvm::ConstantInt::get (TheContext, llvm::APInt (64 , n, true ));
218+ }
219+
214220 static llvm::AllocaInst *createEntryBlockAlloca (
215221 llvm::Function *function,
216222 const std::string &name,
@@ -844,7 +850,15 @@ class LValue : public Expr
844850 {
845851 if (stringLiteral)
846852 {
847- this ->type = TYPE_BYTE_ARRAY;
853+ if (!indices.empty ()) {
854+ if (indices.size () > 1 ) {
855+ semantic_error (line, column, " String literal can only be indexed once." );
856+ }
857+ indices[0 ]->check_type (TYPE_INT);
858+ this ->type = TYPE_BYTE;
859+ } else {
860+ this ->type = TYPE_BYTE_ARRAY;
861+ }
848862 return ;
849863 }
850864
@@ -884,10 +898,6 @@ class LValue : public Expr
884898 this ->type = symbolTypeFromArray (var->type );
885899 else
886900 this ->type = var->type ; // multi-dimensional arrays keep their type
887-
888- // printf("LValue entry %s array dimensions: ", name);
889- // for(int d : var->arrayDimensions) printf("%d ", d);
890- // printf("\n");
891901 }
892902 else
893903 {
@@ -920,15 +930,33 @@ class LValue : public Expr
920930 strConst,
921931 globalName);
922932
923- gvar->setAlignment (llvm::Align (1 ));
924-
925- // Pointer to first character
926- llvm::Constant *zero = AST::c32 (0 );
927- std::vector<llvm::Constant *> idxList = {zero, zero};
928- llvm::Constant *strPtr = llvm::ConstantExpr::getGetElementPtr (
929- strConst->getType (), gvar, idxList);
930-
931- return strPtr;
933+ gvar->setAlignment (llvm::MaybeAlign (1 ));
934+ gvar->setUnnamedAddr (llvm::GlobalValue::UnnamedAddr::Global);
935+
936+ // Indices for GEP into [N x i8]: {0, idx}
937+ llvm::Value *idx0 =AST::c64 (0 ); // First index is always 0 (select the array, not an element past it)
938+ llvm::Value *idx1;
939+
940+ if (!indices.empty ()) {
941+ // Dynamic/variable index
942+ llvm::Value *v = indices[0 ]->igen ();
943+ // Cast/truncate/extend to i64 for GEP
944+ if (!v->getType ()->isIntegerTy (64 ))
945+ v = AST::Builder.CreateSExtOrTrunc (v, AST::i64 , " idx64" );
946+ idx1 = v;
947+ } else {
948+ // No indexing -> point at first char
949+ idx1 = c64 (0 );
950+ }
951+
952+ llvm::Value *chPtr = AST::Builder.CreateInBoundsGEP (
953+ gvar->getValueType (), // [N x i8]
954+ gvar, // [N x i8]*
955+ {idx0, idx1}, // {0, i}
956+ " str.chptr"
957+ );
958+ // return Value of type i8*
959+ return chPtr; // i8*
932960 }
933961
934962 // === Case 2 & 3: Variable lookup (function arg or local) ===
@@ -1589,6 +1617,13 @@ class Block : public Stmt
15891617 return result;
15901618 }
15911619
1620+ bool contains_loop () const override {
1621+ for (Stmt* stmt : stmt_list) {
1622+ if (stmt->contains_loop ()) return true ;
1623+ }
1624+ return false ;
1625+ }
1626+
15921627 llvm::Value *igen () override
15931628 {
15941629 // Save current variable mapping
@@ -1904,6 +1939,15 @@ class IfStmt : public Stmt
19041939 return combine_if (branches);
19051940 }
19061941
1942+ bool contains_loop () const override {
1943+ if (then_block->contains_loop ()) return true ;
1944+ for (IfStmt* i : elifs) {
1945+ if (i->contains_loop ()) return true ;
1946+ }
1947+ if (else_block && else_block->contains_loop ()) return true ;
1948+ return false ;
1949+ }
1950+
19071951 llvm::Value *igen () override
19081952 {
19091953 llvm::Function *function = AST::Builder.GetInsertBlock ()->getParent ();
@@ -2041,6 +2085,10 @@ class LoopStmt : public Stmt
20412085 return ReturnInfo (rc, false , false );
20422086 }
20432087
2088+ bool contains_loop () const override {
2089+ return true ;
2090+ }
2091+
20442092 llvm::Value *igen () override
20452093 {
20462094 llvm::Function *function = AST::Builder.GetInsertBlock ()->getParent ();
@@ -2577,10 +2625,12 @@ class FParDef : public AST
25772625 if (type->getDataType () == DataType::INT)
25782626 {
25792627 entry->type = TYPE_INT_ARRAY;
2628+ entry->isAssigned = true ;
25802629 }
25812630 else if (type->getDataType () == DataType::BYTE)
25822631 {
25832632 entry->type = TYPE_BYTE_ARRAY;
2633+ entry->isAssigned = true ;
25842634 }
25852635 else
25862636 {
@@ -2591,7 +2641,11 @@ class FParDef : public AST
25912641 entry->isRef = true ; // all arrays are passed by reference
25922642 }
25932643 entry->persistent = true ; // Parameters should be persistent because they are needed later
2594- entry->isVar = true ;
2644+
2645+ if (entry->isRef ) {
2646+ entry->isVar = true ;
2647+ }
2648+
25952649 st.insert (id, entry);
25962650 }
25972651 }
@@ -2845,6 +2899,14 @@ class FuncDecl : public AST
28452899 void sem_analyze () override
28462900 {
28472901 header->sem_analyze ();
2902+ STEntry *he = st.lookup (header->getName ());
2903+
2904+ if (header->getParams ().empty () && he->functionReturnType == TYPE_VOID)
2905+ {
2906+ std::ostringstream sout;
2907+ sout << " Function \" " << header->getName () << " \" declaration has no return type or parameters." ;
2908+ semantic_warning (sout.str ());
2909+ }
28482910
28492911 // Let's create a new scope for the decleration so
28502912 // its parameters are entered into the SymbolTable
@@ -2990,17 +3052,19 @@ class VarDef : public AST
29903052 oss << " Duplicate variable definition: variable \" " << varName << " \" is already defined in the current scope." ;
29913053 semantic_error (line, column, oss.str ());
29923054 }
2993-
3055+
29943056 STEntry *entry = new STEntry ();
29953057 if (!type->getParenthesisList ().empty ())
29963058 {
29973059 if (type->getDataType () == DataType::INT)
29983060 {
29993061 entry->type = TYPE_INT_ARRAY;
3062+ entry->isAssigned = true ;
30003063 }
30013064 else if (type->getDataType () == DataType::BYTE)
30023065 {
30033066 entry->type = TYPE_BYTE_ARRAY;
3067+ entry->isAssigned = true ;
30043068 }
30053069 else
30063070 {
@@ -3142,7 +3206,24 @@ class FuncDef : public AST
31423206 }
31433207
31443208 ReturnInfo check_return_case () const override {
3145- return block->check_return_case ();
3209+ ReturnInfo ri = block->check_return_case ();
3210+
3211+ // Added after discussion with Professor Sagonas on issue #43
3212+ if (ri.rc == WEAK_RETURN && contains_loop ()) {
3213+ ri.rc = STRONG_RETURN;
3214+
3215+ std::ostringstream oss;
3216+ oss << " Function \" " << header->getName () << " \" contains a loop and may not always return a value."
3217+ << " Please add a return statement at the end of the function to ensure all possible paths return a value." ;
3218+
3219+ semantic_warning_wline (line, column, oss.str ());
3220+ }
3221+
3222+ return ri;
3223+ }
3224+
3225+ bool contains_loop () const override {
3226+ return block->contains_loop ();
31463227 }
31473228
31483229 Header *getHeader () const
@@ -3155,6 +3236,14 @@ class FuncDef : public AST
31553236 // Check for previous declaration before inserting the new entry of definition
31563237 STEntry *previousDecl = st.getCurrentScope ().searchPreviousDeclaration (header->getName ());
31573238
3239+ // Check for previous definition -> error: no duplicate defs
3240+ STEntry *existingDef = st.lookup (header->getName ());
3241+ if (existingDef && existingDef->type == TYPE_FUNCTION && !existingDef->isDeclaration ) {
3242+ std::ostringstream oss;
3243+ oss << " Duplicate definition of function \" " << header->getName () << " \" is not allowed!" ;
3244+ semantic_error (line, column, oss.str ());
3245+ }
3246+
31583247 // 1st: insert the function's own header into the parent scope
31593248 header->sem_analyze (); // FParDefs are inserted here... No!
31603249
@@ -3292,7 +3381,20 @@ class FuncDef : public AST
32923381 }
32933382 }
32943383 }
3384+
3385+ const Scope& scope = st.getCurrentScope ();
3386+ const std::map<std::string, STEntry*>& entriesMap = scope.getLocals ();
3387+
32953388 block->sem_analyze ();
3389+
3390+ for (auto it = entriesMap.begin (); it != entriesMap.end (); it++) {
3391+ if (!it->second ->isAssigned && it->second ->isVar && it->second ->arrayDimensions .empty ()) {
3392+ std::ostringstream sout;
3393+ sout << " Variable \" " << it->first << " \" is declared but never assigned a value!" ;
3394+ semantic_warning (sout.str ());
3395+ }
3396+ }
3397+
32963398 st.exitScope ();
32973399 }
32983400
0 commit comments