Changeset d2034b4
- Timestamp:
- Mar 19, 2018, 5:29:23 PM (7 years ago)
- Branches:
- ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, with_gc
- Children:
- f810e09
- Parents:
- c5ce0ec (diff), 12db9e4 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Location:
- src
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Common/Debug.h
rc5ce0ec rd2034b4 28 28 namespace Debug { 29 29 /// debug codegen a translation unit 30 static inline void codeGen( __attribute__((unused)) const std::list< Declaration * > & translationUnit, __attribute__((unused)) const std::string & label ) {30 static inline void codeGen( __attribute__((unused)) const std::list< Declaration * > & translationUnit, __attribute__((unused)) const std::string & label, LinkageSpec::Spec linkageFilter = LinkageSpec::Compiler ) { 31 31 #ifdef DEBUG 32 32 std::list< Declaration * > decls; 33 33 34 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), [ ]( Declaration * decl ) {35 return ! LinkageSpec::isBuiltin( decl->get_linkage());34 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), [linkageFilter]( Declaration * decl ) { 35 return ! (decl->linkage & linkageFilter); 36 36 }); 37 37 38 38 std::cerr << "======" << label << "======" << std::endl; 39 CodeGen::generate( decls, std::cerr, false, true );39 CodeGen::generate( decls, std::cerr, true, true ); 40 40 #endif 41 41 } // dump 42 42 43 static inline void treeDump( __attribute__((unused)) const std::list< Declaration * > & translationUnit, __attribute__((unused)) const std::string & label ) {43 static inline void treeDump( __attribute__((unused)) const std::list< Declaration * > & translationUnit, __attribute__((unused)) const std::string & label, LinkageSpec::Spec linkageFilter = LinkageSpec::Compiler ) { 44 44 #ifdef DEBUG 45 45 std::list< Declaration * > decls; 46 46 47 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), [ ]( Declaration * decl ) {48 return ! LinkageSpec::isBuiltin( decl->get_linkage());47 filter( translationUnit.begin(), translationUnit.end(), back_inserter( decls ), [linkageFilter]( Declaration * decl ) { 48 return ! (decl->linkage & linkageFilter); 49 49 }); 50 50 -
src/Common/SemanticError.h
rc5ce0ec rd2034b4 38 38 constexpr const char * const WarningFormats[] = { 39 39 "self assignment of expression: %s", 40 "rvalue to reference conversion of rvalue: %s", 40 41 }; 41 42 42 43 enum class Warning { 43 44 SelfAssignment, 45 RvalueToReferenceConversion, 44 46 NUMBER_OF_WARNINGS, //This MUST be the last warning 45 47 }; … … 50 52 ); 51 53 52 #define SemanticWarning(loc, id, ...) SemanticWarningImpl(loc, id, WarningFormats[(int)id], __VA_ARGS__) 54 // ## used here to allow empty __VA_ARGS__ 55 #define SemanticWarning(loc, id, ...) SemanticWarningImpl(loc, id, WarningFormats[(int)id], ## __VA_ARGS__) 53 56 54 57 void SemanticWarningImpl (CodeLocation loc, Warning warn, const char * const fmt, ...) __attribute__((format(printf, 3, 4))); -
src/GenPoly/Lvalue.cc
rc5ce0ec rd2034b4 59 59 } 60 60 61 struct ReferenceConversions final {61 struct ReferenceConversions final : public WithStmtsToAdd { 62 62 Expression * postmutate( CastExpr * castExpr ); 63 63 Expression * postmutate( AddressExpr * addrExpr ); … … 114 114 } 115 115 116 void convertLvalue( std::list< Declaration* > & translationUnit ) {116 void convertLvalue( std::list< Declaration* > & translationUnit ) { 117 117 PassVisitor<ReferenceConversions> refCvt; 118 118 PassVisitor<ReferenceTypeElimination> elim; … … 150 150 // use type of return variable rather than expr result type, since it may have been changed to a pointer type 151 151 FunctionType * ftype = GenPoly::getFunctionType( func->get_type() ); 152 Type * ret = ftype-> get_returnVals().empty() ? nullptr : ftype->get_returnVals().front()->get_type();153 return func-> get_linkage()== LinkageSpec::Intrinsic && dynamic_cast<ReferenceType *>( ret );152 Type * ret = ftype->returnVals.empty() ? nullptr : ftype->returnVals.front()->get_type(); 153 return func->linkage == LinkageSpec::Intrinsic && dynamic_cast<ReferenceType *>( ret ); 154 154 } 155 155 } … … 160 160 if ( isIntrinsicReference( appExpr ) ) { 161 161 // eliminate reference types from intrinsic applications - now they return lvalues 162 Type * result = appExpr-> get_result();163 appExpr-> set_result( result->stripReferences()->clone());164 appExpr-> get_result()->set_lvalue( true );162 Type * result = appExpr->result; 163 appExpr->result = result->stripReferences()->clone(); 164 appExpr->result->set_lvalue( true ); 165 165 if ( ! inIntrinsic ) { 166 166 // when not in an intrinsic function, add a cast to 167 167 // don't add cast when in an intrinsic function, since they already have the cast 168 168 Expression * ret = new CastExpr( appExpr, result ); 169 ret->set_env( appExpr->get_env() ); 170 appExpr->set_env( nullptr ); 169 std::swap( ret->env, appExpr->env ); 171 170 return ret; 172 171 } … … 187 186 assertf( ftype, "Function declaration does not have function type." ); 188 187 // can be of differing lengths only when function is variadic 189 assertf( ftype-> get_parameters().size() == appExpr->get_args().size() || ftype->get_isVarArgs(), "ApplicationExpr args do not match formal parameter type." );188 assertf( ftype->parameters.size() == appExpr->args.size() || ftype->isVarArgs, "ApplicationExpr args do not match formal parameter type." ); 190 189 191 190 192 191 unsigned int i = 0; 193 const unsigned int end = ftype-> get_parameters().size();194 for ( auto p : unsafe_group_iterate( appExpr-> get_args(), ftype->get_parameters()) ) {192 const unsigned int end = ftype->parameters.size(); 193 for ( auto p : unsafe_group_iterate( appExpr->args, ftype->parameters ) ) { 195 194 if (i == end) break; 196 195 Expression *& arg = std::get<0>( p ); … … 212 211 // std::cerr << "===adding deref to arg" << std::endl; 213 212 // if the parameter is a reference, add a dereference to the reference-typed argument. 214 Type * baseType = InitTweak::getPointerBase( arg-> get_result());215 assertf( baseType, "parameter is reference, arg must be pointer or reference: %s", toString( arg-> get_result()).c_str() );213 Type * baseType = InitTweak::getPointerBase( arg->result ); 214 assertf( baseType, "parameter is reference, arg must be pointer or reference: %s", toString( arg->result ).c_str() ); 216 215 PointerType * ptrType = new PointerType( Type::Qualifiers(), baseType->clone() ); 217 delete arg-> get_result();216 delete arg->result; 218 217 arg->set_result( ptrType ); 219 218 arg = mkDeref( arg ); … … 249 248 Expression * AddrRef::postmutate( AddressExpr * addrExpr ) { 250 249 if ( refDepth == 0 ) { 251 if ( ! isIntrinsicReference( addrExpr-> get_arg()) ) {250 if ( ! isIntrinsicReference( addrExpr->arg ) ) { 252 251 // try to avoid ?[?] 253 refDepth = addrExpr-> get_arg()->get_result()->referenceDepth();252 refDepth = addrExpr->arg->result->referenceDepth(); 254 253 } 255 254 } … … 280 279 // pointer casts in the right places. 281 280 282 // conversion to reference type 283 if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->get_result() ) ) { 284 (void)refType; 285 if ( ReferenceType * otherRef = dynamic_cast< ReferenceType * >( castExpr->get_arg()->get_result() ) ) { 286 // nothing to do if casting from reference to reference. 287 (void)otherRef; 288 PRINT( std::cerr << "convert reference to reference -- nop" << std::endl; ) 289 if ( isIntrinsicReference( castExpr->get_arg() ) ) { 290 Expression * callExpr = castExpr->get_arg(); 291 PRINT( 292 std::cerr << "but arg is deref -- &" << std::endl; 293 std::cerr << callExpr << std::endl; 294 ) 295 callExpr = new AddressExpr( callExpr ); // this doesn't work properly for multiple casts 296 delete callExpr->get_result(); 297 callExpr->set_result( refType->clone() ); 298 // move environment out to new top-level 299 callExpr->set_env( castExpr->get_env() ); 300 castExpr->set_arg( nullptr ); 301 castExpr->set_env( nullptr ); 302 delete castExpr; 303 return callExpr; 304 } 305 int depth1 = refType->referenceDepth(); 306 int depth2 = otherRef->referenceDepth(); 307 int diff = depth1-depth2; 308 if ( diff == 0 ) { 309 // conversion between references of the same depth 310 assertf( depth1 == depth2, "non-intrinsic reference with cast of reference to reference not yet supported: %d %d %s", depth1, depth2, toString( castExpr ).c_str() ); 311 PRINT( std::cerr << castExpr << std::endl; ) 312 return castExpr; 313 } else if ( diff < 0 ) { 314 // conversion from reference to reference with less depth (e.g. int && -> int &): add dereferences 315 Expression * ret = castExpr->arg; 316 for ( int i = 0; i < diff; ++i ) { 317 ret = mkDeref( ret ); 318 } 319 ret->env = castExpr->env; 320 delete ret->result; 321 ret->result = castExpr->result; 322 ret->result->set_lvalue( true ); // ensure result is lvalue 323 castExpr->env = nullptr; 324 castExpr->arg = nullptr; 325 castExpr->result = nullptr; 326 delete castExpr; 327 return ret; 328 } else if ( diff > 0 ) { 329 // conversion from reference to reference with more depth (e.g. int & -> int &&): add address-of 330 Expression * ret = castExpr->arg; 331 for ( int i = 0; i < diff; ++i ) { 332 ret = new AddressExpr( ret ); 333 } 334 ret->env = castExpr->env; 335 delete ret->result; 336 ret->result = castExpr->result; 337 castExpr->env = nullptr; 338 castExpr->arg = nullptr; 339 castExpr->result = nullptr; 340 delete castExpr; 341 return ret; 342 } 343 344 assertf( depth1 == depth2, "non-intrinsic reference with cast of reference to reference not yet supported: %d %d %s", depth1, depth2, toString( castExpr ).c_str() ); 345 PRINT( std::cerr << castExpr << std::endl; ) 281 // Note: reference depth difference is the determining factor in what code is run, rather than whether something is 282 // reference type or not, since conversion still needs to occur when both types are references that differ in depth. 283 284 Type * destType = castExpr->result; 285 Type * srcType = castExpr->arg->result; 286 int depth1 = destType->referenceDepth(); 287 int depth2 = srcType->referenceDepth(); 288 int diff = depth1 - depth2; 289 290 if ( diff > 0 && ! srcType->get_lvalue() ) { 291 // rvalue to reference conversion -- introduce temporary 292 // know that reference depth of cast argument is 0, need to introduce n temporaries for reference depth of n, e.g. 293 // (int &&&)3; 294 // becomes 295 // int __ref_tmp_0 = 3; 296 // int & __ref_tmp_1 = _&_ref_tmp_0; 297 // int && __ref_tmp_2 = &__ref_tmp_1; 298 // &__ref_tmp_2; 299 // the last & comes from the remaining reference conversion code 300 SemanticWarning( castExpr->arg->location, Warning::RvalueToReferenceConversion, toCString( castExpr->arg ) ); 301 302 static UniqueName tempNamer( "__ref_tmp_" ); 303 ObjectDecl * temp = ObjectDecl::newObject( tempNamer.newName(), castExpr->arg->result->clone(), new SingleInit( castExpr->arg ) ); 304 PRINT( std::cerr << "made temp: " << temp << std::endl; ) 305 stmtsToAddBefore.push_back( new DeclStmt( temp ) ); 306 for ( int i = 0; i < depth1-1; i++ ) { // xxx - maybe this should be diff-1? check how this works with reference type for srcType 307 ObjectDecl * newTemp = ObjectDecl::newObject( tempNamer.newName(), new ReferenceType( Type::Qualifiers(), temp->type->clone() ), new SingleInit( new AddressExpr( new VariableExpr( temp ) ) ) ); 308 PRINT( std::cerr << "made temp" << i << ": " << newTemp << std::endl; ) 309 stmtsToAddBefore.push_back( new DeclStmt( newTemp ) ); 310 temp = newTemp; 311 } 312 // update diff so that remaining code works out correctly 313 castExpr->arg = new VariableExpr( temp ); 314 PRINT( std::cerr << "update cast to: " << castExpr << std::endl; ) 315 srcType = castExpr->arg->result; 316 depth2 = srcType->referenceDepth(); 317 diff = depth1 - depth2; 318 assert( diff == 1 ); 319 } 320 321 // handle conversion between different depths 322 PRINT ( 323 if ( depth1 || depth2 ) { 324 std::cerr << "destType: " << destType << " / srcType: " << srcType << std::endl; 325 std::cerr << "depth: " << depth1 << " / " << depth2 << std::endl; 326 } 327 ) 328 if ( diff > 0 ) { 329 // conversion to type with more depth (e.g. int & -> int &&): add address-of for each level of difference 330 Expression * ret = castExpr->arg; 331 for ( int i = 0; i < diff; ++i ) { 332 ret = new AddressExpr( ret ); 333 } 334 if ( srcType->get_lvalue() && srcType->get_qualifiers() != strict_dynamic_cast<ReferenceType *>( destType )->base->get_qualifiers() ) { 335 // must keep cast if cast-to type is different from the actual type 336 castExpr->arg = ret; 346 337 return castExpr; 347 } else if ( castExpr->arg->result->get_lvalue() ) { 348 // conversion from lvalue to reference 349 // xxx - keep cast, but turn into pointer cast?? 350 // xxx - memory 351 PRINT( 352 std::cerr << "convert lvalue to reference -- &" << std::endl; 353 std::cerr << castExpr->arg << std::endl; 354 ) 355 AddressExpr * ret = new AddressExpr( castExpr->arg ); 356 if ( refType->base->get_qualifiers() != castExpr->arg->result->get_qualifiers() ) { 357 // must keep cast if cast-to type is different from the actual type 358 castExpr->arg = ret; 359 return castExpr; 360 } 361 ret->env = castExpr->env; 362 delete ret->result; 363 ret->result = castExpr->result; 364 castExpr->env = nullptr; 365 castExpr->arg = nullptr; 366 castExpr->result = nullptr; 367 delete castExpr; 368 return ret; 369 } else { 370 // rvalue to reference conversion -- introduce temporary 371 } 372 assertf( false, "Only conversions to reference from lvalue are currently supported: %s", toString( castExpr ).c_str() ); 373 } else if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->arg->result ) ) { 374 (void)refType; 375 // conversion from reference to rvalue 376 PRINT( 377 std::cerr << "convert reference to rvalue -- *" << std::endl; 378 std::cerr << "was = " << castExpr << std::endl; 379 ) 338 } 339 ret->env = castExpr->env; 340 delete ret->result; 341 ret->result = castExpr->result; 342 castExpr->env = nullptr; 343 castExpr->arg = nullptr; 344 castExpr->result = nullptr; 345 delete castExpr; 346 return ret; 347 } else if ( diff < 0 ) { 348 // conversion to type with less depth (e.g. int && -> int &): add dereferences for each level of difference 349 diff = -diff; // care only about magnitude now 380 350 Expression * ret = castExpr->arg; 381 TypeSubstitution * env = castExpr->env; 382 castExpr->set_env( nullptr ); 383 if ( ! isIntrinsicReference( ret ) ) { 384 // dereference if not already dereferenced 351 for ( int i = 0; i < diff; ++i ) { 385 352 ret = mkDeref( ret ); 386 353 } 387 if ( ResolvExpr::typesCompatibleIgnoreQualifiers( castExpr->result, castExpr->arg->result->stripReferences(), SymTab::Indexer() ) ) { 388 // can remove cast if types are compatible, changing expression type to value type 389 ret->result = castExpr->result->clone(); 390 ret->result->set_lvalue( true ); // ensure result is lvalue 391 castExpr->arg = nullptr; 392 delete castExpr; 393 } else { 354 if ( ! ResolvExpr::typesCompatibleIgnoreQualifiers( destType->stripReferences(), srcType->stripReferences(), SymTab::Indexer() ) ) { 394 355 // must keep cast if types are different 395 356 castExpr->arg = ret; 396 ret = castExpr; 397 } 398 ret->set_env( env ); 399 PRINT( std::cerr << "now: " << ret << std::endl; ) 400 return ret; 401 } 402 return castExpr; 357 return castExpr; 358 } 359 ret->env = castExpr->env; 360 delete ret->result; 361 ret->result = castExpr->result; 362 ret->result->set_lvalue( true ); // ensure result is lvalue 363 castExpr->env = nullptr; 364 castExpr->arg = nullptr; 365 castExpr->result = nullptr; 366 delete castExpr; 367 return ret; 368 } else { 369 assert( diff == 0 ); 370 // conversion between references of the same depth 371 return castExpr; 372 } 403 373 } 404 374 405 375 Type * ReferenceTypeElimination::postmutate( ReferenceType * refType ) { 406 Type * base = refType-> get_base();376 Type * base = refType->base; 407 377 Type::Qualifiers qualifiers = refType->get_qualifiers(); 408 refType-> set_base( nullptr );378 refType->base = nullptr; 409 379 delete refType; 410 380 return new PointerType( qualifiers, base ); … … 414 384 Expression * GeneralizedLvalue::applyTransformation( Expression * expr, Expression * arg, Func mkExpr ) { 415 385 if ( CommaExpr * commaExpr = dynamic_cast< CommaExpr * >( arg ) ) { 416 Expression * arg1 = commaExpr-> get_arg1()->clone();417 Expression * arg2 = commaExpr-> get_arg2()->clone();386 Expression * arg1 = commaExpr->arg1->clone(); 387 Expression * arg2 = commaExpr->arg2->clone(); 418 388 Expression * ret = new CommaExpr( arg1, mkExpr( arg2 )->acceptMutator( *visitor ) ); 419 ret-> set_env( expr->get_env() );420 expr-> set_env( nullptr );389 ret->env = expr->env; 390 expr->env = nullptr; 421 391 delete expr; 422 392 return ret; 423 393 } else if ( ConditionalExpr * condExpr = dynamic_cast< ConditionalExpr * >( arg ) ) { 424 Expression * arg1 = condExpr-> get_arg1()->clone();425 Expression * arg2 = condExpr-> get_arg2()->clone();426 Expression * arg3 = condExpr-> get_arg3()->clone();394 Expression * arg1 = condExpr->arg1->clone(); 395 Expression * arg2 = condExpr->arg2->clone(); 396 Expression * arg3 = condExpr->arg3->clone(); 427 397 ConditionalExpr * ret = new ConditionalExpr( arg1, mkExpr( arg2 )->acceptMutator( *visitor ), mkExpr( arg3 )->acceptMutator( *visitor ) ); 428 ret-> set_env( expr->get_env() );429 expr-> set_env( nullptr );398 ret->env = expr->env; 399 expr->env = nullptr; 430 400 delete expr; 431 401 … … 436 406 AssertionSet needAssertions, haveAssertions; 437 407 OpenVarSet openVars; 438 unify( ret-> get_arg2()->get_result(), ret->get_arg3()->get_result(), newEnv, needAssertions, haveAssertions, openVars, SymTab::Indexer(), commonType );439 ret-> set_result( commonType ? commonType : ret->get_arg2()->get_result()->clone());408 unify( ret->arg2->result, ret->arg3->result, newEnv, needAssertions, haveAssertions, openVars, SymTab::Indexer(), commonType ); 409 ret->result = commonType ? commonType : ret->arg2->result->clone(); 440 410 return ret; 441 411 } … … 444 414 445 415 Expression * GeneralizedLvalue::postmutate( MemberExpr * memExpr ) { 446 return applyTransformation( memExpr, memExpr-> get_aggregate(), [=]( Expression * aggr ) { return new MemberExpr( memExpr->get_member(), aggr ); } );416 return applyTransformation( memExpr, memExpr->aggregate, [=]( Expression * aggr ) { return new MemberExpr( memExpr->member, aggr ); } ); 447 417 } 448 418 449 419 Expression * GeneralizedLvalue::postmutate( AddressExpr * addrExpr ) { 450 return applyTransformation( addrExpr, addrExpr-> get_arg(), []( Expression * arg ) { return new AddressExpr( arg ); } );420 return applyTransformation( addrExpr, addrExpr->arg, []( Expression * arg ) { return new AddressExpr( arg ); } ); 451 421 } 452 422 453 423 Expression * CollapseAddrDeref::postmutate( AddressExpr * addrExpr ) { 454 Expression * arg = addrExpr-> get_arg();424 Expression * arg = addrExpr->arg; 455 425 if ( isIntrinsicReference( arg ) ) { 456 426 std::string fname = InitTweak::getFunctionName( arg ); … … 458 428 Expression *& arg0 = InitTweak::getCallArg( arg, 0 ); 459 429 Expression * ret = arg0; 460 ret->set_env( addrExpr-> get_env());430 ret->set_env( addrExpr->env ); 461 431 arg0 = nullptr; 462 addrExpr-> set_env( nullptr );432 addrExpr->env = nullptr; 463 433 delete addrExpr; 464 434 return ret; … … 487 457 // } 488 458 if ( AddressExpr * addrExpr = dynamic_cast< AddressExpr * >( arg ) ) { 489 Expression * ret = addrExpr-> get_arg();490 ret-> set_env( appExpr->get_env() );491 addrExpr-> set_arg( nullptr );492 appExpr-> set_env( nullptr );459 Expression * ret = addrExpr->arg; 460 ret->env = appExpr->env; 461 addrExpr->arg = nullptr; 462 appExpr->env = nullptr; 493 463 delete appExpr; 494 464 return ret; -
src/tests/.expect/references.txt
rc5ce0ec rd2034b4 28 28 Destructing a Y 29 29 Destructing a Y 30 3 3 31 3 32 3 33 3 9 { 1, 7 }, [1, 2, 3] 30 34 Destructing a Y 31 35 Destructing a Y -
src/tests/references.c
rc5ce0ec rd2034b4 49 49 &r1 = x, &&r2 = r1, &&&r3 = r2; 50 50 ***p3 = 3; // change x 51 // ((int&)r3 = 3; // change x, ***r3 51 52 **p3 = &x; // change p1 53 // ((int*&)&r3) = &x; // change r1, (&*)**r3 52 54 *p3 = &p1; // change p2 55 // ((int**&)&&r3) = &p2; // change r2, (&(&*)*)*r3 56 // ((int***&)&&&r3) = p3; // change r3 to p3, (&(&(&*)*)*)r3 53 57 int y = 0, z = 11, & ar[3] = { x, y, z }; // initialize array of references 58 // &ar[1] = &z; // change reference array element 59 // typeof( ar[1] ) p; // is int, i.e., the type of referenced object 60 // typeof( &ar[1] ) q; // is int &, i.e., the type of reference 61 // sizeof( ar[1] ) == sizeof( int ); // is true, i.e., the size of referenced object 62 // sizeof( &ar[1] ) == sizeof( int *); // is true, i.e., the size of a reference 54 63 55 64 // test that basic reference properties are true - r1 should be an alias for x … … 76 85 &z1.r = &z1r; 77 86 &z2.r = &z2r; 87 78 88 z1 = z2; 89 90 // test rvalue-to-reference conversion 91 { 92 struct S { double x, y; }; 93 void f( int & i, int & j, S & s, int v[] ) { 94 printf("%d %d { %g, %g }, [%d, %d, %d]\n", i, j, s.[x, y], v[0], v[1], v[2]); 95 } 96 void g(int & i) { printf("%d\n", i); } 97 void h(int &&& i) { printf("%d\n", i); } 98 99 int &&& r = 3; // rvalue to reference 100 int i = r; 101 printf("%d %d\n", i, r); // both 3 102 103 g( 3 ); // rvalue to reference 104 h( (int &&&)3 ); // rvalue to reference 105 106 int a = 5, b = 4; 107 f( 3, a + b, (S){ 1.0, 7.0 }, (int [3]){ 1, 2, 3 } ); // two rvalue to reference 108 } 79 109 } 80 110
Note: See TracChangeset
for help on using the changeset viewer.