@@ -438,6 +438,37 @@ struct RvalueExprVisitor : public ExprVisitor {
438438      return  value;
439439    }
440440
441+     //  We're reading a class property.
442+     if  (auto  *const  property =
443+             expr.symbol .as_if <slang::ast::ClassPropertySymbol>()) {
444+       auto  type = context.convertType (*expr.type );
445+ 
446+       //  Get the scope's implicit this variable
447+       mlir::Value instRef = context.getImplicitThisRef ();
448+       if  (!instRef) {
449+         mlir::emitError (loc) << " class property '" name 
450+                              << " ' referenced without an implicit 'this'" 
451+         return  {};
452+       }
453+ 
454+       auto  fieldSym =
455+           mlir::FlatSymbolRefAttr::get (builder.getContext (), property->name );
456+       auto  fieldTy = cast<moore::UnpackedType>(type);
457+       auto  fieldRefTy = moore::RefType::get (fieldTy);
458+ 
459+       moore::ClassHandleType classTy =
460+           cast<moore::ClassHandleType>(instRef.getType ());
461+ 
462+       auto  targetClassHandle =
463+           context.getAncestorClassWithProperty (classTy, property->name );
464+       auto  upcastRef = context.materializeConversion (targetClassHandle, instRef,
465+                                                      false , instRef.getLoc ());
466+ 
467+       Value fieldRef = moore::ClassPropertyRefOp::create (
468+           builder, loc, fieldRefTy, upcastRef, fieldSym);
469+       return  moore::ReadOp::create (builder, loc, fieldRef).getResult ();
470+     }
471+ 
441472    //  Try to materialize constant values directly.
442473    auto  constant = context.evaluateConstant (expr);
443474    if  (auto  value = context.materializeConstant (constant, *expr.type , loc))
@@ -1155,12 +1186,6 @@ struct RvalueExprVisitor : public ExprVisitor {
11551186
11561187  // / Handle calls.
11571188  Value visit (const  slang::ast::CallExpression &expr) {
1158-     //  Class method calls are currently not supported.
1159-     if  (expr.thisClass ()) {
1160-       mlir::emitError (loc, " unsupported class method call" 
1161-       return  {};
1162-     }
1163- 
11641189    //  Try to materialize constant values directly.
11651190    auto  constant = context.evaluateConstant (expr);
11661191    if  (auto  value = context.materializeConstant (constant, *expr.type , loc))
@@ -1171,9 +1196,90 @@ struct RvalueExprVisitor : public ExprVisitor {
11711196        expr.subroutine );
11721197  }
11731198
1199+   // / Get both the actual `this` argument of a method call and the required
1200+   // / class type.
1201+   std::pair<mlir::Value, moore::ClassHandleType>
1202+   getMethodReceiverTypeHandle (const  slang::ast::CallExpression &expr) {
1203+ 
1204+     moore::ClassHandleType handleTy;
1205+     mlir::Value thisRef;
1206+ 
1207+     //  Qualified call: t.m(...), extract from thisClass.
1208+     if  (const  slang::ast::Expression *recvExpr = expr.thisClass ()) {
1209+       thisRef = context.convertRvalueExpression (*recvExpr);
1210+       if  (!thisRef)
1211+         return  {};
1212+ 
1213+       handleTy = dyn_cast<moore::ClassHandleType>(thisRef.getType ());
1214+       if  (!handleTy) {
1215+         mlir::emitError (loc)
1216+             << " receiver of method '" getSubroutineName ()
1217+             << " ' must be class<...>, got " getType ();
1218+         return  {};
1219+       }
1220+     } else  {
1221+       //  Unqualified call inside a method body: try using implicit %this.
1222+       thisRef = context.getImplicitThisRef ();
1223+       if  (!thisRef) {
1224+         mlir::emitError (loc) << " method '" getSubroutineName ()
1225+                              << " ' called without an object" 
1226+         return  {};
1227+       }
1228+       handleTy = dyn_cast<moore::ClassHandleType>(thisRef.getType ());
1229+       if  (!handleTy) {
1230+         mlir::emitError (loc)
1231+             << " implicit 'this' must be class<...>, got " getType ();
1232+         return  {};
1233+       }
1234+     }
1235+     return  {thisRef, handleTy};
1236+   }
1237+ 
1238+   // / Build a method call including implicit this argument.
1239+   mlir::func::CallOp
1240+   buildMethodCall (const  slang::ast::SubroutineSymbol *subroutine,
1241+                   FunctionLowering *lowering,
1242+                   moore::ClassHandleType actualHandleTy, Value actualThisRef,
1243+                   SmallVector<Value> &arguments,
1244+                   SmallVector<Type> &resultTypes) {
1245+ 
1246+     //  Get the expected receiver type from the lowered method
1247+     auto  funcTy = lowering->op .getFunctionType ();
1248+     auto  expected0 = funcTy.getInput (0 );
1249+     auto  expectedHdlTy = cast<moore::ClassHandleType>(expected0);
1250+ 
1251+     //  Upcast the handle as necessary.
1252+     auto  implicitThisRef = context.materializeConversion (
1253+         expectedHdlTy, actualThisRef, false , actualThisRef.getLoc ());
1254+ 
1255+     //  Build an argument list where the this reference is the first argument.
1256+     SmallVector<Value> explicitArguments;
1257+     explicitArguments.reserve (arguments.size () + 1 );
1258+     explicitArguments.push_back (implicitThisRef);
1259+     explicitArguments.append (arguments.begin (), arguments.end ());
1260+ 
1261+     //  Method call: choose direct vs virtual.
1262+     const  bool  isVirtual =
1263+         (subroutine->flags  & slang::ast::MethodFlags::Virtual) != 0 ;
1264+ 
1265+     if  (!isVirtual) {
1266+       //  Direct (non-virtual) call -> moore.class.call
1267+       auto  calleeSym =
1268+           SymbolRefAttr::get (context.getContext (), lowering->op .getSymName ());
1269+       return  mlir::func::CallOp::create (builder, loc, resultTypes, calleeSym,
1270+                                         explicitArguments);
1271+     }
1272+ 
1273+     mlir::emitError (loc) << " virtual method calls not supported" 
1274+     return  {};
1275+   }
1276+ 
11741277  // / Handle subroutine calls.
11751278  Value visitCall (const  slang::ast::CallExpression &expr,
11761279                  const  slang::ast::SubroutineSymbol *subroutine) {
1280+ 
1281+     const  bool  isMethod = (subroutine->thisVar  != nullptr );
1282+ 
11771283    auto  *lowering = context.declareFunction (*subroutine);
11781284    if  (!lowering)
11791285      return  {};
@@ -1254,20 +1360,34 @@ struct RvalueExprVisitor : public ExprVisitor {
12541360      }
12551361    }
12561362
1257-     //  Create the call.
1258-     auto  callOp =
1259-         mlir::func::CallOp::create (builder, loc, lowering->op , arguments);
1363+     //  Determine result types from the declared/converted func op.
1364+     SmallVector<Type> resultTypes (
1365+         lowering->op .getFunctionType ().getResults ().begin (),
1366+         lowering->op .getFunctionType ().getResults ().end ());
1367+ 
1368+     mlir::func::CallOp callOp;
1369+     if  (isMethod) {
1370+       //  Class functions -> build func.call with implicit this argument
1371+       auto  [thisRef, tyHandle] = getMethodReceiverTypeHandle (expr);
1372+       callOp = buildMethodCall (subroutine, lowering, tyHandle, thisRef,
1373+                                arguments, resultTypes);
1374+     } else  {
1375+       //  Free function -> func.call
1376+       callOp =
1377+           mlir::func::CallOp::create (builder, loc, lowering->op , arguments);
1378+     }
12601379
1380+     auto  result = resultTypes.size () > 0  ? callOp.getResult (0 ) : Value{};
12611381    //  For calls to void functions we need to have a value to return from this
12621382    //  function. Create a dummy `unrealized_conversion_cast`, which will get
12631383    //  deleted again later on.
1264-     if  (callOp. getNumResults () == 0 )
1384+     if  (resultTypes. size () == 0 )
12651385      return  mlir::UnrealizedConversionCastOp::create (
12661386                 builder, loc, moore::VoidType::get (context.getContext ()),
12671387                 ValueRange{})
12681388          .getResult (0 );
12691389
1270-     return  callOp. getResult ( 0 ) ;
1390+     return  result ;
12711391  }
12721392
12731393  // / Handle system calls.
0 commit comments